柴油和 rust-postgres 的比较

柴油和 rust-postgres 的比较

原文:https://medium.com/hackernoon/comparing-diesel-and-rust-postgres-97fd8c656fdd

随着 Diesel 0.9.0 的发布,我想重温一下 Diesel 和 rust-postgres 之间的性能差异,看看自从我上次在 0.1.0 中测量这两个版本以来,情况是否发生了显著变化

TL;DR:在惯用代码方面,Diesel 始终比 rust-postgres 高出 25-30%。它也更简洁,并在编译时捕捉错误,以帮助您更快地编写工作代码。你可以在这里找到完整的基准代码。rust-postgres 基准可以进一步优化,以获得大约 10%的性能提升。然而,这样做会导致代码变得更脆弱,更难理解。

如果你想在这个假期做些什么,并且你还没有尝试过 Diesel,看看我们的入门指南。本文的其余部分将研究这两种用法之间的区别。

基准测试结果完全基于 select 语句的结果,而不是数据的插入。由 SQL 数据库支持的大多数应用程序都是读负载,而不是写负载。此外,rust-postgres 严重依赖绑定参数的动态调度。这意味着对于具有过多绑定参数的查询,比如批量插入,其性能降低到了我认为不公平的程度。

设置数据

我们来看两个基准测试。第一个将把一些行数设置到一个 3 列的表中,然后执行SELECT * FROM users或等效操作。第二个将使用两个包含更多数据的表,并在两个表之间建立一个连接。这是 Diesel 的设置:

NewUser的定义在顶部,看起来像:

#[derive(Insertable)]来自diesel_codegen机箱,并生成适当的代码在 insert 语句中使用这个结构。我们从顶部看起来像infer_schema!("env:DATABASE_URL")的一行代码中知道数据库模式,它将在编译时加载数据库模式。使用NewUser的矢量或切片进行批量插入会自动进行。我们正在对结果执行assert_eq!,以再次检查我们是否确实插入了预期的行数。

rust-postgres 基准测试的设置稍微复杂一些。

当然,这总是有点冗长,因为我们必须手动构造 SQL。对于批量插入,这意味着大量的动态查询结构,这些 SQL 只不过是难以阅读的($1, $2)。这是意料之中的,rust-postgres 并不想成为一个查询生成器。然而,我们不得不通过绑定参数跳过的圈圈让我感到惊讶。

rust-postgres 尽可能推迟绑定参数的序列化。然而,它也没有提供任何保存绑定参数的数据结构。这意味着我们必须将它们放入一个 trait 对象列表中,即使所有的绑定都是同一类型。这也意味着它依赖于动态调度,这将严重阻碍编译器优化序列化代码的能力。

rust-postgres 提供的 API 看起来可能很好,如果您正在构建简单的查询,或者如果您所有的绑定值都是硬编码的,或者简单地借用了已经在范围内的东西。但是一旦你在做动态的事情,就很难弄清楚到底该做什么。我花了很长时间才弄明白我需要在那里构造借来的绑定参数的确切咒语。

最后,因为我们只是在简单的 SQL 字符串上操作,所以我们还需要检查以确保我们没有试图插入 0 行。由于 rust-postgres 只处理原始 SQL,它无法理解我们自动处理这种情况的意图。

简单查询执行

对于第一个基准测试,我们将执行SELECT * FROM users或等价的,将所有的行反序列化为一个Vec<User>,然后检查向量的长度是否是我们期望的。两个基准的结构定义如下所示:

在 Diesel 基准测试中,该结构用#[derive(Queryable)]进行了注释,生成代码将行反序列化到该结构中。柴油的基准看起来像这样:

需要注意的一点是,它生成的查询与您自己编写的略有不同。准确的查询是SELECT "users"."id", "users"."name", "users"."hair_color" FROM "users"。我们必须在这里为::<User>添加一条 turbofish,因为我们从来没有使用过 vector,编译器也无法判断出它的类型。

rust-postgres 基准有点类似:

需要注意的一点是,我们已经内联编写了反序列化代码,而不是提取一个名为User::from_row或类似的函数。在代码中使用 rust-postgres 创建该函数是一种常见的模式,但它最终非常脆弱。除了这个查询之外,我们不知道这些列是否已经被重命名。正如我们将在后面看到的,经常需要重命名。我们也不知道这段代码是否出现在一个连接中。如果它在左外部连接的右侧,我们将需要返回Option<User>。由于User::from_row函数的重用有限,所以我选择不提取它。

