登录
首页 >  数据库 >  MySQL

初探分库分表

来源:SegmentFault

时间:2023-02-16 15:20:29 273浏览 收藏

哈喽!今天心血来潮给大家带来了《初探分库分表》,想必大家应该对数据库都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到MySQL、Java、架构设计,若是你正在学习数据库,千万别错过这篇文章~希望能帮助到你!

jRaOSneBgwoyLv3.jpg
本文来自:DanielLin07的博客《初探分库分表》

概述

在数据量较小的时候,数据多是以单表的形式存储。但随着业务量的扩大存储数据量的增加,单表的操作性能也会大大降低,影响正常的业务工作。
这时就需要考虑使用分库分表,一般而言,在单表数据量达到1000万左右(公司DBA建议)时,就可以考虑使用分库分表。

分库分表策略

垂直切分

用简单的话来说,垂直切分就是将一个表中涉及的多个字段切分到不同的表甚至是库中存储。如下图所示:

垂直切分

我们常用的 数据库三大范式 设计,其实也是一种垂直切分。
另一种常用的垂直切分,则是将热门访问字段与冷门访问字段进行切分,从而让数据库可以以更少的字段缓存更多的行,进而带来性能的提升。

水平切分

用简单的话来说,水平切分就是将一个表中存储的数据依照某种策略存储到不同的表上。如下图所示:

水平切分

Range

水平切分的第一种方式就是Range,即根据一定的范围进行分发。
如:根据时间范围,一个月的数据存储一张表,或者是根据用户ID这种自增序列,用户ID在000000至100000范围的存一张表,100001至200000范围的存一张表等。
根据Range分发的好处就是数据扩容时方便。缺点就是容易产生数据热点问题。

Hash

水平切分的第二种方式就是Hash,即通过一次哈希运算然后取余分表数量-1的方式确定数据要存的表的位置。
如:根据用户姓名进行Hash分发。用户姓名小明,计算hashcode,得到754703,预先确定分表数量为8,再取余7,得到3,即分发到索引为3的数据表上。
根据Hash分发的好处就是数据分发均匀,不会产生数据热点问题,但是扩容的时候非常不方便,还需要重新计算数据的哈希值。

MyBatis + ShardingJDBC 实践分库分表

ShardingJDBC是ShardingSphere的子项目,在Java的JDBC层提供的额外服务。具体可见ShardingPhere官方文档

数据库准备

现有用户信息需要存储,分别有五个字段:uid、name、mobile、credit_id、create_time。
现在的分库分表策略是:

  • 根据uid进行水平切分,uid最后一位为偶数的,分到sharding0db数据库,否则分到sharding1db数据库。
  • 在各数据库中,uid倒数第二位为偶数的,分到t_user_0表,否则分到t_user_1表。

所以每个表存储的字段都是一样的,其中一个表的数据库 Schema 脚本如下:

DROP TABLE IF EXISTS `t_user_0`;
CREATE TABLE `t_user_0`  (
  `uid` int(6) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `credit_id` varchar(16) NOT NULL,
  `create_time` datetime(0) NULL,
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

在sharding0db与sharding1db都建立了数据表后,结构如下图所示:

数据库结构

Maven依赖

本项目使用的是

org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-starter-weborg.mybatis.spring.bootmybatis-spring-boot-starter1.3.2org.projectlomboklomboktrueorg.apache.shardingspheresharding-jdbc-spring-boot-starter4.0.0-RC1mysqlmysql-connector-java

配置文件

spring:

  shardingsphere:
    datasource:
      names: sharding0db,sharding1db
      sharding0db:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/sharding0db?useUnicode=true&useSSL=false&useAffectedRows=true&characterEncoding=utf8
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password:
      sharding1db:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/sharding1db?useUnicode=true&useSSL=false&useAffectedRows=true&characterEncoding=utf8
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password:

    sharding:
      # 分库分表策略
      default-database-strategy:
        inline:
          # 分片的列
          sharding-column: uid
          # 分片的表达式,groovy语言,这里是对uid进行取余,如果为结果为0则分到sharding0db,结果为1则分到sharding1db
          algorithm-expression: sharding$->{uid % 2}db
      tables:
        t_user:
          actual-data-nodes: sharding$->{0..1}db.t_user_$->{0..1}
          table-strategy:
            inline:
              sharding-column: uid
              # 分片的表达式,对uid倒数第二位取余,如果为结果为0则分到t_user_0,结果为1则分到t_user_1
              algorithm-expression: t_user_$->{uid.intdiv(10) % 2}

# MyBatis配置
mybatis:
  # Mapper映射文件的位置
  mapper-locations: classpath:mapper/*.xml
  # 包下所有类的别名,配置别名为了在对象映射文件中接收参数类型和返回参数类型时省略包路径
  type-aliases-package: com.daniellin.demosharding.entity

编码

准备

@Mapper
@Repository
public interface UserDAO {

    /**
     * 获取所有用户
     *
     * @return 所有用户
     */
    List queryList();

    /**
     * 添加新用户
     *
     * @param user 新用户
     */
    void insert(User user);
}

准备


        INSERT INTO t_user(uid, name, mobile, credit_id, create_time)
        VALUES (#{uid},#{name},#{mobile},#{creditId},#{createTime})
    

准备

@Data
public class User {

    private Integer uid;

    private String name;

    private String mobile;

    private String creditId;

    private Date createTime;
}

编写单元测试插入数据,这里是通过随机生成100个用户的uid进行测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoShardingApplicationTests {

    @Autowired
    private UserDAO userDAO;

    @Test
    public void testInsert() {
        System.out.println(("----- sharding insert method test ------"));
        for (int i = 0; i 

运行结果

查看数据结果,可以看到数据已成功插入到指定的数据库表中。
最后一位为奇数,倒数第二位为偶数的,被插入到sharding1db.t_user_0:

sharding1db.t_user_0

最后一位为偶数,倒数第二位为奇数的,被插入到sharding0db.t_user_1:

sharding0db.t_user_1

参考资料

# 文章链接 作者
1 sharding:谁都能读懂的分库、分表、分区 骏马金龙
2 一次难得的分库分表实践 crossoverjie
3 advanced-java doocs

本篇关于《初探分库分表》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于数据库的相关知识,请关注golang学习网公众号!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>