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快速入门相关推荐

  1. 【高级】分表和分区的区别、分库分表介绍与区别

    分表和分区的区别: 一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看:mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这 ...

  2. Sharding-Sphere,Sharding-JDBC_分库分表介绍_Sharding-Sphere,Sharding-JDBC分布式_分库分表工作笔记002

    这个分库分表的介绍以前就已经,说过了,很简单这里再提一下. . 如果电商网站数据激增. 可以把单张表分开放,这样提高数据容纳能力,但是 这个并不是大数据的层面,还是从关系型数据库的层面来尽可能的解决问 ...

  3. 分区和分片的区别_Mysql分表和分区的区别、分库分表介绍与区别

    分表和分区的区别: 一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看: [root@BlackGhost test]# ls |grep user a ...

  4. 【分布式mysql分库分表中间件sharding】

    分布式mysql分库分表中间件,sharding领域的一站式解决方案.具备丰富.灵活的路由算法支持,能够方便DBA实现库的水平扩容和降低数据迁移成本.shark采用应用集成架构,放弃通用性,只为换取更 ...

  5. MySQL之分库分表

    文章目录 MySQL之分库分表 1.问题分析 2.分库分表介绍 3.分库分表拆分策略 4.垂直分库 4.1介绍 4.2特点 5.垂直分表 5.1介绍 5.2特点 6.水平分库 6.1介绍 6.2特点 ...

  6. mysql分库分表篇

    1. 分库分表介绍 1. 使用背景 当表的数量达到几百上千张表时, 众多的业务模块都访问这个数据库, 压力会非常的大, 考虑对其进行分库 当表的数据达到几千万级别, 在做很多操作的时候比较吃力, 考虑 ...

  7. 银行背景下分库分表技术选型

    业务持续增长带来的单表数据量过大,必然影响到数据库的读写性能,那到底要不要分库分表呢? 阿里巴巴P3C规范给出一个推荐: [推荐]单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表. 说 ...

  8. mysql+join+分库分表_MySQL分库分表篇

    传统项⽬结构 数据库性能瓶颈: 1.数据库连接数有限 MySQL数据库默认100个连接.单机最⼤1500连接. 2.表数据量 1)表数量多,成百上千 2)单表数据,千万级别 3)索引,命中率问题,索引 ...

  9. mycat配置访问oracle_MySQL:如何使用MyCAT实现分库分表?

    分库分表介绍 随着微服务这种架构的兴起,我们应用从一个完整的大的应用,切分为很多可以独立提供服务的小应用.每个应用都有独立的数据库. 数据的切分分为两种: 垂直切分:按照业务模块进行切分,将不同模块的 ...

最新文章

  1. python06-集合和序列
  2. 使用python来访问Hadoop HDFS存储实现文件的操作
  3. Scikit-Learn 与 TensorFlow 机器学习实用指南学习笔记 4 —— 数据探索与可视化、发现规律
  4. 3.2 矩阵和图像类型
  5. MySQL索引知识复习
  6. 程序员面试金典 - 面试题 16.21. 交换和(哈希set)
  7. vsftp 一键安装包
  8. 在建工地扬尘在线监控系统推荐_综执 | 针对工地扬尘、噪音监控系统问题对各在建工地开展集中约谈...
  9. Angular07 利用angular打造管理系统页面
  10. JavaScript:设置网站title
  11. Openlaye:学习笔记之事件
  12. JS定义const常量对象
  13. overleaf表格_Latex中插入表格
  14. android 主流分辨率是多少,Android程序开发设计主流屏幕分辨率介绍
  15. 一个bug看一天,写代码像cxk
  16. web网页设计实例作业 :美食坊网站设计——美食坊美食购物主题(15页) HTML+CSS+JavaScript
  17. 进行分词时,报错omw-1.4安装包未找到?
  18. 第一篇:详细介绍三次握手和四次挥手
  19. 梦幻西游 手游下载连接
  20. 论文投稿指南——中文核心期刊推荐(新闻事业)

热门文章

  1. 操作系统课设--扩展文件系统
  2. 2021-09-15
  3. sql注入-error、boolean、time-based and 宽字节
  4. 一次历史漏洞分析与复现的全部过程
  5. 分享SSRF漏洞的学习和利用
  6. 通过SEH 非inline hook
  7. MySQL AS:设置别名
  8. 将Calendar对象转换为日期时间字符串
  9. 105. 七夕祭【环形均分纸牌问题】
  10. Thymeleaf抽取公共页面片段