• Rust 和 ScyllaDB NoSQL:提高性能的 3 种方法


    在不同的基准测试中,Rust 驱动程序被证明比其他驱动程序具有更高的性能,这让我们产生了将它用作其他驱动程序的统一核心的想法。 

    这篇博文基于 ScyllaDB 大学 Rust 课程。在这篇文章中,我将介绍课程的要点。您将了解准备好的语句、分页和重试,并查看使用 ScyllaDB Rust 驱动程序的示例。最终目标是演示一些微小的更改如何显着提高应用程序的性能。 

    在 Docker 中启动 ScyllaDB

    从 git 下载示例:

    git clone https://github.com/scylladb/scylla-code-samples.git

    cd scylla-code-samples/Rust_Scylla_Driver/chat/

    要快速启动并运行 ScyllaDB,请使用官方 Docker 镜像

    1. docker run \
    2. -p 9042:9042/tcp \
    3. --name some-scylla \
    4. --hostname rust-scylla \
    5. -d scylladb/scylla:4.5.0 \
    6.   --smp 1 --memory=750M --overprovisioned 1

    示例应用程序

    在此示例中,您将创建一个控制台应用程序,该应用程序从标准输入读取消息并将它们放入 ScyllaDB 中的表中。首先,创建键空间和表:

    docker exec -it some-scylla cqlsh

    1. CREATE KEYSPACE IF NOT EXISTS log WITH REPLICATION = {
    2. 'class': 'SimpleStrategy',
    3. 'replication_factor': 1
    4. };

    1. CREATE TABLE IF NOT EXISTS log.messages (
    2. id bigint,
    3. message text,
    4. PRIMARY KEY (id)
    5. );

    现在,看看应用程序的主要代码: 

    应用程序连接到数据库,从控制台读取一些行,并将它们存储在表中log.messages。然后它从表中读取这些行并打印出来。 

    到目前为止,这与您在 Rust 入门课程中看到的非常相似。使用此应用程序,您将看到一些小的更改如何提高应用程序的性能。

    准备好的陈述

    在 while 循环的每次迭代中,我们都希望将新数据插入log.messages表中。天真地这样做是低效的,因为每次调用session.query都会将整个查询字符串发送到数据库,然后数据库对其进行解析。可以使用会话提前准备查询以避免不必要的数据库端calculations.prepare方法。对该方法的调用将返回一个PreparedStatement对象,稍后可以使用该对象session.execute()来执行所需的查询。

    什么是准备好的陈述?

    准备好的陈述是由 ScyllaDB 解析的查询,然后保存以备后用。使用准备好的语句的一个重要好处是,您可以继续重用相同的查询,同时修改查询中的变量以匹配名称、地址和位置等参数。 

    当要求准备 CQL 语句时,客户端库将向 ScyllaDB 发送 CQL 语句。然后,ScyllaDB 将通过 MD5 哈希为该 CQL 语句创建一个唯一的指纹。ScyllaDB 然后使用这个散列来检查它的查询缓存,看看它是否已经看到它。如果是这样,它将返回对该缓存的 CQL 语句的引用。如果 ScyllaDB 在其缓存中没有该唯一查询哈希,它将继续解析查询并将解析后的输出插入其缓存中。 

    然后,客户端将能够发送并执行指定语句 ID(封装在PreparedStatement对象中)并提供(绑定)变量的请求,如下所示。

    在应用程序中使用准备好的语句

    查看上面的示例代码并将其修改为使用准备好的语句。session.prepare第一步是在 while 循环之前创建一个准备好的语句(借助)。接下来,您需要在 while 循环内 替换session.querywith 。session.execute

    在这两个步骤之后,应用程序将重用准备好的语句 insert_message而不是发送原始查询。这显着提高了性能。

    分页

    查看应用程序的最后几行: 

    调用了该Session::query方法,并发送了未准备好的选择查询。由于此查询只执行一次,因此不值得准备。但是,如果我们怀疑结果会很大,使用分页可能会更好。

    什么是分页?

    分页是一种以可管理的块的形式返回大量数据的方法。在没有分页的情况下,协调器节点准备一个包含所有数据的结果实体并将其返回。如果结果很大,这可能会对性能产生重大影响,因为它可能会在客户端和 ScyllaDB 服务器端占用大量内存。 

    为避免这种情况,请使用分页,以便将结果以有限大小的块传输,一次传输一个块。传输每个块后,数据库停止并等待客户端请求下一个。重复此操作,直到传输整个结果集。 

    客户端可以根据它可以包含的行数来限制页面的大小。如果页面在达到客户端提供的行限制之前达到大小限制,则称为短页面或短读取。

    将分页添加到我们的应用程序

    正如您现在可能已经猜到的那样,Session::query不使用分页。它一次性将整个结果提取到内存中。另一种 Session 方法在后台使用分页——  Session::query_iterSession::execute_iter是另一种适用于准备好的语句的方法)。该Session::query_iter方法将一个查询和一个值列表作为参数,并在结果行上返回一个异步迭代器。这是它的使用方式: 

    调用后query_iter,驱动程序启动后台任务以获取后续行。调用者任务(调用的任务query_iter)使用类似迭代器的流接口来消耗新获取的行。调用者和后台任务同时运行,因此其中一个可以获取新行,而另一个使用它们。 

    通过向应用程序添加分页,您可以减少内存使用并提高应用程序的性能。

    重试

    查询失败后,驱动程序可能会根据重试策略和查询本身决定重试。可以为整个会话或单个查询配置重试策略。

    可用的重试策略

    驱动程序提供两种策略可供选择:

    • Fallthrough Retry Policy:从不重试并将所有错误直接返回给用户
    • 默认重试策略:默认使用,如果成功几率很高,可能会重试

    可以通过实施 RetryPolicy 和 RetrySession 来提供自定义重试策略。

    使用重试策略

    享受重试策略好处的关键是提供更多关于查询幂等性的信息。如果查询可以多次应用而不改变初始应用的结果,则该查询是幂等的。如果驱动程序不是幂等的,则它不会重试失败的查询。将查询标记为幂等应由用户完成,因为驱动程序不解析查询字符串。 

  • 相关阅读:
    快速上手JSON数据传递&异常处理
    Android&Flutter混合开发
    OKR高效落地的四个关键要素
    手撸一个在线学习在线教育小程序
    .NET 高效灵活的API速率限制解决方案
    Spring循环依赖
    高并发场景防止超卖的实现
    一文搞懂二分查找算法!
    企业管理低代码—无代码平台的7个技巧
    千万级别的表分页查询非常慢,怎么办?
  • 原文地址:https://blog.csdn.net/vvoennvv/article/details/127931275