专栏首页Linyb极客之路springboot实战之mysql分库分表

springboot实战之mysql分库分表

什么是分库分表

把存于一个库的数据分散到多个库中,把存于一个表的数据分散到多个表中。如果说读写分离是为了分散数据库读写操作压力,分库分表就是为了分散存储压力

什么时候考虑切分

1、能不切分尽量不要切分

并不是所有表都需要进行切分,主要还是看数据的增长速度。切分后会在某种程度上提升业务的复杂度,数据库除了承载数据的存储和查询外,协助业务更好的实现需求也是其重要工作之一。

不到万不得已不用轻易使用分库分表这个大招,避免"过度设计"和"过早优化"。分库分表之前,不要为分而分,先尽力去做力所能及的事情,例如:升级硬件、升级网络、读写分离、索引优化等等。当数据量达到单表的瓶颈时候,再考虑分库分表。

2、数据量过大,正常运维影响业务访问

这里说的运维,指:

1)对数据库备份,如果单表太大,备份时需要大量的磁盘IO和网络IO。例如1T的数据,网络传输占50MB时候,需要20000秒才能传输完毕,整个过程的风险都是比较高的

2)对一个很大的表进行DDL修改时,MySQL会锁住全表,这个时间会很长,这段时间业务不能访问此表,影响很大。如果使用pt- online-schema-change,使用过程中会创建触发器和影子表,也需要很长的时间。在此操作过程中,都算为风险时间。将数据表拆分,总量减 少,有助于降低这个风险。

3)大表会经常访问与更新,就更有可能出现锁等待。将数据切分,用空间换时间,变相降低访问压力

3、随着业务发展,需要对某些字段垂直拆分

4、数据量快速增长

随着业务的快速发展,单表中的数据量会持续增长,当性能接近瓶颈时,就需要考虑水平切分,做分库分表了。此时一定要选择合适的切分规则,提前预估好数据容量

5、安全性和可用性

鸡蛋不要放在一个篮子里。在业务层面上垂直切分,将不相关的业务的数据库分隔,因为每个业务的数据量、访问量都不同,不能因为一个业务把数 据库搞挂而牵连到其他业务。利用水平切分,当一个数据库出现问题时,不会影响到100%的用户,每个库只承担业务的一部分数据,这样整体的可用性就能提高。

分库分表的方式

垂直切分

适用场景:如果是因为表的个数多而让数据多,可以按照功能划分,把联系密切的表切分出来放在同一个库中(分库);

垂直拆分是指,将一个属性较多,一行数据较大的表,将不同的属性拆分到不同的表中,以降低单库(表)大小,达到提升性能的目的的方法,垂直切分后,各个库(表)的特点是:

(1)每个库(表)的结构都不一样

(2)一般来说,每个库(表)的属性至少有一列交集,一般是主键

(3)所有库(表)的并集是全量数据

水平切分

适用场景:如果是因为表中的数据量过于庞大,则可以采用水平切分,按照某种约定好的规则将数据切分到不同的数据库中;

水平切分是指,以某个字段为依据(例如uid),按照一定规则(例如取模),将一个库(表)上的数据拆分到多个库(表)上,以降低单库(表)大小,达到提升性能的目的的方法,水平切分后,各个库(表)的特点是:

(1)每个库(表)的结构都一样

(2)每个库(表)的数据都不一样,没有交集

(3)所有库(表)的并集是全量数据

几种常用的分库分表的策略

1、HASH取模

假设有用户表user,将其分成3个表user0,user1,user2.路由规则是对3取模,当uid=1时,对应到的是user1,uid=2时,对应的是user2.

2、范围分片

从1-10000一个表,10001-20000一个表。

3、地理位置分片

华南区一个表,华北一个表。

4、时间分片

按月分片,按季度分片等等,可以做到冷热数据。

分库分表后引入的问题

分布式事务问题

如果我们做了垂直分库或者水平分库以后,就必然会涉及到跨库执行SQL的问题,这样就引发了互联网界的老大难问题-"分布式事务"。

那要如何解决这个问题呢?

  • 使用分布式事务中间件
  • 使用MySQL自带的针对跨库的事务一致性方案(XA),不过性能要比单库的慢10倍左右。
  • 能否避免掉跨库操作(比如将用户和商品放在同一个库中)

跨库join的问题

分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。

粗略的解决方法:

  • 全局表:基础数据,所有库都拷贝一份。
  • 字段冗余:这样有些字段就不用join去查询了。
  • 系统层组装:分别查询出所有,然后组装起来,较复杂。

横向扩容的问题

当我们使用HASH取模做分表的时候,针对数据量的递增,可能需要动态的增加表,此时就需要考虑因为reHash导致数据迁移的问题。

结果集合并、排序的问题

因为我们是将数据分散存储到不同的库、表里的,当我们查询指定数据列表时,数据来源于不同的子库或者子表,就必然会引发结果集合并、排序的问题。如果每次查询都需要排序、合并等操作,性能肯定会受非常大的影响。走缓存可能一条路!

分库分表实现方法

分库分表的实现主要有2种方案,第一种是代码层封装,比如使用Sharding-JDBC,本文的主要就是通过Sharding-JDBC来进行分库分表。第二种是通过中间件代理,比如使用mycat。

Sharding-JDBC分库分表

正常分库分表大部分都是先单库分表,再来分库分表,下面的例子也是遵循这个原则。另外例子的sharding-jdbc版本为

<sharding.boot.version>3.1.0</sharding.boot.version>

1、单库分表

a、表结构如下

DROP TABLE IF EXISTS `book_0`;

