分库分表介绍和Sharding-JDBC快速入门
1.分库分表介绍
垂直分表:可以把一个宽表的字段按访问频次、是否是大字段的原则拆分为多个表,这样既能使业务清晰,还能提升部分性能。拆分后,尽量从业务角度避免联查,否则性能方面将得不偿失。
比如我们可以将访问频次低的商品描述信息单独存放在一张表中,访问频次较高的商品基本信息单独放在一张表中。
垂直分库:可以把多个表按业务耦合松紧归类,分别存放在不同的库,这些库可以分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能,同时能提高整体架构的业务清晰度,不同的业务库可根据自身情况定制优化方案。但是它需要解决跨库带来的所有复杂问题。
比如我们可以有商品库,订单库,配置中心库等。
水平分库:可以把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能。它不仅需要解决跨库带来的所有复杂问题,还要解决数据路由的问题(数据路由问题后边介绍)。
它带来的提升是:
解决了单库大数据,高并发的性能瓶颈。
提高了系统的稳定性及可用性。
当一个应用再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平分库了,经过水平切分的优化,往往能解决单库存储量及性能瓶颈。但由于同一个表被分配在不同的数据库,需要额外进行数据操作的路由工作,因此大大提升了系统复杂度 。
水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,这样做能小幅提升性能,它仅仅作为水平分库的一个补充优化。
它带来的提升是: 优化单一表数据量过大而产生的性能问题 避免IO争抢并减少锁表的几率 库内的水平分表,解决了单一表数据量过大的问题,分出来的小表中只包含一部分数据,从而使得单个表的数据量变小,提高检索性能
2.水平分库分表示例
下面我们简单给个水平分库和水平分表的示例:
比如我们先这样有一个用户的操作日志表,由于数据库巨大,我们需要将日志按某个规则进行分库分表的拆分。
我们的日志主库为bc。
方法1:
我们首先进行水平分库。按用户的id的最后一位对2取余,如果是单数,则放到bc_0库中,如果是双数,则放到bc_1库中。
然后我们将日志中的create_time进行格式化为yyyy-MM-dd的格式,在bc_0和bc_1中按时间进行水平分表,具体如下图:
方法2:
我们按用户id将日志拆分到10个数据库中,分别为bc_0到bc_9。然后对id对10取余,水平分库到不同的数据库中,然后在每个数据库中,我们都设置100张表,分别为balance_log_0到balance_log_100。当用户分到对应的数据库中后,在对100取余,分到对应的表中。具体如下图:
3.分库分表带来的问题
分库分表能有效的缓解了单机和单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数的瓶颈,同时也带来了一些问题。
(1)事务一致性问题
由于分库分表把数据分布在不同库甚至不同服务器,不可避免会带来分布式事务问题。
(2)跨节点关联查询
在没有分库前,我们检索商品时可以通过以下SQL对店铺信息进行关联查询:
SELECT p.*,r.[地理区域名称],s.[店铺名称],s.[信誉] FROM [商品信息] p LEFT JOIN [地理区域] r ON p.[产地] = r.[地理区域编码] LEFT JOIN [店铺信息] s ON p.id = s.[所属店铺] WHERE...ORDER BY...LIMIT...
但垂直分库后[商品信息]和[店铺信息]不在一个数据库,甚至不在一台服务器,无法进行关联查询。
可将原关联查询分为两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据,最后将获得到的数据进行拼装。
(3)跨节点分页、排序函数
跨节点多库进行查询时,limit分页、order by排序等问题,就变得比较复杂了。需要先在不同的分片节点中将数据进行排
序并返回,然后将不同分片返回的结果集进行汇总和再次排序。
如进行水平分库后的商品库,按ID倒序排序分页,取第一页:
以上流程是取第一页的数据,性能影响不大,但由于商品信息的分布在各数据库的数据可能是随机的,如果是取第N页,
需要将所有节点前N页数据都取出来合并,再进行整体的排序,操作效率可想而知。所以请求页数越大,系统的性能也会
越差。
在使用Max、Min、Sum、Count之类的函数进行计算的时候,与排序分页同理,也需要先在每个分片上执行相应的函数,
然后将各个分片的结果集进行汇总和再次计算,最终将结果返回。
(4)主键避重
在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自增长将无用武之地,某个分区数据库生成
的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重复问题。
4.公共表
实际的应用场景中,参数表、数据字典表等都是数据量较小,变动少,而且属于高频联合查询的依赖表。例子中地理区域
表也属于此类型。
可以将这类表在每个数据库都保存一份,所有对公共表的更新操作都同时发送到所有分库执行。由于分库分表之后,数据
被分散在不同的数据库、服务器。因此,对数据的操作也就无法通过常规方式完成,并且它还带来了一系列的问题。好
在,这些问题不是所有都需要我们在应用层面上解决,市面上有很多中间件可供我们选择,其中Sharding-JDBC使用流行
度较高,我们来了解一下它。
5.Sharding-JDBC介绍
Sharding-JDBC是当当网研发的开源分布式数据库中间件,从 3.0 开始Sharding-JDBC被包含在 Sharding-Sphere中,之
后该项目进入进入Apache孵化器,4.0版本之后的版本为Apache版本。
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、ShardingProxy和
Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功
能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。
官方地址:https://shardingsphere.apache.org/document/current/cn/overview/
咱们目前只需关注Sharding-JDBC,它定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数
据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
Sharding-JDBC的核心功能为数据分片和读写分离,通过Sharding-JDBC,应用可以透明的使用jdbc访问已经分库分表、
读写分离的多个数据源,而不用关心数据源的数量以及数据如何分布。
适用于任何基于Java的ORM框架,如: Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
6.Sharding-JDBC快速入门
1.需求说明
人工创建两张表,t_order_1和t_order_2,这两张表是订单表拆分后的表,通过Sharding-Jdbc向订单表插入数据,按照一
定的分片规则,主键为偶数的进入t_order_1,另一部分数据进入t_order_2,通过Sharding-Jdbc 查询数据,根据 SQL语
句的内容从t_order_1或t_order_2查询数据 。
2.创建数据库
--创建订单库order_db
CREATE DATABASE `order_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
--在order_db中创建t_order_1、t_order_2表
DROP TABLE IF EXISTS `t_order_1`;
CREATE TABLE `t_order_1` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `t_order_2`;
CREATE TABLE `t_order_2` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
3.引入maven依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
<!-- sharding-jdbc和SpringBoot整合的Jar包 --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId></dependency>
4.分片规则配置
分片规则配置是sharding-jdbc进行对分库分表操作的重要依据,配置内容包括:数据源、主键生成策略、分片策略等。
在application.properties中配置 :
server.port=56081
spring.application.name = sharding-jdbc-simple-demo
server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true
#sharding-jdbc分片规则配置
#数据源
spring.shardingsphere.datasource.names = m1
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:3306/order_db?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = root
# 指定t_order表的数据分布情况,配置数据节点 m1.t_order_1,m1.t_order_2
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}
# 指定t_order表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
# 指定t_order表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show = true
swagger.enable = true
logging.level.root = info
logging.level.org.springframework.web = info
logging.level.com.itheima.dbsharding = debug
logging.level.druid.sql = debug
(1)首先定义数据源m1,并对m1进行实际的参数配置。
(2)指定t_order表的数据分布情况,他分布在m1.t_order_1,m1.t_order_2
(3)指定t_order表的主键生成策略为SNOWFLAKE,SNOWFLAKE是一种分布式自增算法,保证id全局唯一
(4)定义t_order分片策略,order_id为偶数的数据落在t_order_1,为奇数的落在t_order_2,分表策略的表达式为
t_order_$->{order_id % 2 + 1}
5.数据操作
@Mapper
@Component
public interface OrderDao {
/*** 插入订单* @param price* @param userId* @param status* @return*/@Insert("insert into t_order(price,user_id,status)values(#{price},#{userId},#{status})")int insertOrder(@Param("price")BigDecimal price,@Param("userId")Long userId,@Param("status")String status);
/*** 根据id列表查询订单* @param orderIds* @return*/@Select("<script>" +"select" +" * " +" from t_order t " +" where t.order_id in " +" <foreach collection='orderIds' open='(' separator=',' close=')' item='id'>" +" #{id} " +" </foreach>" +"</script>")List<Map> selectOrderbyIds(@Param("orderIds") List<Long> orderIds);
}
6.测试
@Testpublic void testInsertOrder(){for(int i=1;i<20;i++){orderDao.insertOrder(new BigDecimal(i),1L,"SUCCESS");}}
通过日志可以发现order_id为奇数的被插入到t_order_2表,为偶数的被插入到t_order_1表,达到预期目标。
@Testpublic void testSelectOrderbyIds(){List<Long> ids = new ArrayList<>();ids.add(373897739357913088L);ids.add(373897037306920961L);
List<Map> maps = orderDao.selectOrderbyIds(ids);System.out.println(maps);}
通过日志可以发现,根据传入order_id的奇偶不同,sharding-jdbc分别去不同的表检索数据,达到预期目标。
7.流程分析
通过日志分析,Sharding-JDBC在拿到用户要执行的sql之后干了哪些事儿:
(1)解析sql,获取片键值,在本例中是order_id
(2)Sharding-JDBC通过规则配置 t_order_$->{order_id % 2 + 1},知道了当order_id为偶数时,应该往t_order_1表插数
据,为奇数时,往t_order_2插数据。
(3)于是Sharding-JDBC根据order_id的值改写sql语句,改写后的SQL语句是真实所要执行的SQL语句。
(4)执行改写后的真实sql语句
(5)将所有真正执行sql的结果进行汇总合并,返回。
8.SpringBoot的另外3种集成方式
(1)yml配置
server:port: 56081servlet:context-path: /sharding-jdbc-simple-demo
spring:application:name: sharding-jdbc-simple-demohttp:encoding:enabled: truecharset: utf-8force: truemain:allow-bean-definition-overriding: trueshardingsphere:datasource:names: m1m1:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/order_db?useUnicode=trueusername: rootpassword: rootsharding:tables:t_order:actualDataNodes: m1.t_order_$->{1..2}tableStrategy:inline:shardingColumn: order_idalgorithmExpression: t_order_$->{order_id % 2 + 1}keyGenerator:type: SNOWFLAKEcolumn: order_idprops:sql:show: true
mybatis:configuration:map-underscore-to-camel-case: true
swagger:enable: true
logging:level:root: infoorg.springframework.web: infocom.itheima.dbsharding: debugdruid.sql: debug
(2)java配置
@Configuration
public class ShardingJdbcConfig {
//配置分片规则// 定义数据源Map<String, DataSource> createDataSourceMap() {DruidDataSource dataSource1 = new DruidDataSource();dataSource1.setDriverClassName("com.mysql.jdbc.Driver");dataSource1.setUrl("jdbc:mysql://localhost:3306/order_db?useUnicode=true");dataSource1.setUsername("root");dataSource1.setPassword("root");Map<String, DataSource> result = new HashMap<>();result.put("m1", dataSource1);return result;}// 定义主键生成策略private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE","order_id");return result;}
// 定义t_order表的分片策略TableRuleConfiguration getOrderTableRuleConfiguration() {TableRuleConfiguration result = new TableRuleConfiguration("t_order","m1.t_order_$->{1..2}");result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_$->{order_id % 2 + 1}"));result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
return result;}// 定义sharding-Jdbc数据源@BeanDataSource getShardingDataSource() throws SQLException {ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());//spring.shardingsphere.props.sql.show = trueProperties properties = new Properties();properties.put("sql.show","true");return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig,properties);}
}
由于采用了配置类所以需要屏蔽原来application.properties文件中spring.shardingsphere开头的配置信息。
还需要在SpringBoot启动类中屏蔽使用spring.shardingsphere配置项的类:
@SpringBootApplication(exclude = {SpringBootConfiguration.class})
public class ShardingJdbcSimpleDemoBootstrap {....}
(3)xml配置
<?xml version="1.0" encoding="UTF‐8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sharding="http://shardingsphere.apache.org/schema/shardingsphere/sharding"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring‐beans.xsd
http://shardingsphere.apache.org/schema/shardingsphere/sharding
http://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring‐context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring‐tx.xsd">
<context:annotation‐config />
<!‐‐定义多个数据源‐‐>
<bean id="m1" class="com.alibaba.druid.pool.DruidDataSource" destroy‐method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/order_db_1?useUnicode=true" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!‐‐定义分库策略‐‐>
<sharding:inline‐strategy id="tableShardingStrategy" sharding‐column="order_id" algorithm‐
expression="t_order_$‐>{order_id % 2 + 1}" />
<!‐‐定义主键生成策略‐‐>
<sharding:key‐generator id="orderKeyGenerator" type="SNOWFLAKE" column="order_id" />
<!‐‐定义sharding‐Jdbc数据源‐‐>
<sharding:data‐source id="shardingDataSource">
<sharding:sharding‐rule data‐source‐names="m1">
<sharding:table‐rules>
<sharding:table‐rule logic‐table="t_order" table‐strategy‐
ref="tableShardingStrategy" key‐generator‐ref="orderKeyGenerator" />
</sharding:table‐rules>
</sharding:sharding‐rule>
</sharding:data‐source>
</beans>
分库分表介绍和Sharding-JDBC快速入门相关推荐
- 【高级】分表和分区的区别、分库分表介绍与区别
分表和分区的区别: 一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看:mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这 ...
- Sharding-Sphere,Sharding-JDBC_分库分表介绍_Sharding-Sphere,Sharding-JDBC分布式_分库分表工作笔记002
这个分库分表的介绍以前就已经,说过了,很简单这里再提一下. . 如果电商网站数据激增. 可以把单张表分开放,这样提高数据容纳能力,但是 这个并不是大数据的层面,还是从关系型数据库的层面来尽可能的解决问 ...
- 分区和分片的区别_Mysql分表和分区的区别、分库分表介绍与区别
分表和分区的区别: 一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看: [root@BlackGhost test]# ls |grep user a ...
- 【分布式mysql分库分表中间件sharding】
分布式mysql分库分表中间件,sharding领域的一站式解决方案.具备丰富.灵活的路由算法支持,能够方便DBA实现库的水平扩容和降低数据迁移成本.shark采用应用集成架构,放弃通用性,只为换取更 ...
- MySQL之分库分表
文章目录 MySQL之分库分表 1.问题分析 2.分库分表介绍 3.分库分表拆分策略 4.垂直分库 4.1介绍 4.2特点 5.垂直分表 5.1介绍 5.2特点 6.水平分库 6.1介绍 6.2特点 ...
- mysql分库分表篇
1. 分库分表介绍 1. 使用背景 当表的数量达到几百上千张表时, 众多的业务模块都访问这个数据库, 压力会非常的大, 考虑对其进行分库 当表的数据达到几千万级别, 在做很多操作的时候比较吃力, 考虑 ...
- 银行背景下分库分表技术选型
业务持续增长带来的单表数据量过大,必然影响到数据库的读写性能,那到底要不要分库分表呢? 阿里巴巴P3C规范给出一个推荐: [推荐]单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表. 说 ...
- mysql+join+分库分表_MySQL分库分表篇
传统项⽬结构 数据库性能瓶颈: 1.数据库连接数有限 MySQL数据库默认100个连接.单机最⼤1500连接. 2.表数据量 1)表数量多,成百上千 2)单表数据,千万级别 3)索引,命中率问题,索引 ...
- mycat配置访问oracle_MySQL:如何使用MyCAT实现分库分表?
分库分表介绍 随着微服务这种架构的兴起,我们应用从一个完整的大的应用,切分为很多可以独立提供服务的小应用.每个应用都有独立的数据库. 数据的切分分为两种: 垂直切分:按照业务模块进行切分,将不同模块的 ...
最新文章
- python06-集合和序列
- 使用python来访问Hadoop HDFS存储实现文件的操作
- Scikit-Learn 与 TensorFlow 机器学习实用指南学习笔记 4 —— 数据探索与可视化、发现规律
- 3.2 矩阵和图像类型
- MySQL索引知识复习
- 程序员面试金典 - 面试题 16.21. 交换和(哈希set)
- vsftp 一键安装包
- 在建工地扬尘在线监控系统推荐_综执 | 针对工地扬尘、噪音监控系统问题对各在建工地开展集中约谈...
- Angular07 利用angular打造管理系统页面
- JavaScript:设置网站title
- Openlaye:学习笔记之事件
- JS定义const常量对象
- overleaf表格_Latex中插入表格
- android 主流分辨率是多少,Android程序开发设计主流屏幕分辨率介绍
- 一个bug看一天,写代码像cxk
- web网页设计实例作业 :美食坊网站设计——美食坊美食购物主题(15页) HTML+CSS+JavaScript
- 进行分词时,报错omw-1.4安装包未找到?
- 第一篇:详细介绍三次握手和四次挥手
- 梦幻西游 手游下载连接
- 论文投稿指南——中文核心期刊推荐(新闻事业)