随机写入真的是最好的选择吗?

很多时候大家都建议采用随机(意思是完全随机)的方式写入以避免热分片,从这个意义上讲,讲写入分散到所有的节点上那么就可以充分发挥分布式集群的优势,提升写入吞吐,为了做到这一点,简单的做法是采用UUID,它可以被认为是非常稀疏的,可以被分散到所有的节点上,以分散单个节点,单个分片的写压力。但是考虑到rocksDB采用LSM-tree,底层通过异步compacion来保证同一层内数据有序(L0除外),这样这种完全随机的写就带来了灾难,因为SSTable之间重叠太多,会让compaction压力变的很大,占用大量的CPU和磁盘IO,这样就反噬了前面提到的随机写带来的写入性能提升。并且磁盘的寿命也会受到影响。 如果只进行部分随机化处理,比如shard bit策略,那么只要保证压力被分散到不同的节点上,同时又不至于那么稀疏,保证写入吞吐的同时兼顾rocksDB的compaction压力看起来是一个更好的选择。 目前我还没有机会进行这方面的测试仪量化这两种场景下的集群性能整体对比,如果有小伙伴有相关的对比测试数据,也希望分享一下。

在高并发场景下,如果有,比如 auto id 递增,或者时间戳 这类的场景,热点全部会落在一个 region 上面,性能会受到影响。

随机打散其实是挺好的。你想,当真的高并发的写入,数据分散在整个集群,这时不要考虑这单个请求,而是考虑无数多并发的请求,batch 过来之后,再散落在 region 上面的情况,也不完全算稀疏。

你说的顺序写入对 rocksDB 比较友好,这个从单机引擎的角度看是对的。并且有一些特定场景还真有这种问题,比如 loader 的导入数据。我们如果是完全按顺序的导入,是不能发挥集群能力的,因为都在一台机器上了;如果我们完全随机切数据后并发,不是特别好的匹配引擎的顺序写的性能优势;切成很多批,并且局部小块是有序的情况下,会获得比较好的综合性能。

  • UUID 是 128 bit 字符串, 不适合作为主键: 不仅长度大, 字符串比较的性能也不好. 实际上, 如果主键不是整型字段, TiDB 会使用自增的 rowid 作为"隐式主键"——把数据存入 TiKV 时实际作为 key 的是 rowid. 也就是说, 如果使用字符串主键和组合主键(无论构成主键的各个字段是否都为整型), 实际效果等同于自增 ID 做主键. 我们的经验里, 即使用了 SHARD_ROW_ID_BITS, 也不能完全避免写入性能问题.
  • 如果数据并发写入量较大, 主键还是要使用唯一的 INT/BIGINT 值, 并将其随机打散. 有多种做法可以生成唯一 ID, 比如较为常用的 Twitter Snowflake 算法; 唯一 ID 往往是单调递增或者趋势递增的, 将其随机化打散的常用做法是 bit-reverse, 10100 --> 00101. 关于高并发唯一序列号生成方案, 这里有一些介绍.
  • TiDB 3.1 开始引入 AUTO_RANDOM ID. 非常期待这个功能进入实用阶段.相信它能降低生成随机唯一 ID 的门槛, 并将 “每一个 TiDB 表都要有一个随机分布的整型主键” 固化为和 “每一个 MySQL 表都要有一个单调递增的整型主键” 一样的日常开发规范.

关于热点和高并发写入, 不妨参考这两篇: 1 2

2赞