从 MongoDB 到 Cassandra
开始选择新的存储(Cassandra)进行数据迁移,他们认为 Cassndra 是当时(2015 年底)唯一能满足他们要求的数据库(后面也打脸了)。他们对数据库的要求如下:
- 线性可扩展性——不需要手动进行数据的分片
- 自动故障转移——尽可能的进行自我修复
- 维护成本低——设置好后就能工作,以后数据量增加后只需要增加节点即可。
- 已经被证明有效——他们喜欢采用新技术,但又不是太新
- 可预测的性能——当 API 的响应时间的 P95 超过 80ms 时就会告警,他们也不希望在 Redis 或者在 Memcache 中缓存数据
- 不是 Blob 存储——如果必须不断地反序列化 Blob 并附加到它们,那么每秒写入数千条消息的效果并不好。
- 开源——掌控自己的命运,不想依赖第三方公司
理想很丰满现实很骨感,随着业务场景和消息规模的增长,2022 年初 Cassandra 有 177 个节点,拥有数万亿条消息 ,Cassandra 也出现了严重的性能问题。
在 Cassandra 中,读取比写入更昂贵。 写入会附加到提交日志并写入称为内存表的内存结构,最终刷新到磁盘。 然而,读取需要查询 memtable 和可能的多个 SSTable(磁盘文件),这是一个更昂贵的操作。 用户与服务器交互时的大量并发读取可以使分区成为热点,称之为“热分区”。 当数据集的大小与这些访问模式相结合时,导致 Cassandra 的集群陷入困境。
当遇到热分区时,它经常会影响整个数据库集群的延迟。 一个通道和存储桶对接收了大量流量,并且随着节点越来越努力地服务流量并且越来越落后,节点中的延迟将会增加。由于该节点无法跟上,对该节点的其他查询受到影响。 由于我们以仲裁一致性级别执行读取和写入,因此对服务热分区的节点的所有查询都会遭受延迟增加,从而导致更广泛的最终用户影响。
集群维护任务也经常造成麻烦。 他们很容易在压缩方面落后,Cassandra 会压缩磁盘上的 SSTable 以提高读取性能。 不仅的读取成本更高,而且当节点试图压缩时,还会看到级联延迟。
由于 Cassandra 是 Java 开发的,他们还花费了大量时间调整 JVM 的垃圾收集器和堆设置,因为 GC 暂停会导致显着的延迟峰值。
从 Cassandra 到 ScyllaDB
他们选取的方案是 ScyllaDB,这是一个用 C++ 编写的与 Cassandra 兼容的数据库。 它承诺提供更好的性能、更快的修复、通过每核分片架构实现更强的工作负载隔离,以及无垃圾收集器,听起来相当吸引人。它采用 C++编译而不是 Java 所以没有垃圾收集器的 GC 暂停问题。
ScyllaDB 也并不是完全没有问题,当以与表排序相反的顺序扫描数据库时,有反向查询性能不足的问题,现在 ScyllaDB 已经优先解决了这个问题。ScyllaDB 同样也存在“热分区”的问题,当前还是需要业务通过其他方式去解决。
热分区问题
Discord 采用的方案是:在 ScyllaDB 和业务服务之间加了一个中介服务(Rust 语言编写),它不包含任何业务逻辑,主要功能就是合并请求。
合并请求
如果多个用户同时请求数据库的同一行,那么只会查询数据库一次。 第一个发出请求的用户会导致该服务中启动工作任务, 后续请求将检查该任务是否存在并订阅它, 该工作任务将查询数据库并将该行返回给所有订阅者。
收敛请求
同时根据一致性 hash 将同类查询请求,比如同一个频道的请求,进一步收敛到中介服务,这个请求合并的效果更好。
迁移效果
将运行 177 个 Cassandra 节点减少到仅运行 72 个 ScyllaDB 节点。 每个 ScyllaDB 节点拥有 9TB 磁盘空间,高于每个 Cassandra 节点平均 4TB 的存储空间。1774-729=60T,这么看的话他们的存储空间也节省了一些。在 Cassandra 上获取历史消息的 p99 为 40-125 毫秒,而 ScyllaDB 的延迟为 15 毫秒,消息插入性能从 Cassandra 上的 5-70 毫秒 p99 到 ScyllaDB 上稳定的 5 毫秒 p99。
标签:存储,读取,Discord,数据库,ScyllaDB,请求,迁移,节点,Cassandra From: https://blog.51cto.com/JavaEdge/7970141