分库分表
目录
分库与分表 #
分表
比如你单表都几千万数据了,你确定你能扛住么?绝对不行,单表数据量太大,会极大影响你的 sql执行的性能,到了后面你的 sql 可能就跑的很慢了。一般来说,就以我的经验来看,单表到几百万的时候,性能就会相对差一些了,你就得分表了。
分表就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。比如按照用户 id 来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在 200 万以内。
分库
分库就是你一个库一般我们经验而言,最多支撑到并发 2000,一定要扩容了,而且一个健康的单库并发值你最好保持在每秒 1000 左右,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。
简而言之就是数据拆分:将一个表结构分为多个表,或者将一个表数据分片后放入多个表,这些表可以放在同一个数据库里,也可以放到不同的数据库中,甚至可以放到不同的数据库实例中。
数据拆分的方式 #
数据拆分有两种方式:
- 垂直拆分: 根据业务的维度,将原本一个库中的表拆分多个表,每个库中表与原有的结构不同
- 水平拆分: 根据分片算法,将一个库拆分成多个库,每个库依旧保留原有的结构
在实际的开发过程中,通常是先进行维度拆分形成微服务结构,然后再进行水平拆分
拆分方式 | 概念 | 优点 | 缺点 |
---|---|---|---|
垂直拆分 | 就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表里去,然后将较多的访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。 | 1. 拆分后业务清晰,拆分规则明确 2. 系统之间进行整合或扩展容易 3. 按照成本、应用等级、应用的类型等将表放到不同的机器上,便于管理 4. 便于实现动静分离、冷热分离的数据库表的设计模式 5. 数据维护简单 | 1. 部分业务表无法进行关联、只能通过接口的方式来解决,提高了系统的复杂度 2. 受每种业务不同的限制,存在单库性能瓶颈,对数据扩展和性能提升不友好 3. 事务处理复杂 |
水平拆分 | 就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来抗更高的并发,还有就是用多个库的存储容量来进行扩容。 | 1. 单库单表的数据保持一定的量级,有助于性能的提高 2. 切分的表的结构相同,应用层改造较少,只需要增加路由规则即可 3. 提高了系统的稳定性和负载能力 | 1. 切分后数据是分散的,很难利用数据库的关联查询,跨库查询性能较差 2. 拆分规则难以抽象 3. 分片数据的一致性难以解决 4. 数据扩容的难度和维护量极大 |
综上所述,我们发现垂直拆分和水平拆分具有共同点:
- 存在分布式事务问题
- 存在跨节点join的问题
- 存在跨节点合并排序、分页的问题
- 存在多数据源管理的问题
- 垂直拆分更偏向于业务拆分的过程,在技术上我们更倾向于水平切分的方案;
实例分析 #
比如我们有一张表,随着业务的不断进行,mysql中表中数据量达到了10亿,若是将数据存放在一张表中,则性能一定不会太好,根据我们使用的经验,mysql数据库一张表的数据记录极限一般在5000万左右,所以我们需要对进行分片存储(水平拆分),按照5000万一个单位来拆分的话,需要切片数量20个,也就是20个数据库表
如果将20个相同业务表存放在同一个数据库中,那么单个数据库实例的网卡I/O、内存、CPU和磁盘性能是有限的,随着数据库访问频率的增加,会导致单个数据库实例和数据库达到性能瓶颈,因此我们需要将20个表分到多个数据库和多个数据库实例中,具体的实现如下:
分库分表的方式 #
分库分表是对数据库拆分的一种解决方案,根据实施切片逻辑的层次不同,我们将分库分表方案大致分为三大类:
客户端分片、代理分片和支持事务的分布式数据库。
客户端分片 #
所谓的客户端分片即在使用数据库的应用层直接操作分片逻辑,分片规则需要在同一个应用的多个节点间进行同步,每个应用层嵌入一个操作切片的逻辑实现。
在客户端分片,目前主要有以下三种方式:
在应用层直接实现
这是一种非常通用的解决方案,直接在应用层读取分片规则,解析分片规则,根据分片规则实现切分的路由逻辑,从应用层直接决定每次操作应该使用哪个数据库实例中的对应的数据库。这种解决方案虽然有一定的代码侵入,但是实现起来比较简单,但是切片的逻辑是自己开发的, 如果生产上遇到了问题,能快速定位解决;
当然这种方式也存在缺点:代码的耦合度比较高,其次这种实现方式会让数据库保持的链接比较多,这要看应用服务的节点数量,需要提前进行容量上的评估。
通过定制JDBC协议实现
这种解决方案主要是为了解决1中解决方案中的代码耦合,通过定制JDBC协议来实现(主要是针对业务逻辑层提供与JDBC一致的接口),让分库分表在JDBC的内部实现
目前当当网开源的框架:Sharding JDBC 就是使用这种解决方案来实现的
通过定制ORM框架实现
目前ORM框架非常流行,流行的JPA、Mybatis和Hibernate都是优秀的ORM框架,通过定制ORM框架来实现分库分表方案,常见的有基于Mybatis的分库分表方案的解决;
<select id="selectUser" parameterType="java.util.Map" resultType="User"> select user_id as userId,user_name as userName from user_#{index} where user_id = #{userId} </select>
代理分片 #
代理分片就是在应用层和数据库层之间添加一个代理层,把分片的路由规则配置在代理层,代理层对外提供与JDBC兼容的接口给应用层,在业务实现之后,在代理层配置路由规则即可;
这种方案的优点:让应用层的开发人员专注于业务逻辑的实现,把分库分表的配置留给代理层处理。 同样这种业务存在缺点:增加了代理层,这样的话对每个数据库操作都增加了一层网络传输,这对性能是有影响的,同时需要维护增加的代理层,也有了硬件成本,线上生产环境出现了问题,不能迅速定位,需要有一定的技术专家来维护。
我们常见的 Mycat就是基于此种解决方案来实现的。
支持事务的分布式数据库 #
支持分布式事务的框架,目前有OceanBase、TiDB框架,这些框架将可伸缩特定和分布式事务的实现包装到了分布式数据库内部实现,对使用者透明,使用者不需要直接控制这些特性,但是对事务的支持不如关系型数据,适合大数据日志系统、统计系统、查询系统、社交网站等。
常见的分片策略 #
按照哈希切片 #
对数据库的某个字段进行来求哈希,再除以分片总数后取模,取模后相同的数据为一个分片,这样将数据分成多个分片的方法叫做哈希分片。我们大多数在数据没有时效性的情况下使用哈希分片,就是数据不管是什么时候产生的,系统都需要处理或者查询;
优点:数据切片比较均匀,数据压力分散的效果好
缺点:数据分散后,对于查询需求需要进行聚合处理
好处在于说,可以平均分配每个库的数据量和请求压力;
坏处在于说扩容起来比较麻烦,会有一个数据迁移的过程,之前的数据需要重新计算 hash 值重新分配到不同的库或表
按照时间切片 #
按照时间的范围将数据分布到不同的分片上,比如我们可以将交易数据按照月进行切片,或者按照季度进行切片,由交易数据的多少来决定按照什么样的时间周期来进行切片。这种切片方式适合明显时间特点的数据,常见的就是订单历史查询。
好处在于说,扩容的时候很简单,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了;缺点,但是大部分的请求,都是访问最新的数据。实际生产用 range,要看场景。
分库分表中间件 #
这个其实就是看看你了解哪些分库分表的中间件,各个中间件的优缺点是啥?然后你用过哪些分库分表的中间件。
cobar #
阿里 b2b 团队开发和开源的,属于 proxy 层方案。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库 join 和分页等操作。
TDDL #
淘宝团队开发的,属于 client 层方案。支持基本的 crud 语法和读写分离,但不支持 join、多表查询等语法。目前使用的也不多,因为还依赖淘宝的 diamond 配置管理系统。
atlas #
360 开源的,属于 proxy 层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在 5 年前了。所以,现在用的公司基本也很少了。
sharding-jdbc #
当当开源的,属于 client 层方案。确实之前用的还比较多一些,因为 SQL 语法支持也比较多,没有太多限制,而且目前推出到了 2.0 版本,支持分库分表、读写分离、分布式 id 生成、柔性事务(最大努力送达型事务、TCC 事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从 2017 年一直到现在,是有不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。
mycat #
基于 cobar 改造的,属于 proxy 层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于 sharding jdbc 来说,年轻一些,经历的锤炼少一些。