![]()
分库分表
背景描述
用户请求量大
单台服务器TPS、内存、IO都是有上限的,我们需要将请求分散到多个服务器去
单库数据量太大
单表数据量太大
单表数据量很大了之后,查询、插入、更新操作都会变慢,加字段、索引都会产生高负载。
解决方案:
-
2.1 水平分库
2.2 水平分表
垂直拆分
按照业务切割,如将电商的数据库分为:用户库、商品库、订单库
将一个表进行切分,如将商品表分为:商品名称表、商品属性表
分片规则
Range
时间:按照年、月、日去切分
地域:按照省或者市划分
大小:如从0到一千万的数据,每100万放一个表,0-100万、100-200万等。
Hash
用户ID取模
一致性Hash
实例:
用户表
但是这样的话如果是用户需要通过手机号登录,就要查询多个库。
订单表
划分方法1:按照用户id为分片键。这样每个用户的订单都在一个库。
划分方法2:我们可以采用空间换时间,将订单再按照商家ID进行分片,再存一份数据。
ShardingSphere
Sharding JDBC:被定位为轻量级Java框架,在Java的JDBC层提供额外服务,以jar包形式使用
Sharding Proxy:被定位为数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。
Sharding Sidecar:被定位为Kubernates或Mesos的云原生数据库代理,以DaemonSet的形式代理所有对数据库的访问。
Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar三者区别如下:
Sharding-JDBC
适用于任何基于Java的ORM框架,如:JPA、Hibernate、Mybatis、Spring JDBC Template等
适用于基于任何第三方的数据库连接池,如:DBCP、C3P0、BoneCP、Druid、HikariCP等
支持任意的实现JDBC规范的数据库,目前支持:MySQL、Oracle、SQLServer和PostgreSQL
Sharding-JDBC主要功能:
分库分表
读写分离
分片策略
分布式主键
分布式事务
标准的事务接口
XA强一致性事务
柔性事务
配置动态化
编排和治理
数据脱敏
可视化链路追踪
核心概念
表概念
真实表
逻辑表
数据分片后,同一类表结构的名称。例如b_order
数据节点
绑定表
指的是分片规则一致的关系表(主表、子表),例如订单表和订单明细表,均按照order_id进行分片,则此两个表可以设置为绑定表关系,绑定表之间的连接查询不会出现笛卡尔积关联,提升查询效率。
没配置绑定表的话,关联查询时需要4个sql
select * from order1 left join order_detail1 on ....
select * from order1 left join order_detail2 on ....
select * from order2 left join order_detail1 on ....
select * from order2 left join order_detail2 on ....
配置了绑定表后:
select * from order1 left join order_detail1 on ....
select * from order2 left join order_detail2 on ....
广播表
在使用中,有些表没必要做分片,例如字典表、省份信息等,因为他们数据量不大,而且这种表可能需要跟一些大表关联查询。广播表会在不同的数据节点上进行存储,存储的表结构和数据完全相同。
2. 分片算法
精确分片算法PreciseShardingAlgorithm
用于处理使用单一键作为分片键的=与in进行分片的场景
范围分片算法RangeShardingAlgorithm
复合分片算法ComplexKeysSharingAlgorithm
用于处理使用多键作为分片键进行分片的场景
Hint分片算法HintShardingAlgorithm
分片策略
分片策略包含分片键和分片算法。目前提供5种分片策略:
标准分片策略StandardShardingStrategy
PreciseShardingAlgorithm是必选的,RangeShardingAlgorithm是可选的。但是SQL中使用了范围操作,如果不配置RangeShardingAlgorithm会采用全库路由扫描,效率低。
复合分片策略ComplexShardingStrategy
行表达式分片策略InlineShardingStrategy
只支持单分片键。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发。如: t_user_$->{u_id % 8}表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
Hint分片策略HintShardingStrategy
不分片策略NoneShardingStrategy
不分片的策略。
SQL使用规范
路由至单数据节点时,目前MySQL数据库100%全兼容,其他数据库完善中
路由至多数据节点时,全面支持DQL、DML、DDL、DCL、TCL。支持分页、去重、排序、分组、聚合、关联查询。不支持CASE WHEN、HAVING、UNION。支持的最复杂的查询示例:
SELECT select_expr [, select_expr ...]
FROM table_reference [, table_reference ...]
[WHERE predicates]
[GROUP BY {col_name | position} [ASC | DESC], ...]
[ORDER BY {col_name | position} [ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
支持分页子查询,但是只支持嵌套一层的子查询。
当分片键处于运算表达式或函数中的SQL时,将采用全路由的形式获取结果。
不支持项:
INSERT INTO tbl_name (col1, col2, …) VALUES(1+2, ?, …) //VALUES语句不支持运算表达式
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name
WHERE col3 = ? //INSERT .. SELECT
SELECT COUNT(col1) as count_alias FROM tbl_name GROUP BY col1 HAVING
count_alias > ? //HAVING
SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2 //UNION
SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2 //UNION ALL
SELECT * FROM ds.tbl_name1 //包含schema
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name //同时使用普通聚合函数和DISTINCT
SELECT * FROM tbl_name WHERE to_date(create_time, ‘yyyy-mm-dd’) = ? //会导致全路由
分页查询:
SELECT * FROM b_order ORDER BY id LIMIT 1000000, 10
如果是分为2个库的情况,为了保证数据正确性,SQL会被改写为:
SELECT * FROM b_order ORDER BY id LIMIT 0, 1000010
并且两个库都会执行这条SQL,然后取10条数据出来。
流式处理+归并排序的方式避免内存的过量占用。
由于SQL改写不可避免的占用了额外的带宽,但并不会导致内存暴涨。与直觉不同,大多数人认为ShardingSphere会将1,000,010*2记录全部加载至内存,进而占用大量内存而导致内存溢出。
对落至单节点的查询进行进一步的优化
落至单分片查询的请求并不需要改写SQL也可以保证记录的正确性,因此在此种情况下,ShardingSphere并未进行SQL改写,从而达到节省带宽的目的。
其他功能
Inline行表达式
语法格式:
行表达式的使用非常直观,只需要在配置中使用${expression }或$->{ expression }标识行表达式即可。例如:
${begin..end} # ..表示范围区间
${[unit1, unit2, unit_x]} # [,,]表示枚举值
行表达式中如果出现多个${}或$->{}表达式,整个表达式结果会将每个子表达式结果进行笛卡尔(积)组合。例如,以下行表达式:
${['online', 'offline']}_table${1..3}
$->{['online', 'offline']}_table$->{1..3}
最终会解析成:
online_table1, online_table2, online_table3,
offline_table1, offline_table2, offline_table3
对于均匀分布的分片还是好写,那么不均匀的呢?
db0
├── b_order0
└── b_order1
db1
├── b_order2
├── b_order3
└── b_order4
可以写成这样:
db0.b_order${0..1},db1.b_order${2..4}