如果我们显式地列出每一列,并通过索引而不是名称获取结果,我们也可以获得一些改进。然而,这个基准测试的目标是从用户的角度编写代码,花费同样的精力,做大致相同的事情。通过索引获取结果是非常脆弱的,而且由于Row类型对数据的底层 SQL 类型一无所知,所以非常容易意外地将垃圾数据反序列化为错误的类型。我在野外看到的所有代码都通过名称访问字段,这可能是正确的决定。

这些是“简单查询”基准测试的结果。

由于用 postgres 执行一个查询的开销,会有很大的差异,但是 Diesel 每次运行都会快 25%到 30%。

复杂的查询

对于第二个基准,我们将变得稍微复杂一些。对于数据,我们将为用户改变头发颜色,并给每三个用户一个帖子。这是 Diesel 的设置代码。

对于NewPost,我们主要使用静态数据。#[derive(Insertable)]是不是设计的既可以处理自有数据,也可以处理借来的数据,所以你可以使用最适合这种情况的数据。在我们生成动态字符串的NewUser的例子中,将结果放在自己的字符串中要容易得多。

rust-postgres 的设置也类似于第一个基准测试。

为 posts 查询收集绑定参数甚至比第一个基准测试还要困难。如果数据只是稍微静态一点,我们就可以马上把它放到一个Vec<&ToSql>中,就像它的作者想要的那样。然而,我们必须给计数器加 1 来得到user_id,这意味着我们有一个动态构造的值。由于这一次绑定参数的类型不同,我们必须将它们打包以获得单一类型的向量。即使在那之后,我们仍然需要第二次迭代,从Vec<Box<ToSql>>Vec<&ToSql>,最终我们可以借用到 rust-postgres 想要的特定&[&ToSql]

对第二基准的查询是SELECT * FROM users LEFT OUTER JOIN posts ON posts.user_id = users.id WHERE hair_color = "black" ORDER BY name DESC或等效的。柴油版的代码如下所示:

您会注意到我们从来没有为这个查询指定过ON子句。关于如何执行连接的信息是由类似于#[has_many(posts)]User上的注释生成的。我们默认假设外键的形式是parent_id。如果您不使用这个约定,或者有一个更复杂的连接,那么很容易覆盖这个缺省值。

该基准测试的 rust-postgres 代码如下所示:

这里的查询要长得多。我们需要手动列出每一列,并给它们起别名,这样我们就可以确保以后获取的是正确的列。rust-postgres 提供的 API 不允许您为列指定表名。在发生冲突的情况下,执行row.get("id")会自动给出先出现的id列,没有任何迹象表明您可能使用了错误的数据。

我们再次以内联方式执行了反序列化。这个例子证明了我之前的观点。许多代码库使用的通用抽象非常脆弱。不幸的是,rust-postgres 没有提供为这类情况编写通用反序列化代码所需的 API。

rust-postgres 的一个优点是这是一个比其他查询更容易执行的查询。我们所有的绑定参数都是静态的,所以我们能够将它内嵌到查询中。我相信这是它的作者优化的案例类型,当你在这些界限内时,它工作得很好。

第二项基准测试的结果是:

结论

希望这有助于说明柴油和 rust-postgres 之间的一些关键差异。尽管专注于保持比柴油低的水平,rust-postgres 并没有提供比柴油更好的性能。

需要注意的是,rust-postgres 基准测试用时 2.76 秒,而 Diesel 基准测试用时 3.4 秒。我预计在大型代码库上,这个差距会越来越大。rust-postgres 基准测试的二进制文件大约比 Diesel 基准测试的二进制文件大 25%,我认为不管代码库有多大,Diesel 的二进制文件都会更小。

我真的相信 Diesel 提供的安全保证会显著提高开发人员的生产力。我尝试了很多次才让 rust-postgres 基准真正发挥作用。与 Diesel 给出的快速编译时反馈相比,等待运行时错误告诉我该做什么的反馈周期(假设消息是有用的)非常慢。

如果你还没有尝试过 Diesel,我希望你能抓住这个机会在假期尝试一下。可以在 http://diesel.rs 了解更多

感谢阅读!

黑客中午是黑客如何开始他们的下午。我们是 @AMI 家庭的一员。我们现在接受投稿并乐意讨论广告&赞助机会。

要了解更多信息,请阅读我们的“关于”页面在脸书上给我们点赞/发消息,或者简单地说, tweet/DM @HackerNoon。

如果你喜欢这个故事,我们推荐你阅读我们的最新科技故事趋势科技故事。直到下一次,不要把世界的现实想当然!


本站为非盈利网站,作品由网友提供上传,如无意中有侵犯您的版权,请联系删除