1、为什么分库分表
分库的原因:
1)QPS过高,数据库响应速度来不及,一般mysql单机也就1000左右的QPS,如果超过1000,就要考虑分库。
分表的原因:
1)单表太大,复杂SQL的查询速度变慢,一般mysql单表也就1000万左右的量,如果超过1000万,就要考虑分表;
2、垂直拆分与水平拆分
1)垂直拆分表
把一个表的多个字段分别拆成多个表,一般按字段的冷热拆分,热字段一个表,冷字段一个表。拆完以后,可以放在一个库,也可以放在多个库里。
2)水平拆分表
把一个表按照某个字段,拆分到多个表里面,这个字段叫sharding-key。
3、Sharding-key的选择
方案一:
可以用id的hash值取模,需要分多少个表,就取什么的模
这种方案的缺点是如果分表的数量变了的话,所有的数据都要重新放到不同的分表
方案二:
可以用id的一致性hash,将数据分到不同的表中
这种方案只需要把在之前的分表,现在需要放在新的分表的数据移动即可。
方案三:
根据时间范围来分表。
可以划分最近3个月的数据,3-12个月的数据,12月之前的数据,这种分法可区分数据的冷热程度,对于热数据,加redis缓存访问来提高访问效率,对于3-12个月的数据,由于使用的少,可以放到ES或hbase或mongoDB里面,对于12月之前的数据,可以使用hive只支持离线统计,不支持纪录查询。
也可以划分相同范围的时间,比如以一个月为范围划分,不断添加新的月度表。
这种方案的缺点是数据量并发还是都作用在同一张表上,无法提高同一张表的并发效率。
4、全局唯一ID的生成
方案一:雪花模型算法
通过雪花模型服务生成唯一ID。
方案二:mysql自增初始值与步长
通过mysql的auto increment,从mysql取ID。
为避免单点故障,用多台mysql服务器,每台mysql服务器上放一个自增的sequence表,自增的步长就是自增服务器的数量,sequence表的起始值分别是1,2…N。
缺点是要两台以上mysql,废资源。
方案三:自己写编码规则
自己写的编码规则不仅能尽量保证唯一性,还能携带业务规则,方便逻辑判断。
一般自己写的唯一ID都会包括三部分:时间戳+随机数+某个编码。
其中时间戳用来区分数据的冷热;某个编码可以是某个业务编码,比如用户编码、服务器编码、地域编码等等,这个业务编码也用来区分数据;随机数必须使用高唯一性随机码。
缺点是规则设计比较难,要充分考虑业务情况。另外,由于使用了随机数,是有可能存在着ID重复的可能性的,所以要有容错机制,即如果插入的时候ID重复,那么重新生成再插入。
5、分库分表的动态扩容
一开始就设计32个库,32个表,一次弄个够,后面就不扩了。
6、如何解决读写分离主从库数据不一致
问题出现原因:往主库写数据以后,从库还没来得及从主库复制数据,读请求就已经发到从库了,这时从库的数据就不是最新的,查到的就不是新数据。
解决办法:配置主库的复制策略为半同步复制,必须有一个slave节点接收到主库复制的数据,主库才认定这条SQL执行成功。
7、如何解决跨库Join的问题
问题出现原因:分库分表以后,数据处于不同的库里,这个时候需要做关联查询
解决办法:
1)首先,对于这类问题,我们的首要方案是尽力避免跨库Join,避免的方式包括:
——采用mycat全局表,比如数据字典,在每个库里都需要用到,那我们在每个库里都放一份,然后做成全局表进行同布,这样库表跟数据字典的join就不会跨库了;
——采用字段冗余,比如ID和NAME,库库里只有ID,没有NAME,NAME在另一个库里,那我们在设计之初就在表里也加上NAME,这样就不用会查另一个库的表了。不过这样有个问题,就是数据不一致的问题,需要定期更新这部分数据;
2)其次,如果已经出现了这种情况,那解决方式有:
——Mycat中配置表的childtable,作为它的从表,并设置joinKey,这样从表的数据也会跟主表存在一个库里面;
——手工查询,先主库查出来,然后一条一条从从库查,这样性能比较低。不过可以对从库的数据查询加缓存,能提高效率。
8、线上环境分库分表后的迁移
采用双写的方式,修改代码,所有涉及到分库分表的表的增、删、改的代码,都要对新库进行增删改。
同时,再有一个数据抽取服务,不断地从老库抽数据,往新库写,边写边按时间比较数据是不是最新的。