背景描述
在系统搭建刚开始的时候我们一般使用的是单机数据库或主从架构,但是随着业务的发展,数据量越来越大。我们会遇到下面的一系列问题:
单台服务器TPS、内存、IO都是有上限的,我们需要将请求分散到多个服务器去
单个数据库处理能力有限;单库所在的服务器磁盘空间、IO都是有限制的
单表数据量很大了之后,查询、插入、更新操作都会变慢,加字段、索引都会产生高负载。
解决方案:
一般我们采用分库分表来解决以上的问题。而我们分库分表又可以分为垂直拆分和水平拆分。
1.1 垂直分库
按照业务切割,如将电商的数据库分为:用户库、商品库、订单库
1.2 垂直分表
将一个表进行切分,如将商品表分为:商品名称表、商品属性表
2.1 水平分库
将单张表的数据切分到多个服务器上去,每个服务器具有相同的库与表结构,只是数据不同。
2.2 水平分表
将单张表的数据切分到多个表里面去。消除单表数据量太大引发的性能低的问题。
分片规则
在我们分库分表的时候需要按照一定规则进行划分。我们一般划分的方法有如下几个:
Range
时间:按照年、月、日去切分
地域:按照省或者市划分
大小:如从0到一千万的数据,每100万放一个表,0-100万、100-200万等。
Hash
用户ID取模
一致性Hash
实例:
划分方法1:按用户id进行hash来分片。
但是这样的话如果是用户需要通过手机号登录,就要查询多个库。
划分方法2:此时可以存储一个用户ID和手机号的关系表。这个关系表用手机号来分片。
划分方法1:按照用户id为分片键。这样每个用户的订单都在一个库。
但是这样的话如果是商家端想查看买了自家公司产品的订单,就需要查询多个库。
划分方法2:我们可以采用空间换时间,将订单再按照商家ID进行分片,再存一份数据。
Apache ShardingSphere是一款开源的分布式数据库中间件组成的生态圈。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。
Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar三者区别如下:
Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架的使用。
Sharding-JDBC主要功能:
数据分片
分库分表
读写分离
分片策略
分布式主键
分布式事务
标准的事务接口
XA强一致性事务
柔性事务
数据库治理
配置动态化
编排和治理
数据脱敏
可视化链路追踪
核心概念
数据库中真实存在的物理表。例如b_order0、b_order1
数据分片后,同一类表结构的名称。例如b_order
在分片之后,由数据源和数据表组成。例如ds0.b_order1
指的是分片规则一致的关系表(主表、子表),例如订单表和订单明细表,均按照order_id进行分片,则此两个表可以设置为绑定表关系,绑定表之间的连接查询不会出现笛卡尔积关联,提升查询效率。
如order1、order2、order_detail1、order_detail2四个表
没配置绑定表的话,关联查询时需要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. 分片算法
由于分片算法和业务实现紧密相关,因此并未提供内置的分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让开发者自行实现分片算法。目前提供4种分片算法:
用于处理使用单一键作为分片键的=与in进行分片的场景
用于处理使用单一键作为分片键的BETWEEN AND、<、>、>=、<=进行分片的场景
用于处理使用多键作为分片键进行分片的场景
用于处理使用Hint行分片的场景。
分片策略包含分片键和分片算法。目前提供5种分片策略:
只支持单分片键,提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
PreciseShardingAlgorithm是必选的,RangeShardingAlgorithm是可选的。但是SQL中使用了范围操作,如果不配置RangeShardingAlgorithm会采用全库路由扫描,效率低。
支持多分片键。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
只支持单分片键。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发。如: t_user_$->{u_id % 8}表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
不分片的策略。
SQL使用规范
支持项:
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}]
不支持项:
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’) = ? //会导致全路由
分页查询:
查询偏移量大的分页会导致数据库获取数据性能低下,如下面这个SQL
SELECT * FROM b_order ORDER BY id LIMIT 1000000, 10
如果是分为2个库的情况,为了保证数据正确性,SQL会被改写为:
SELECT * FROM b_order ORDER BY id LIMIT 0, 1000010
并且两个库都会执行这条SQL,然后取10条数据出来。
ShardingSphere对分页查询进行了2个优化:
由于SQL改写不可避免的占用了额外的带宽,但并不会导致内存暴涨。与直觉不同,大多数人认为ShardingSphere会将1,000,010*2记录全部加载至内存,进而占用大量内存而导致内存溢出。
由于每个结果集的记录是有序的,因此ShardingSphere每次比较仅获取各个分片的当前结果集记录10条,驻留在内存中的记录仅为当前路由到的分片的结果集的当前游标指向,即只有20条。对于本身即有序的待排序对象,归并排序的时间复杂度仅为O(n),性能损耗很小。
落至单分片查询的请求并不需要改写SQL也可以保证记录的正确性,因此在此种情况下,ShardingSphere并未进行SQL改写,从而达到节省带宽的目的。
其他功能
InlineShardingStrategy:采用Inline行表达式进行分片的配置。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}
书山有路勤为径,学海无涯苦作舟
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/javammc/p/16257293.html
内容来源于网络,如有侵权,请联系作者删除!