[关闭]
@JunQiu 2018-09-18T21:17:43.000000Z 字数 5046 阅读 2024

分布式CAP理论、mongo_连接数、driver(连接池)、分表分表:避免数据迁移和热点数据

summary_2018/08 arch mongodb database


1、日常

1.1、mongodb:连接数、driver连接池

1.2、分布式中的CAP理论浅读

1.3、一种可以避免数据迁移、热点数据的分库分表策略


2、技术

2.1、mongo的连接数与driver(连接池)

  1. 一些指标:
  2. 1TPS: 每秒事务量
  3. 2IOPS:(Input/Output Operations Per Second),即每秒进行读写(I/O)操作的次数,多用于数据库等场合,衡量随机访问的性能。
  4. 3QPS: 每秒Query
  5. 资源占用:
  6. 1、内存:MongoDB为例,每个线程都要分配1MB的栈内存出来。1000个连接,1G内存就这么没了,甭管是否是活跃连接
  7. 2、文件句柄:每个连接都要打开一个文件句柄,当然从成本上讲,这个消耗相对内存是小了很多。但换个角度,文件句柄也被其他模块消耗着,比如WT存储引擎,就需要消耗大量的文件句柄
  1. 通常 MongoClient 使用默认100的连接池(具体默认值以 Driver 的文档为准)都没问题,当访问同一个 Mongod 的源比较多时,则需要合理的规划连接池大小。
  2. 举个例子,Mongod的连接数限制为2000,应用业务上有40个服务进程可能同时访问 这个Mongod,这时每个进程里的 MongoClient 的连接数则应该限制在 2000 / 40 = 50 以下 (连接复制集时,MongoClient 还要跟复制集的每个成员建立一条连接,用于监控复制集后端角色的变化情况)。

2.2、分布式中的CAP理论浅读

  1. 1、一致性
  2. 一致性指“all nodes see the same data at the same time”,即所有节点在同一时间的数据完全一致。
  3. 对于一致性,可以分为从客户端和服务端两个不同的视角。
  4. 客户端
  5. 从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。
  6. 服务端
  7. 从服务端来看,则是更新如何分布到整个系统,以保证数据最终一致。
  8. 对于一致性,可以分为强/弱/最终一致性三类
  9. 从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。
  10. // 强一致性
  11. 对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。
  12. // 弱一致性
  13. 如果能容忍后续的部分或者全部访问不到,则是弱一致性。
  14. // 最终一致性
  15. 如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
  16. 2Availability 可用性
  17. 可用性指“Reads and writes always succeed”,即服务在正常响应时间内一直可用。
  18. 3Partition Tolerance分区容错性
  19. 分区容错性指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务。
  1. 一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。
  2. 当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。
  3. 提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。
  4. 然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。
  5. // 或者
  6. 想象两个节点分处分区两侧。允许至少一个节点更新状态会导致数据不一致,即丧失了C性质。如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质。除非两个节点可以互相通信,才能既保证C又保证A,这又会导致丧失P性质。
  1. // 实际上情况根据场景而言
  2. 对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性,即保证PA,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。
  3. 对于涉及到钱财这样不能有一丝让步的场景,C必须保证。网络发生故障宁可停止服务,这是保证CA,舍弃P