CREATE TABLE `book_0` (
  `id` bigint(20) NOT NULL,
  `book_name` varchar(200) DEFAULT NULL,
  `author` varchar(50) DEFAULT NULL,
  `description` varchar(500) DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `stock` int(6) DEFAULT NULL,
  `update_date` datetime DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Table structure for table `book_1` */

DROP TABLE IF EXISTS `book_1`;

CREATE TABLE `book_1` (
  `id` bigint(20) NOT NULL,
  `book_name` varchar(200) DEFAULT NULL,
  `author` varchar(50) DEFAULT NULL,
  `description` varchar(500) DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `stock` int(6) DEFAULT NULL,
  `update_date` datetime DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

b、pom.xml

<dependencies>
        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.dozermapper</groupId>
            <artifactId>dozer-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>


    </dependencies>

c、application.yml 配置

sharding:
  jdbc:
    datasource:
      names: master-ds
      # 数据源master-ds
      master-ds:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
        username: root
        password: 123456
    config:
      sharding:
        props:
          sql.show: true
        tables:
          book:  #逻辑表名
            key-generator-column-name: id  #主键
            actual-data-nodes: master-ds.book_${0..1}   #物理表数据节点,均匀分布
            table-strategy:  #分表策略
              inline: #行表达式
                sharding-column: id
                algorithm-expression: book_${id % 2}  #按id模运算分配
spring:
  main:
    allow-bean-definition-overriding: true

仅需上面3步就可以实现一个单库分表方案,更多详细配置可以查看如下链接

https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/configuration/config-yaml/

2、分库分表

例子分库分表表结构和单库分表的表结构是一样的,仅仅只是又多了一个库。

分库分表其配置文件内容如下

sharding:
  jdbc:
    datasource:
      #Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/boot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
        username: root
        password: 123456
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
        username: root
        password: 123456
    config:
      sharding:
        props:
          sql.show: true
        tables:
         book:
            key-generator-column-name: id  #主键
            actual-data-nodes: ds${0..1}.book_${0..1}    #数据节点,均匀分布
            database-strategy:   #分库策略
              inline: #行表达式
                sharding-column: id        #列名称,多个列以逗号分隔
                algorithm-expression: ds${id % 2}    #按id模运算分配
            table-strategy:  #分表策略
              inline: #行表达式
                sharding-column: stock
                algorithm-expression: book_${stock % 2}

spring:
  main:
    allow-bean-definition-overriding: true

同单库分表相比,通过sharding-jdbc进行分库分表,就配置文件内容不一样,其他sharding-jdbc底层都已经帮你实现好了。更多详细配置可以查看如下链接

https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/configuration/config-yaml/

总结

通过那个例子的整合,也许会给大家一个错觉,分库分表实现看起来也很简单啊,通过shariding-jdbc就可以快速实现,但事实上任何脱离业务的实现,都是纸上谈兵,上边的例子也只是一个简单的入门,具体分库分表方案,还得根据业务的复杂性来定。对于分库分表的一些介绍,大家也可以查看这篇文章浅谈高性能数据库集群 —— 分库分表

参考文档

一分钟掌握数据库垂直拆分

http://1t.click/azW4

数据库分库分表,何时分?怎样分?详细解读,一篇就够了

http://1t.click/azW9

MySQL分库分表原理

https://www.jianshu.com/p/7aec260ca1a2

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-split-table

本文分享自微信公众号 - Linyb极客之路(gh_c420b2cf6b47),作者:linyb极客之路

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 浅谈高性能数据库集群——分库分表

    最近学习了阿里资深技术专家李运华的架构设计关于分库分表的教程,颇有收获,总结一下。

    lyb-geek
  • MYSQL数据库数据拆分之分库分表总结

    单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到。

    lyb-geek
  • 如何优雅地玩转分库分表

    在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是,这些朋友所服务的公司业务量正在(...

    lyb-geek
  • MTDDL——美团点评分布式数据访问层中间件

    背景 2016年Q3季度初,在美团外卖上单2.0项目上线后,商家和商品数量急速增长,预估商品库的容量和写峰值QPS会很快遇到巨大压力。随之而来也会影响线上服务的...

    美团技术团队
  • 数据库表拆分的三种解决方案

    数据库拆分的方式有两种,前面文中已经聊过,即就是垂直拆分和水平拆分,分库分表是对数据库拆分的一种解决方案。根据分库分表方案中实施切片逻辑的层次不同,我们可以将数...

    用户4143945
  • 快速搭建ELK7.5版本的日志分析系统--搭建篇

    ELK是Elasticsearch、Logstash、Kibana的简称,这三者是核心套件,但并非全部

    用户6641876
  • OSPF路由协议之多区域配置

    在大型网络中,使用OSPF路由协议时经常会遇到以下问题: 1、在大型网络环境中,网络结构的变化是时常发生的,因此OSPF路由器就会经常运行SPF算法来重新计算路...

    小手冰凉
  • ELK从入门到还未精通(一)

    大家好,我是泥腿子安尼特。毫不夸张的说,最近这几个月与我相处最久的就是公司里面的ELK系统。

    老李秀
  • 带外架构——远程网络管理的整合

    摘要:本文回顾比较通用的监测、维护和恢复系统的方法,同时探索更有效的方法来满足日益紧迫的网络评估目标要求-减少成本同时提高业务水平和生产能力。  这篇文章还将...

    一见
  • 匹兹堡大学37岁华裔助理教授被枪杀,校方称受害者生前研究新冠病毒即将获得重大突破

    哥伦比亚广播公司(CBS)匹兹堡分支KDKA报导:5月2日,匹兹堡大学新冠病毒华人研究员、37岁的Bing Liu博士在宾夕法尼亚州罗斯镇(Ross Towns...

    新智元

扫码关注云+社区

领取腾讯云代金券