通过自定义二进制序列化提高自动完成性能

发布于:2021-01-14 10:09:31

0

88

0

二进制 序列化 SQL

0.1秒的目标

在为即将到来的SQL Studio Web应用程序开发后端服务期间,我们希望在用户键入代码时向用户提供快速的自动完成帮助。我们将“快速”目标设定为100毫秒左右。

自动完成功能需要了解任何数据库的元数据(有关数据的数据)。

无状态容器

后端使用以C#编写并托管在当前在Amazon Web Services上运行的Docker容器中的.Net Core代码。容器实例可以来去去,并且每个容器可以为许多用户提供服务。将内容保存在容器的内存中不是很可扩展。负载均衡器可以路由每个用户请求以命中不同的容器。

提取元数据是对数据库的一系列昂贵操作,因此我们需要解决为任何类型的数据库缓存元数据的问题。决定使用Redis缓存服务器,在该服务器中我们将使用数据库的二进制表示形式。

省力的方法

有效保存和重新加载缓存的元数据的问题归结为包含所述元数据的对象列表的序列化和反序列化。

在第一个实现中,我们只需要工作即可,因此我们使用了基本方法-使用BinaryFormatter将对象保存到字节数组。它使用方法Serialize和Deserialize很好且简单。给它一个对象,它将使用反射和魔术吐出描述该对象的字节数组。太好了-非常适合包含10个表,视图和其他对象的示例数据库。

但是真实的数据库远不及10个表或过程。更像是数以千计的对象。最重要的是过程的反序列化部分,对于一个如此大的数据库,该过程花费了大约400毫秒。结合其他必需的步骤,例如用户键入的脚本文档的解析和静态分析,这是完全不可接受的。

走向“更接近金属”

C#有许多处理序列化的库,它们提供各种类型的附加值和功能。但是我们决定使用自己的BinaryReader和BinaryWriter类。这些类提供基本.Net类型的序列化,而这就是我们所需要的。降低它会更困难,因为我们必须在(内存)流中逐步移动要保存的数据的大小。

这样,我们就无需对序列化消息进行版本控制或任何形式的格式描述-但这还是不必要的。每个需要序列化的类都必须具有自己的实现,即从字节数组中读取时如何创建该类或如何将其自身转换为此类字节数组。

使用该技术的总效果是,对于上述实际数据库而言,反序列化大约需要20毫秒-仅是原始基本方法的5%。现在,我们有更多的空间可以在设定的性能目标内为自动完成功能执行其他更有趣的操作。