2.3、一种可以避免数据迁移、热点数据的分库分表策略

  1. 1mod方式
  2. 2dayofweek系列日期方式(所有星期1的数据在一个库/表,或所有?月份的数据在一个库表)
  3. 本质的特点:离散性加周期性。
  4. 存在的问题:
  5. 1、随着数据量的增大,每个表或库的数据量都是各自增长。当一个表或库的数据量增长到了一个极限,要加库或加表的时候,介于这种分库分表算法的离散性,必需要做数据迁移才能完成。
  6. 2、考虑到数据增长的特点,如果我们以代表时间增长的字段,按递增的范围分库,则可以避免数据迁移。但是这样的方式会带来一个热点问题:当前的数据量达到某个库表的范围时,所有的插入操作,都集中在这个库/表了。
  7. 因此:我们在分库分表的时候应该尽量避免这两个问题:数据迁移、热点
  1. ## 比如
  2. // 阶段一:一个数据库,两个表
  3. 分库规则dbRule: DB0
  4. 分表规则tbRule: t + (id % 2)

  1. // 阶段二:当单库的数据量接近1千万,单表的数据量接近500万时,进行扩容(数据量只是举例,具体扩容量要根据数据库和实际压力状况决定
  2. 增加一个数据库DB1,将DB0.t1整表迁移到新库DB1。每个库各增加1个表,未来10M-20M的数据mod2分别写入这2个表:t0_1t1_1
  3. 分库规则dbRule:
  4. DB + (id % 2)
  5. 分表规则tbRule:
  6. if(id < 1千万){
  7. return t”+ (id % 2); //1千万之前的数据,仍然放在t0和t1表。t1表从DB0搬迁到DB1库
  8. }else if(id < 2千万){
  9. return t”+ (id % 2) +”_1”; //1千万之后的数据,各放到两个库的两个表中: t0_1,t1_1
  10. }else{
  11. throw new IllegalArgumentException(“id outof range[20000000]:” + id);
  12. }
  13. 10M以后的新生数据会均匀分布在DB0DB1;插入更新和查询热点仍然能够在每个库中均匀分布。每个库中同时有老数据和不断增长的新数据。每表的数据仍然控制在500万以下。

  1. // 阶段三:当两个库的容量接近上限继续水平扩展时,进行如下操作
  2. 新增加两个库:DB2DB3. id % 4分库。余数0123分别对应DB的下标. t0t1不变,
  3. DB0.t0_1整表迁移到DB2; DB1.t1_1整表迁移到DB3
  4. 20M-40M的数据mod4分为4个表:t0_2t1_2t2_2t3_2,分别放到4个库中:
  5. // 分库规则dbRule:
  6. if(id < 2千万){
  7. //2千万之前的数据,4个表分别放到4个库
  8. if(id < 1千万){
  9. return db”+ (id % 2); //原t0表仍在db0, t1表仍在db1
  10. }else{
  11. return db”+ ((id % 2) +2); //原t0_1表从db0搬迁到db2; t1_1表从db1搬迁到db3
  12. }
  13. }else if(id < 4千万){
  14. return db”+ (id % 4); //超过2千万的数据,平均分到4个库
  15. }else{
  16. throw new IllegalArgumentException(“id out of range. id:”+id);
  17. }
  18. // 分表规则tbRule:
  19. if(id < 2千万){ //2千万之前的数据,表规则和原先完全一样,参见阶段二
  20. if(id < 1千万){
  21. return t”+ (id % 2); //1千万之前的数据,仍然放在t0和t1表
  22. }else{
  23. return t”+ (id % 2) +”_1”; //1千万之后的数据,仍然放在t0_1和t1_1表
  24. }
  25. }else if(id < 4千万){
  26. return t”+ (id % 4)+”_2”; //超过2千万的数据分为4个表t0_2,t1_2,t2_2,t3_2
  27. }else{
  28. throw new IllegalArgumentException(“id out of range. id:”+id);
  29. }

  1. 随着时间的推移,当第一阶段的t0/t1,第二阶段的t0_1/t1_1逐渐成为历史数据,不再使用时,可以直接truncate掉整个表。省去了历史数据迁移的麻烦。(TDDL2.x中已经实现)
  2. 当然还可以成倍数的继续扩充,但也可以不成倍的扩充,有兴趣可以阅读原文。
  1. 1、离散映射:如moddayofweek,这种类型的映射能够很好的解决热点问题,但带来了数据迁移和历史数据问题。
  2. 2、连续映射;如按idgmt_create_time的连续范围做映射。这种类型的映射可以避免数据迁移,但又带来热点问题。
  3. 因此,我们应该两者结合,合理的根据需求构建,基于以上考量,分库分表规则的设计和配置,长远说来必须满足以下要求:
  4. 1、规则可以分层级叠加,旧规则可以在新规则下继续使用,新规则是旧规则在更宽尺度上的拓展,以此支持新旧规则的兼容,避免数据迁移
  5. 2、用mod方式时,最好选2的指数级倍分库分表,这样方便以后切割。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注