一、分库分表
1、随着时间和业务发展,数据库数据量不可控,造成表中数据越来越多,此时再进行CRUD操作的话,会造成很大的性能问题,比如查询实时数据,表数据达到了千万级别,要求一分钟查询一次,但你一个select就要耗时2两分钟才能执行完,这岂不是很尴尬。
2、分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。
3、性能解决方案
方案1
通过提升服务器硬件能力来提高数据处理能力,比如增加存储容量 、CPU等,这种方案成本很高,并且如果瓶颈在MySQL本身那么提高硬件也是有限的。
方案2
把数据分散到不同的数据库中,使得单一数据库的数据量变小来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。
二、分库分表的方式
垂直分表、垂直分库、水平分表、水平分库。
1、垂直分表
(1)基本概念
将一个表按照字段分成多个表,每个表存储其中一部分字段。
(2)性能提升
为了避免IO争抢并减少锁表的几率;充分发挥热门数据的操作效率,热门字段和冷门字段分开存储,比如一个产品基本信息表、一个产品详细信息表,大字段一定要放在冷门字段的表中。
(3)为什么大字段IO效率低?
数据本身长度过长,需要更长的读取时间;跨页,页是数据库存储基本单位,很多查找及定位操作都是以页为单位,单页内的数据行越多数据库整体性能越好,而大字段占用空间大,单页存储数据少,因此IO效率低;数据以行为单位将数据加载到内存中,如果字段长度短,内存就可以加载更多的数据,减少磁盘IO,从而提高数据库性能;
2、垂直分库
(1)基本概念
垂直分表只解决了单一表数据量大的问题,但没有将表分布到不同的服务器上,因此每张表还是竞争同一个物理机的CPU、内存、网络IO、磁盘。
垂直分库的意思就是将表进行分类,分别部署在不同的数据库上面,每个库放到不同的服务器上,它的核心理念就是专库专用。
每个微服务使用单独的数据库。
(2)性能提升
解决业务层面的耦合,业务清晰 能对不同业务的数据进行分级管理、维护、监控、扩展等 高并发场景下,垂直分库一定程序上提升IO、减少数据库连接数、降低单机硬件资源的瓶颈
3、水平分表
(1)基本概念
水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。
(2)性能提升
优化单一表数据量过大而产生的性能问题 避免IO争抢并减少锁表的几率 单一数据库内的水平分表,解决了单一表数据量过大的问题,分出来的小表只包含一部分数据,从而使单表查询的速度更快,效率更好。
(3)水平分表的方式
① Hash取模分表
数据库分表一般都是采用这种方式,比如一个position表,根据positionId%4,并按照结果分成4张表。
优点:
数据分片较为平均,不容易出现热点和并发访问的瓶颈。
缺点:
容易产生跨分片查询的复杂问题。
② 数值Range分表
按照时间区间或ID区间进行切分。
优点:
单表大小可控 易于扩展 有效避免跨分片查询的问题
缺点:
热点数据成为性能瓶颈。
例如按时间分片,有些分片存储在最近时间段的表内,可能被频繁的读写操作,而历史数据表则访问较少。
③ 一致性Hash算法
较为复杂,小编暂时不做介绍,有兴趣的可以自行百度。
4、水平分库
(1)基本概念
水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。
(2)性能提升
解决了单库数据量大,高并发的瓶颈 提高了系统的稳定性和可用性
(3)何时使用
当一个应用难以再进行垂直切分,或垂直切分后数据量行数巨大,存在单库读写存储的性能瓶颈,这时候就可以考虑使用水平分库了。
(4)使用弊端
但水平分库的弊端也很明显,需要确定你所需要的数据在哪一个库中,因此大大提高了系统的复杂度。
5、小总结
垂直分表:热门数据、冷门数据分开存储,大字段放在冷门数据表中。垂直分库:按业务拆分,放到不同的库中,这些库分别部署在不同的服务器,解决单一服务器性能的瓶颈,同时提升整体架构的业务清晰度。水平分表:解决单一表数据量过大的问题 水平分库:把一个表的数据分别分到不同的库中,这些库分别部署在不同的服务器,解决单一服务器数据量过大的问题
三、分库分表带来的问题
1、学习成本问题 大多数初级开发者都不会分库分表,如果使用不得到,还不如直接使用单一数据库了。
2、事务问题 (1)解决方案1:使用分布式事务
① 优点
由数据库管理,简单有效。
② 缺点
性能代价高,特别是shard越来越多时。
(2)解决方案2:由应用程序和数据库共同控制
① 原理
将一个分布式的大事务分解成单个数据库的小事务,并通过应用程序来控制各个小事务。
② 优点
性能更佳
③ 缺点
需要应用程序在事务控制上做灵活设计,如果使用Spring的事务管理机制,改动起来面临一定困难。
3、跨节点join问题 解决方式是分两次查询。
4、跨节点的count、order by、group by以及聚合函数问题 与join的解决方案类似,分别在各个节点得到结果然后再合并。和join的差别在于,各节点的查询可以并行执行,因此很多时候它的速度会比单一张大表快很多。但是,如果结果集很大,对应用程序内存的消耗也是一个问题。
5、数据迁移、容量规划、扩容问题 6、主键ID问题 因为分表的原因,主键自增策略wufa实现。
解决方案1:UUID
使用UUID作为主键是最简单的方案,但是缺点是UUID非常的长,会占用大量存储空间,在进行连表查询的问题上也存在性能问题。
解决方案2:多维护一个Sequence表
建立一个新表,字段包含table_name和next_id。
看见表结构之后,秒懂吧?
大概意思就是记录分表后的每张表的下一个ID是多少,缺点很明显,就是每次插入数据都要访问这张表获取插入数据的id,该表很容易成为系统性能的瓶颈,同时它也存在单点问题,一旦该表数据库失效,整个系统都无法正常工作。此时可能通过主备机同步机制,解决单点问题。
解决方案3:Twitter的分布式自增ID算法Snowflake snowflake的结构如下(每部分用-分开):0 - 0000000000 0
00000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)
一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)
snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。
7、跨分片的排序分页
在不同的分片节点中将数据进行排序,并将结果进行汇总,再次排序。
四、分库数量
分库数量首先和单库处理能力息息相关,比如MySQL单库超过5000万记录,Oracle单库超过1亿条记录,数据库压力就很大了。
在满足上述前提下,如果分库数量少,达不到分散存储和减轻DB性能压力的目的;如果分库数量多,跨库访问也是个问题,如果是并发模式,要消耗宝贵的线程资源,如果是串行,偶数,执行时间急剧增加。
分库数量还会直接影响硬件的投入,所以要分多少个库,要进行综合评估,一般初次分库建议分为4-8个库。