Mysql高可用高性能存储应用系列4 - 分库分表、中间件
概述
为什么要分库的原因:1)很多时候接口性能慢都是数据库造成的,2)并发量比较大时,大量的数据库请求,会带来磁盘I/O的性能瓶颈,3)来越多,导致sql查询数据,即使走了索引也比较慢。
分库分表的场景
分库和分表是不同的两个概念,解决的问题也不同。
- 并发量很大,但数据量比较小,可以只分库,不分表。
- 并发量不大,但数据量比较大,可以只分表,不分库。
- 并发量很大,数据量也比较多时,既要分库,也要分表。
垂直分库分表
分库垂直分库,针对一个系统的不同业务进行拆分,比如:用户拆到User库,文章拆到Novel库,拆分后放到不同的服务器上,在高并发一定程度能够给解决I/O链接数,硬件资源瓶颈等。
垂直分表
垂直分表,整体策略就是大表拆小表,基于表中字段拆分,将不常用的,数据较大的拆分到扩展表,一般针对几百列的大表进行拆分。
特点:
- 每个库/表的结构都不一样
- 每个库/表的数据至少一列一样
- 每个库/表的并集是全量数据
垂直拆分:
优点:拆分后业务清晰,数据维护简单,按照业务放到不同的服务器中。
缺点:
- 单表数据量大时,写读压力大
- 受业务影响,热门业务压力大,冷门业务造成资源浪费。
比如我们常用的user表,由2部分主要的功能组成,用户和密码主要是用来鉴权用户,一部分是展示用户信息,这个表就可以拆分成2个表。
mysql> desc user;
+----------------------+---------------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+---------------------+------+-----+------------+----------------+
| user_id | int(4) | NO | PRI | NULL | auto_increment |
| user_name | varchar(16) | NO | MUL | NULL | |
| user_psw | varchar(50) | NO | | NULL | |
| user_status | tinyint(1) | NO | | 1 | |
| user_display | tinyint(4) | NO | | 1 | |
| user_nickname | varchar(50) | YES | MUL | NULL | |
| user_email | varchar(50) | NO | MUL | NULL | |
| user_qq | varchar(15) | YES | | NULL | |
| user_tel | varchar(18) | YES | MUL | NULL | |
| user_sex | int(1) | NO | | 1 | |
| user_birthday | date | NO | | 1991-10-24 | |
| user_head | varchar(200) | NO | | NULL | |
| user_sign | varchar(225) | NO | | NULL | |
| user_ticket_act | int(11) | NO | | 0 | |
| user_gold2 | decimal(12,3) | NO | | 0.000 | |
| user_gold2_frozen | int(11) | NO | | 0 | |
| user_ticket_rec | int(11) | NO | | 0 | |
| user_ischarge | tinyint(1) | NO | | 0 | |
| user_exp | int(4) | NO | | 0 | |
| user_browse | int(4) | NO | | 0 | |
| user_logintime | int(4) | NO | | 0 | |
| user_regtime | int(4) | NO | MUL | 0 | |
| user_regip | varchar(15) | NO | | 0.0.0.0 | |
| user_displaytime | int(4) | NO | | 0 | |
| user_nickname_before | text | YES | | NULL | |
| user_auth | tinyint(1) | NO | | 0 | |
| delete_time | int(11) | NO | | 0 | |
| refresh_token | varchar(50) | NO | | | |
| nation_code | int(10) unsigned | NO | | 86 | |
| credit | int(11) | NO | | 0 | |
| close_time | int(10) unsigned | NO | | 0 | |
| finance_id | int(11) | NO | | 0 | |
| user_underage_status | tinyint(1) | NO | | 0 | |
| attr_limit | varchar(20) | NO | | NULL | |
| identity | tinyint(3) unsigned | NO | | 1 | |
+----------------------+---------------------+------+-----+------------+----------------+
35 行于数据集 (0.08 秒)
水平拆分分库分表
- 水平分表,针对数据量巨大的单表,按照某种规则,拆分到多个表中,但是这些表还是在一个库中。
- 水平库分表,按照某种规则,把拆分的表再拆到不同的库中去。
水平分库的分表规则
- RANGE, 按照范围拆分,比如0-10000一个表,10001到20000一个表
- HASH取模,比如通过用户ID取模,然后分配到不同的库表中。
- 地理区域,比如按照华北,东北等区域区分。
- 时间拆分,比如将6个月前的数据拆出去放到一张表,随着时间的流逝,这些表的数据查询的几率很小,这也是冷热数据分离。
水平拆分
优点:
- 单库/表的数据减少,有利性能
- 库/表结构相同,程序改动小
缺点:
- 数据库扩容难度大,比如取模值变了
水平拆分的好处在于使用一个业务场景的划分值,比如user_id
进行取模进行分表,但是在后台管理数据的时候会遇到一个问题,数据聚合和保持一致性的问题,比如我们现在业务中的评论数据就很多,user_id
对 128进行取模,再后台管理的时候,把数据再存储在MongoDB中一份做数据管理使用,所有的方法都会有优缺点的,看你怎么设计更符合业务场景。
一致性Hash算法
普通取模方法
根据10取模,数据1保存到节点上,数据2保存在节点2上,当节点数发生变化,分子发生了变化,需要同步旧数据的值。
一致性Hash算法
- hash值是非负的整数,值的范围构成一个圆环,值为2^32
- 集群节点按照一定的规则求hash值,然后放在环中
- 对数据K求hash值,然后放入环中,在按照顺时针方向找到最近的节点,保存到上面
一致性Hash算法是怎么解决节点上的存储不均的情况呢?
在节点上创建虚拟节点,让虚拟节点对应真实节点,让数据的存储尽量均衡,这样就解决了数据不均的情况。
当添加新节点时,在普通取模算法中会影响到其他数据,而在一致性Hash算法中,首先会停止服务,防止数据乱掉,先把属于新增节点D的数据迁移到D节点中,然后添加节点,放入环中,启动服务。
当移除节点时,首先停止服务,把D节点移除之后,根据顺时针原则,把属于D节点的数据迁移在节点A上,把D节点服务移除,启动服务,整个节点受到影响的只有D节点的数据,不会影响其他的服务节点。
中间件
中间件的范围比较笼统,我理解的中间件就是在数据处理流程里,加入了一层逻辑处理,用规定好的规则下发在下一个流程中,主要介绍Mysql的2种中间件,ShardingSphere(直连模式) 和 MyCat(代理模式)。
- ShardingSphere:Jdbc直连,相当于增强JDBC包,对所有的数据库操作语句按照特定的要求分发到不同的库中,这部分操作在jdbc中直接完成了。
- MyCat:proxy代理,把sql发送给代理服务,代理服务去数据库完成相应的操作后再返回给web应用。
转载自:https://juejin.cn/post/7216360818766020666