mysql分库分表一般有如下场景
创新互联公司专业为企业提供山丹网站建设、山丹做网站、山丹网站设计、山丹网站制作等企业网站建设、网页设计与制作、山丹企业网站模板建站服务,十余年山丹做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
其中1,2相对较容易实现,本文重点讲讲水平拆表和水平拆库,以及基于mybatis插件方式实现水平拆分方案落地。
在 《聊一聊扩展字段设计》 一文中有讲解到基于KV水平存储扩展字段方案,这就是非常典型的可以水平分表的场景。主表和kv表是一对N关系,随着主表数据量增长,KV表最大N倍线性增长。
这里我们以分KV表水平拆分为场景
对于kv扩展字段查询,只会根据id + key 或者 id 为条件的方式查询,所以这里我们可以按照id 分片即可
分512张表(实际场景具体分多少表还得根据字段增加的频次而定)
分表后表名为kv_000 ~ kv_511
id % 512 = 1 .... 分到 kv_001,
id % 512 = 2 .... 分到 kv_002
依次类推!
水平分表相对比较容易,后面会讲到基于mybatis插件实现方案
场景:以下我们基于博客文章表分库场景来分析
目标:
表结构如下(节选部分字段):
按照user_id sharding
假如分1024个库,按照user_id % 1024 hash
user_id % 1024 = 1 分到db_001库
user_id % 1024 = 2 分到db_002库
依次类推
目前是2个节点,假如后期达到瓶颈,我们可以增加至4个节点
最多可以增加只1024个节点,性能线性增长
对于水平分表/分库后,非shardingKey查询首先得考虑到
基于mybatis分库分表,一般常用的一种是基于spring AOP方式, 另外一种基于mybatis插件。其实两种方式思路差不多。
为了比较直观解决这个问题,我分别在Executor 和StatementHandler阶段2个拦截器
实现动态数据源获取接口
测试结果如下
由此可知,我们需要在Executor阶段 切换数据源
对于分库:
原始sql:
目标sql:
其中定义了三个注解
@useMaster 是否强制读主
@shardingBy 分片标识
@DB 定义逻辑表名 库名以及分片策略
1)编写entity
Insert
select
以上顺利实现mysql分库,同样的道理实现同时分库分表也很容易实现。
此插件具体实现方案已开源:
目录如下:
mysql分库分表,首先得找到瓶颈在哪里(IO or CPU),是分库还是分表,分多少?不能为了分库分表而拆分。
原则上是尽量先垂直拆分 后 水平拆分。
以上基于mybatis插件分库分表是一种实现思路,还有很多不完善的地方,
例如:
MyCat。MyCat是服务端的代理,使用MyCat实现了,整个分库分表和读写分离过程对,Java程序来说是完全透明的。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,属于Oracle旗下产品。
1
基本思想之什么是分库分表?
从字面上简单理解,就是把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上。
2
基本思想之为什么要分库分表?
数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;另外,由于无法进行分布式式部署,而一台服务器的资源(cpu、磁盘、内存、io等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。
3
分库分表的实施策略。
分库分表有垂直切分和水平切分两种。
3.1
何谓垂直切分,即将表按照功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立定义数据库workdb、商品数据库paydb、用户数据库userdb、日志数据库logdb等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
3.2
何谓水平切分,当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如userid散列,进行划分,然后存储到多个结构相同的表,和不同的库上。例如,我们的userdb中的用户数据表中,每一个表的数据量都很大,就可以把userdb切分为结构相同的多个userdb:part0db、part1db等,再将userdb上的用户数据表usertable,切分为很多usertable:usertable0、usertable1等,然后将这些表按照一定的规则存储到多个userdb上。
3.3
应该使用哪一种方式来实施数据库分库分表,这要看数据库中数据量的瓶颈所在,并综合项目的业务类型进行考虑。
如果数据库是因为表太多而造成海量数据,并且项目的各项业务逻辑划分清晰、低耦合,那么规则简单明了、容易实施的垂直切分必是首选。
而如果数据库中的表并不多,但单表的数据量很大、或数据热度很高,这种情况之下就应该选择水平切分,水平切分比垂直切分要复杂一些,它将原本逻辑上属于一体的数据进行了物理分割,除了在分割时要对分割的粒度做好评估,考虑数据平均和负载平均,后期也将对项目人员及应用程序产生额外的数据管理负担。
在现实项目中,往往是这两种情况兼而有之,这就需要做出权衡,甚至既需要垂直切分,又需要水平切分。我们的游戏项目便综合使用了垂直与水平切分,我们首先对数据库进行垂直切分,然后,再针对一部分表,通常是用户数据表,进行水平切分。
4
分库分表存在的问题。
4.1
事务问题。
在执行分库分表之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
4.2
跨库跨表的join问题。
在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上,这时,表的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。
4.3
额外的数据管理负担和数据运算压力。
额外的数据管理负担,最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算,例如,对于一个记录用户成绩的用户数据表usertable,业务要求查出成绩最好的100位,在进行分表之前,只需一个order
by语句就可以搞定,但是在进行分表之后,将需要n个order
by语句,分别查出每一个分表的前100名用户数据,然后再对这些数据进行合并计算,才能得出结果。
上述整理于互联网
一、分库分表的必要性
分库分表技术的使用,主要是数据库产生了瓶颈,如单库的并发访问或单表的查询都超出了阈值。对系统使用造成一定的影响,不得已而产生的技术。
通过分库分表技术来解决此类问题,但正因为使用此技术,会产生ACID一系列的问题,各类中间件解决此类问题各有各的优势。
提示:如场景无必要,千万不要使用分库分表。
二、分库分表的思路
1、垂直区分
垂直分库:从业务角度,一个库分成多个库,如把订单和用户信息分成两个库来存储。这样的好处就是可以微服务了。每块的业务单独部署,互不影响,通过接口去调用。
垂直分表:把大表分成多个小表,如热点数据和非热点数据分开,提高查询速度。
2、水平区分
水平分表:同一业务如数据量大了以后,根据一定的规则分为不同的表进行存储。
水平分库:如订单分成多个库存储,分解服务器压力。
以上一般来说,垂直分库和水平分表用的会多些。
三、分库分表的原理分析
分库分表常用的方案:Hash取模方案和range范围方案;
路由算法为最主要的算法,指得是把路由的Key按照指定的算法进行存放;
1、Hash取模方案
根据取余分配到不同的表里。要根据实际情况确认模的大小。此方案由于平均分配,不存在热点问题,但数据迁移很复杂。
2、Range范围方案
range根据范围进行划分,如日期,大小。此方案不存在数据迁移,但存在热点问题。
四、分库分表的技术选型
1、技术选型
解决方案主要分为4种:MySQL的分区技术、NoSql、NewSQL、MySQL的分库分表。
(1)mysql分区技术:把一张表存放在不同存储文件。由于无法负载,使用较少。
(2)NoSQL(如MongoDB):如是订单等比较重要数据,强关联关系,需约束一致性,不太适应。
(3)NewSql(具有NoSQL对海量数据的存储管理能力,还保持了传统数据库支持ACID和SQL等特性):如TiDB可满足需求。
(4)MySQL的分库分表:如使用mysql,此种方案为主流方式。
2、中间件
解决此类问题的中间件主要为:Proxy模式、Client模式。
(1)Proxy模式
(2)Client模式
把分库分表相关逻辑存放在客户端,一版客户端的应用会引用一个jar,然后再jar中处理SQL组合、数据库路由、执行结果合并等相关功能。
(3)中间件的比较
由于Client模式少了一层,运维方便,相对来说容易些。
五、分库分表的实践
根据容量(当前容量和增长量)评估分库或分表个数 - 选key(均匀)- 分表规则(hash或range等)- 执行(一般双写)- 扩容问题(尽量减少数据的移动)。
在这里我们选用中间件share-jdbc。
1、引入maven依赖
2、spring boot规则配置
行表达式标识符可以使用${...}或$-{...},但前者与Spring本身的属性文件占位符冲突,因此在Spring环境中使用行表达式标识符建议使用$-{...}。
3、创建DataSource
通过ShardingDataSourceFactory工厂和规则配置对象获取ShardingDataSource,ShardingDataSource实现自JDBC的标准接口DataSource。然后即可通过DataSource选择使用原生JDBC开发,或者使用JPA, MyBatis等ORM工具。