降低 RavenDB 中的解析成本

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

请注意,这是我们正在为 4.0 考虑的事情。

RavenDB 原生使用 JSON 来处理几乎所有的事情。这对 JSON 文档数据库有意义。 JSON 易于解析、人类可读,并且在大多数情况下,没有真正的复杂性。

然而,虽然 JSON 易于解析,但解析它会产生一些计算开销。事实上,在我们的性能测试中,我们花了很多时间来序列化和反序列化 JSON。这是我们必须处理的主要成本之一,主要是因为这是经常发生的事情。我们以前使用 BSON 在内部存储文档,但分析实际上表明,将数据保存为 JSON 文本并对其进行解析的成本更低。这就是我们目前正在做的事情。

然而,更快的解析仍然是解析,这是我们想要避免的。这在内存中的实际表示方式也存在问题。让我们考虑以下 JSON:


 {"FirstName":"John","LastName":"Smith"}

在内存中,这表示为(高度简化):

  • Dictionary<string,object> 实例
  • 键的字符串[]
  • “名字”字符串
  • “姓氏”字符串
  • object[] 的值
    • “约翰”字符串
    • “史密斯”字符串

    换句话说,特别是对于大文档,有 很多 正在创建的小对象。这不会影响即时解析成本,但确实需要在事后收集这些成本,这 我们希望避免的事情。

    我们目前正在考虑使用 Flat Buffers 进行内部文档存储。 Flat Buffers 的好处是没有中间解析步骤。您将获得一块可以立即访问的内存。这有两个主要优点,将文档加载到内存意味着只需从磁盘读取缓冲区,无需额外成本。但这也意味着释放文档的行为会便宜得多,我们只需要再次收集缓冲区,而不是潜在的数万个小对象。

    另一个好处是我们通常需要加载要索引的文档,而通常索引只需要文档中很少的字段。通过避免解析成本,只为我们实际接触的对象付出代价,我们可以更好地降低索引成本。

    使用平面缓冲区的粗略模式是:

    
     {"FirstName":"John","LastName":"Smith"}
    

    文档中的值按字段名称排序,因此我们可以使用二进制搜索来搜索字段。

    Nitpicker 角落:是的,我们可能想在这里使用哈希,但这是一个粗略的草稿来测试东西,稍后会出现。

    我们需要优化模式,使用它 不会 很有趣,但速度和内存的改进应该是显着的。

    另一种选择是这样的模式:

    
     {"FirstName":"John","LastName":"Smith"}
    

    这里我们将所有字段名称存储一次,然后我们使用其在根对象中的索引来引用字段名称。这应该具有减少重复字符串名称的优点。