目录
一、面临问题
我们在后端开发中,经常会用到数据库,数据库往往是系统中不可或缺的一部分。伴随着业务量的增长,数据量也随之增长。当增长到一定程度时,数据库往往会成为系统的瓶颈。
- 当热点数据太多,数据库缓存放不下时,每次查询时会产生大量的IO。
- 请求的数据太多,网络带宽不够。
- 单表数据量太大,查询时扫描的行太多,SQL效率低。
二、解决问题
当单个数据库和表无法承受巨大的业务量时,我们就要考虑分库分表。也就是将单节点压力分散到多个节点上,从而提高系统的并发量和吞吐量。
- 分库:将单个数据库拆分成多个数据库,将数据散落在多个数据库中。
- 分表:将单张表拆分成多张表,将数据散落在多张表中。
三、详细了解
分库分表包括分库和分表两个部分,通常分为垂直分库、水平分库、垂直分表、水平分表四种方式。在一些复杂系统中又可以结合起来使用
3.1 垂直切分
垂直切分可以分为垂直分表和垂直分库
- 垂直切分的优点:
- 业务间数据解耦,不同业务的数据进行独立的维护、监控、扩展。
- 在高并发场景下,一定程度上缓解了数据库的压力。
- 垂直切分的缺点:
- 提升了开发的复杂度,由于业务的隔离性,很多表无法直接访问,必须通过接口方式聚合数据。
- 分布式事务管理难度增加。
- 数据库还是存在单表数据量过大的问题,并未根本上解决,需要配合水平切分。
3.1.1 垂直分表
垂直分表是指将一个表按照字段分成多表,每个表存储其中一部分字段。一般来说,某业务实体中的各个数据项的访问频次是不一样的,部分数据项可能是占用存储空间比较大的BLOB或是TEXT。当表数据量很大时,可以将表按字段切开,将热门字段、冷门字段分开放置在不同库中,这些库可以放在不同的存储设备上,避免IO争抢。垂直切分带来的性能提升主要集中在热门数据的操作效率上,而且磁盘争用情况减少。 它带来的提升是:
- 为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览互不影响
- 充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累。
通常我们按以下原则进行垂直拆分:
- 把不常用的字段单独放在一张表。
- 把text,blob等大字段拆分出来放在附表中。
- 经常组合查询的列放在一张表中。
例如:商品列表页面在展示商品列表时,只用展示商品名称、商品价格和商品产地。当用户选中商品查看详情是才需要展示商品描述,商品规格,商品库存等。列表页展示的产品字段相对于详情页展示的产品字段访问的频率较高。这时我们可以将产品表垂直拆分,分为产品基本信息表和产品详情表。
3.1.2 垂直分库
垂直分库是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念 是专库专用。 它带来的提升是:
- 解决业务层面的耦合,业务清晰 。
- 能对不同业务的数据进行分级管理、维护、监控、扩展等 。
- 高并发场景下,垂直分库一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈
例如:一个商城系统,我们可以将数据库分为订单库、产品库和积分库等
垂直分库通过专库专用来分摊压力提升性能,但是依然没有解决单表数据量过大的问题
3.2 水平切分
水平切分分为水平分库和水平分表
- 水平切分的优点:
- 解决高并发时单库数据量过大的问题,提升系统稳定性和负载能力。
- 业务系统改造的工作量不是很大。
- 水平切分的缺点:
- 跨分片的事务一致性难以保证。
- 跨库的join关联查询性能较差。
- 扩容的难度和维护量较大,(拆分成几千张子表想想都恐怖)。
3.2.1 水平分库
水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平分库了。它带来的提升是:
- 解决了单库大数据,高并发的性能瓶颈。
- 提高了系统的稳定性及可用性。
例如:订单库可以根据订单id按照一定的策略,保存到不同的数据库中。这些数据库可以部署到不同的服务器上。
经过水平切分的优化,往往能解决单库存储量及性能瓶颈。但由于同一个表被分配在不同的数据库,需要额外进行数据操作的路由工作,因此大大提升了系统复杂度。
3.2.2 水平分表
水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。库内的水平分表,解决了单一表数据量过大的问题,分出来的小表中只包含一部分数据,从而使得单个表的数据量变小,提高检索性能。它带来的提升是:
- 优化单一表数据量过大而产生的性能问题。
- 避免IO争抢并减少锁表的几率
例如:订单表可以按照订单id取模的方式,将一个大的订单表拆分为多个小的订单表。
四、分库分表带来的问题
分库分表能有效的缓解了单机和单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数的瓶颈,同时也带来 了一些问题。
4.1 事务一致性问题
由于分库分表把数据分布在不同库甚至不同服务器,不可避免会带来分布式事务问题。
4.2 跨节点关联查询
在没有分库前,我们查询时可以直接对库里的表进行关联查询。但垂直分库后表不在一个数据库,甚至不在一台服务器,无法进行关联查询。增加了开发的复杂度。
可将原关联查询分为两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据,最后将获得到的数据进行拼装。
4.3 跨节点分页、排序函数
跨节点多库进行查询时,limit分页、order by排序等问题,就变得比较复杂了。需要先在不同的分片节点中将数据 进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序。
4.4 主键避重
在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自增长将无用武之地,某个分区数据库生成的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重复问题。
4.5 公共表
实际的应用场景中,参数表、数据字典表等都是数据量较小,变动少,而且属于高频联合查询的依赖表。可以将这类表在每个数据库都保存一份,所有对公共表的更新操作都同时发送到所有分库执行。
五、分库分表工具
5.1 jar包类工具
- sharding jdbc
5.2 中间件类工具
- MyCat
六、总结
- 在日常生产中,当数据库成为系统瓶颈时,我们首先考虑的使用一些缓存、索引和优化表结构等来提升性能。当这些都无法满足业务量增长是我们再去考虑分库分表,首先要考虑垂直分表,将热点数据和访问量较少的数据拆分开。数据量继续增长时我们可以考虑垂直分库。将单节点压力分散到多个节点上。但是垂直拆分是无法解决单表数据量过大的问题的,这时候就要用到水平拆分了。
- 分库分表在提升数据库性能的同时也带来一系列的问题。增加了系统的复杂度。我们在平时的开发中需要做一定的取舍,分库分表不是必须的。我们可以根据当前的业务量和以后可能达到的业务量为切入点,保证mysql单表数据量在一千万以内即可。可以只垂直切分,也可只水平切分,也可以既垂直切分又水平切分。适合的就是最好的。