一、切分方式

  1. 垂直切分

    1. 垂直分库:将不同业务数据存放到不同的库。如订单库,商品库

    2. 垂直分表:将一个表的大字段且不常访问字段,划分出来放到其他的表。内存中的数据页可以存放更多的热点数据。增加查询效率,减少I/O

优点:业务解偶,不同业务数据独立维护;一定程度缓解库的压力

缺点:多表/多库访问,需要在接口层聚合数据;分布式事务管理难度增加;依然有单表数据量多大问题

  2. 水平切分

    1.  库内分表:降低单一表的数据量,对数据库的CPU,内存和网络I/O的竞争没变、

    2. 分库分表:将切分的子表分散到不同数据库,降低单表数据量,也降低单库的访问压力

优点:解决高并发下单库访问量,提升系统稳定性和负载

缺点:跨库的join关联查询性能差;扩容难度和维护量大;跨分片事务一致性难保证  

二、分库分表规则

  1. 根据取值范围

    按照时间区间或者ID区间。

优点:

      1. 单表数据量可控

      2. 水平扩展只需要添加新节点,无需对其他分片数据进行迁移

      3. 能快速定位要查询的数据在哪个库

      4. 范围查找,不需要跨分片查询

缺点:

      1. 连续分片存在数据热点,比如某个范围查询频率很高

  2. hash取模

    对分库字段的hash结果取模,得到余数即数据库编号。假如共有N个数据库,编号从0到N-1。则 hash(user_id) mod N 得到的结果为数据库编号

优点:

      1. 数据分片相对均匀,不容易出现热点和并发访问的瓶颈

缺点:

      1. 当集群中的数据库宕机,数据库总数减少,计算数据库算法 hash(user_id) mod N-1 ,同一个用户的信息不在一个库中。(考虑一致性哈希)

      2. 集群扩容时,需要迁移旧数据。(一致性hash算法可以避免)

      3. 如果查询条件不带分库字段,则无法定位到具体的库,将遍历所有库查询

三、分库分表带来的问题

  1. 事务一致性问题

    保证强一致性:使用强一致性的分布式事务比如2PC,最大限度保障一致性。但是延长了事务提交时间,进而导致加锁时间延长,降低并发量。

    保证最终一致性:对性能要求高,对一致性要求不高的,采用事后补偿机制。比如定期和标准数据源同步。

  2. 跨节点关联join问题

    1. 全局表:需要关联查询的表类似于配置表,那么在每个节点都配置一份表。

    2. 字段冗余:将需要的字段额外添加一份到表中

    3. 数据组装:在上层分两次查询,将结果组装

    4. ER分片:

    5. 数据异构:按照查询需求将数据重新导出到Redis/ES/DB

  3. 跨节点分页、排序、函数问题

    如果排序、分页字段恰好是分片字段,按照分片规则容易定位需要的分片。如果不是分片字段,需要在所有节点都执行一下同等的分页、排序等操作,然后在业务层进行合并和二次处理。

  4. 全局主键避重问题

    表中数据分布在不同分页中,分散的表将无法使用各自的自增ID作为主键,需要设置新的主键规则,一般有如下几种:

    1. UUID

    UUID是随机的,作为主键

    1. 插入时会造成索引页的频繁分裂,移动大量数据

    2. 频繁分裂导致每个索引页上的数据是稀疏的,并容易产生碎片(大小不合适无法被再次被利用的内存)

    3. 写入时需要加载磁盘中的页到缓存,造成大量随机I/O。跟顺序ID写完一页才写下一页不一样。

    2. 主键生成表

    多个节点公用一张生成表,生成主键并发插入时通过数据库锁控制获得自增主键。并发限制在单台数据库的性能,当数据库宕机时,整个系统不可用。使用主从备份,从服务器不一定跟主服务器是完全一致的,存在延迟写的问题。

改进:部署两台以上的数据库,自增的步长设置为节点的数量。每台初始值设置从1到N。保证了每台生成的自增ID不同,当其中一台数据库宕机时,可以从其他数据库获取。

    3. 雪花ID

    生成规则:1bit(符号位) + 41bit(时间戳) + 10bit(工作机器id) + 12bit(序列号) 生成64位的Long型数字

    依赖时钟,如果服务器时钟同步导致回拨,会产生重复ID。并且字段长导致索引页增多。

  5. 数据迁移和扩容问题   

    如果采用取模计算分片,扩容时需要读取历史数据,再按照新的分片规则写入到不同分片。

   

 

参考:https://zhuanlan.zhihu.com/p/99396275

人生就像蒲公英,看似自由,其实身不由己。