为什么
分表:解决单表数据量过大,查询性能下降,ddl困难问题
分库:解决单库连接数不足,写并发高,通过分摊压力提高吞吐量,单机瓶颈。
问题
引入了分布式复杂性
方式
垂直分库分表
按照业务模块拆分库
按列拆分表
水平分库分表
按行拆分,解决单表过大问题,应对大数据量核心手段
分片键
怎么选?
查询频率高的
路由算法
范围分片
按照id或时间分。扩容,查询方便
缺点,容易数据倾斜
哈希取模
分片键哈希取模,数据分布均匀
缺点,扩容困难,需要迁移数据
一致性哈希
解决扩容问题,节点变化前只需迁移少量数据。
缺点,实现复杂,存在哈希倾斜风险
分库分表数量为什么是2的幂次方
和hashMap是一个道理。
- 位运算优化:hash % 8 等价于 hash & (8-1),性能更高。
- 均匀分配:便于将表均匀分配到多个库中。
- 易于扩容:从N张表扩容到2N张表时,只需迁移一半数据。利用了“高位运算”:因为容量永远是 2 的幂,扩容时只需看 hash 值新增的那一位高位是 0 还是 1。是 0 就留在原索引(低位),是 1 就迁移到 原索引 + 旧容量(高位)。
分布式ID
雪花算法,redis自增(挂了怎么办?)
跨库JOIN
- 常用字段冗余
- 全局表:每个库存一份变动少基础表(配置)。本地join
- 应用层组装,查好数据在内存中组装
- 相关联的数据,强制放在一个节点。比如订单和订单详情,相同的订单,数据要放在同一个节点。
分布式事务如何处理?
比如强一致性的二阶段提交。但是不建议使用,因为性能差。
最终一致性,比如tcc和saga。 Tcc比较复杂,每种事务都要实现三个方法 Try ,confirm和cancel。本地消息表加重试,或者rocket mq事务消息。
非分片键如何查询?
一,建立映射表,非分片键和分片键,建立映射表
二,异构索引,根据另一个维度,再建立一套分库分表。
三,基因法,将分片信息植入到id中。比如订单表和用户id。订单号包含用户id分片信息。
生成订单号的时候,可以将用户id生成二进制,然后放到订单号的尾部。查询的时候解析出来即可。
或者是生成雪花id的时候,专门划分几位,保存库表编号。
基因法的一个优点就是不用去查映射表,也不用去配置中心拉取路由规则,计算路由。速度会很快,就是单纯的位运算。那他的一个缺点也很明显,就是灵活性很差,比如说我一个节点挂了,或者发生数据倾斜,或者是要扩容,迁移数据的时候,就没有办法使用。
这里一个解决方案就是可以兜底,使用映射覆盖,就是在中间件层设置一个优先级更高的热迁移配置。如果有设置配置,那么就去读取配置强制使用配置中的路由,否则就使用id中的路由。
如何设计?
当查询遇到分页怎么办?
先查出来,然后再在内存中聚合。但是性能比较差。尽量能不分页就不分页,或者使用es。
如何设计动态扩容方案
一,停机扩容,但是不推荐,数量大的时候会很慢。
二,一次性规划好,预分配。一次性就设计好,32个库,每个库32张表,一共是1024张表。够用好久,避免扩容迁移数据。