Sharding作为海量存储高并发沙场中的制胜利器,制定合理的 Sharding 策略会为系统提供强有力的性能保障,对于多数业务场景,分库分表的规划方案如下:
- 数据量大,选分表
- 并发高,选分库
- 海量存储+高并发,分库+分表
Sharding 定位为利器的同时,它也存在一定的使用门槛,需要放弃一些数据库原生支持的能力,取舍由业务情况而定。由于 Sharding 相对复杂的特点,选择合适的分片键和分片策略,是分布式改造良好开端必不可少的两要素。
如何选择分片键(Sharding Key)
关于分片键的选择,我们需要选择具有共性的字段是最基本的要求,也是就尽量能覆盖绝大多数查询场景。同时分片键也应具有足够庞大的基数以及唯一性,从而使 Shard 可灵活规划,具备较好的扩展性。举个反例,如果选取布尔类型的字段为分片键,那么分片最多只能存在两份,这就陷入了非常尴尬的局面,基本失去了 Sharding 的意义。
分片键的选择建议
1.选择具有共性的字段作为分片键,即查询中高频出现的条件字段;
2.分片字段应具有高度离散的特点,分片键的内容不能被更新;
3.可均匀各分片的数据存储和读写压力,避免片内出现热点数据;
4.尽量减少单次查询所涉及的分片数量,降低数据库压力;
5.不要更换分片键,更换分片键需重分布数据,代价较大。
举例:融合法 + 冗余法
这里来思考一个在超大规模的电商场景中,使用订单 ID 和买家 ID 查询数据的问题。大规模电商场景每日会产生海量的交易订单,在这个场景中,我们选择使用订单 ID 作为分片键是一个没有异议的选择。那么此时,我们通过 APP 来查询自己的订单时,查询条件变为了分片键之外的买家 ID,默认情况下,查询语句中不带有分片键会导致全路由情况。面对这样的情况,应如何设计一个高效的分片策略?
大厂常常使用的方案是基因法,即将买家 ID 融入到订单 ID 中,作为订单 ID 后缀。这样,指定买家的所有订单就会与其订单在同一分片内了。
还有一个问题,电商场景除了订单和买家外,还有一个高频条件是卖家 ID,如果想按照卖家 ID 查询数据又该如何处理?
可以考虑做数据冗余的方案,即广播表(或克隆表)技术,在每个片上都存有一份相同的卖家数据,用空间换取效率。对于超大规模的电商场景,使用基因法+冗余数据的方案即可处理绝大部分订单、买家和卖家的请求。
对于其他检索条件的场景,可以考虑如下方案:
1.对于检索条件不包含订单 ID、买家 ID 和 卖家 ID 的查询,使用 ES 方案解决;
2.对于大型卖家的数据,可规划单独的库/表,或进一步拆分映射成多个虚拟商家,来避免热点。
分片算法
常见分片算法如 Hash、Range 和 List,用户也可以组合使用,或考虑结合业务定制分片算法。访问流量可均分、数据处理可并行、热点数据能规避,是比较理想的分片算法。在海量数据库高并发场景中,使用相对较多的分片算法是 Hash。
Hash
Hash算法会将数据相对均匀的分布在各分片中,可有效规避热点问题。虽然 Hash 方案成为多数场景中被青睐的方案,但跨片操作和分片扩缩容是 Hash 需面对的挑战。
Range
按照字段的取值范围对数据进行分片,对于范围条件的数据检索有较高的性能优势,基于 Range 的分片扩容也非常容易。在分布式架构中,Range 相对 Hash 没有那么通用化,热点问题是最大的劣势,它适合数据体量较大且并发不高的场景,
List
对于需要枚举值的业务场景中,List 算法是合适的选择,如基于地区或基于币种的业务,数据有明确的存储位置,单片失效不会影响全局业务。显而易见的是无法避免热点问题。
总结
分片策略的规划不能脱离业务,它没有统一的模板。理想的分片策略应尽量满足访问流量可均分、数据处理可并行、热点数据能规避。
引用:https://blog.csdn.net/daiyejava/article/details/127409237
网友评论