分布分表的模式
Client模式
Proxy模式
比较
分布分表的执行标准
再后来,阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。对此,有阿里的黄金铁律支撑,所以,很多人设计大数据存储时,多会以此为标准,进行分表操作。
那么,我对于分库分表的观点是,需要结合实际需求,不宜过度设计,在项目一开始不采用分库与分表设计,而是随着业务的增长,在无法继续优化的情况下,再考虑分库与分表提高系统的性能。对此,阿里巴巴《Java 开发手册》补充到:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。那么,回到一开始的问题,你觉得这个数值多少才合适呢?我的建议是,根据自身的机器的情况综合评估,如果心里没有标准,那么暂时以 500 万行作为一个统一的标准,相对而言算是一个比较折中的数值。
分布分表的架构方案
垂直切分:
-
垂直分库 将一个数据库根据业务功能模块切换成多个库。每个库的表中是不一样的。
-
垂直分表
将一个表拆分成【热门数据表】和【冷门数据表】,两个表数据不一样,减少了数据库的IO
- 垂直切分的优点和缺点分析:
- 拆分之后业务逻辑清晰、拆分规则明确。| 系统之间进行整合和扩展容易|
单表数据量大的问题在垂直拆分中无法解决。
水平拆分:
- 水平分库
当系统绝对并发量上来了,分表难以从根本上解决问题,并且还没有明显的业务归属进行抽取业务的情
况下使用水平分库方案。
- 水平分表
使用水平分表的场景:
当系统绝对并发量没有上来,只是单表的数据量太多,影响SQL效率,加重了CPU负担,以致于成为瓶 颈的情况下使用水平分表方案。
- 水平拆分的优点和缺点 -缺点:
1,切分后,数据是分散的,很难利用数据库的Join操作,跨库Join性能较差 拆分规则难以抽象 2,分片事务的一致性难以解决 3,数据多次拓展难度跟维护量极
小结:
- 垂直分库按照不同的业务模块进行拆分
- 水平分库按照一定的策略进行拆分,拆完的数据库结构一样
- 垂直分表:将表中数据按照冷热数据拆分,拆完之后表结构不一样,可以解决IO的问题,减少数据交互量。
- 水平分表:将表中数据按照策略记性拆分,拆完之后表结构一样,数据不一样。
业务演进
1,先进行垂直分库,做业务隔离。
2,还没有改变问题,根据业务属性,将一个表的冷热数据进行垂直分表,分成冷表+热表,表数据通过ID关联。把单表的数据量(大小,size,G维度)减下来,减少IO。
3,后面数据量持续增加,则将表进行水平拆分,将单表的数据量(数据条数,qty)减下来,增加表的查询速度。分表没有事务问题
4,后面数据量持续增加,则进行库的水平拆分。
分库分表之后的问题剖析:
- 水平分库之后的分布式事务问题: 可以通过Seata解决。【比如阿里的Seata分布式事务,XA两阶段事务,saga柔性事务。】
- 跨节点关联查询Join问题:
- 全局表:不经常表,数据量小,查询比较频繁的表,如:系统表。
- 采用【数据组装】方式
- ER分表:举例:将订单和其对应的交易流水放到同一个数据库中。避免跨节点的join查询问题。【相关联的数据放到同一个库中】,用于水平分库。
- 跨节点分页,排序,函数问题
排序:每个节点获取10条后再进行排序。
- 全局主键避免重复问题:单库单表可以主键ID自增长,但是水平分库场景不支持。
- SnowFlake(雪花算法)
- UUID/GUID
分库分表演进
1,每个服务都对数据库进行链接,一个节点挂掉影响全局
2,演进:故障自愈
3,数据迁移
数据库节点是3位,001是一个节点,002是个节点,通过路由的轮询算法找到对应的节点。
4,分页问题的说明: 如果要获取limit10,20 . 则两张表中的数据node1:要limit0,20; node2:要limit0,20. 然后合并之后再limit10,20.
电商系统分库分表的实战指导:使用ShardJDBC
引入坐标
配置yaml文件
#端口号
server.port=56000
#实例名称
spring.application.name= sharding_quick #表示后发现的bean会覆盖之前相同名称的bean spring.main.allow-bean-definition-overriding=true #该配置项就是指将带有下划线的表字段映射为驼峰格式的实体类属性 mybatis.configuration.map-underscore-to-camel-case=true #以下是分片规则配置
##定义数据源 spring.shardingsphere.datasource.names=m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSou
rce
spring.shardingsphere.datasource.m1.driver-class-name =com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://118.31.18.203:3306/orde
r_db?useUnicode=true
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#指定t_order表的数据分布情况,配置数据节点 </br>
spring.shardingsphere.sharding.tables.t_order.actualDataNodes=m1.t_order_$-> {1..2}
#指定t_order表的主键生成策略为SNOWFLAKE </br>
spring.shardingsphere.sharding.tables.t_order.keyGenerator.column=order_id spring.shardingsphere.sharding.tables.t_order.keyGenerator.type=SNOWFLAKE
#指定t_order表的分片策略,分片策略包括分片键和分片算法</br>
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingC olumn=order_id spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithm Expression=t_order_$->{order_id % 2+1}
#打开sql输出日志
spring.shardingsphere.props.sql.show=true
#日志级别
logging.level.root=info logging.level.org.springframework.web=info logging.level.com.itheima=debug logging.level.druid.sql=debug
写sql。
/**
* 订单mapper接口 */
1,写入的是逻辑表,而非具体的物理表。
2,主键自动生成,sql中无需插入主键。
@Mapper
public interface OrderMapper {
@Insert("insert into t_order(price,user_id,status) values(#{price},#
{userId},#{status})")
public int inserOrder(Order order);
}
- 查询接口
- 打印的日志:去db1和db2中获取数据。
原理
shardingJDBC 重写了对于数据库的connection链接。
水平分库
垂直分库
- 可以用shardingJDBC,但是推荐使用业务区分,不同的业务链接不同的数据库。没有必要使用shadingJdbc
读写分离
- 环境:使用mysql5.7版本进行测试。
- 具体sharding的配置文件
#端口号
server.port=561000
#实例名称
spring.application.name= sharding_order
#表示后发现的bean会覆盖之前相同名称的bean
spring.main.allow-bean-definition-overriding=true
#该配置项就是指将带有下划线的表字段映射为驼峰格式的实体类属性
mybatis.configuration.map-underscore-to-camel-case=true
#以下是分片规则配置
##定义数据源
spring.shardingsphere.datasource.names=m1,m2,s1,s2
#------------------------------------------------------------------------
#定义order_db_1数据源主库
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name =com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3340/order_db_1?useUnicode=true&createDatabaseIfNotExist=true&useSSL=false
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#定义order_db_1数据源从库
spring.shardingsphere.datasource.s1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s1.driver-class-name =com.mysql.jdbc.Driver
spring.shardingsphere.datasource.s1.url=jdbc:mysql://localhost:3341/order_db_1?useUnicode=true&createDatabaseIfNotExist=true&useSSL=false
spring.shardingsphere.datasource.s1.username=root
spring.shardingsphere.datasource.s1.password=123456
#读写分离,主库和从库绑定
spring.shardingsphere.sharding.master-slave-rules.ds1.masterDataSourceName=m1
spring.shardingsphere.sharding.master-slave-rules.ds1.slaveDataSourceNames=s1
#-------------------------------------------------------------------------------------
#定义order_db_2数据源主库
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name =com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3340/order_db_2?useUnicode=true&createDatabaseIfNotExist=true&useSSL=false
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
#定义order_db_2数据源从库
spring.shardingsphere.datasource.s2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s2.driver-class-name =com.mysql.jdbc.Driver
spring.shardingsphere.datasource.s2.url=jdbc:mysql://localhost:3341/order_db_2?useUnicode=true&createDatabaseIfNotExist=true&useSSL=false
spring.shardingsphere.datasource.s2.username=root
spring.shardingsphere.datasource.s2.password=123456
#读写分离,主库和从库绑定
spring.shardingsphere.sharding.master-slave-rules.ds2.masterDataSourceName=m2
spring.shardingsphere.sharding.master-slave-rules.ds2.slaveDataSourceNames=s2
#指定t_order表的数据分布情况,配置数据节点
#spring.shardingsphere.sharding.tables.t_order.actualDataNodes=m$->{1..2}.t_order_$->{1..2}
spring.shardingsphere.sharding.tables.t_order.actualDataNodes=ds$->{1..2}.t_order_$->{1..2}
#指定t_order表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.keyGenerator.column=order_id
spring.shardingsphere.sharding.tables.t_order.keyGenerator.type=SNOWFLAKE
#t_order分表策略,指定t_order表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithmExpression=t_order_$->{order_id % 2+1}
#分库策略,以user_id为分片键,分片策略为user_id%2+1,user_id为偶数操作m1数据源,否则操作m2。
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.algorithmExpression=ds$->{user_id % 2+1 }
#打开sql输出日志
spring.shardingsphere.props.sql.show=true
#日志级别
logging.level.root=info
logging.level.org.springframework.web=info
logging.level.com.itheima=debug
logging.level.druid.sql=debug
myCat
shardingJDBC vs myCat
附录:
mysql Docker主从同步教程
- docker -v 命令的介绍