1 Star 0 Fork 32

lynn_kong / sharding-datasource-demo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

完整分库分表流程demo


一、 技术


二、 模拟订单表分库分表迁移流程

1. 确认分库分表键

  • 可以统计查询订单表的where条件

    选用命中率高的, 减轻主要查询压力

  • 需要考虑分布式事务

    t_order表和t_order_item表, 保存一次订单,确保分库后order和order_item都放在同一个库中,
    这样就可以使用本地事务了。一般选用user_id, 选用order_id也可以, 本demo使用user_id

    注意事项,选用了user_id做分片键,则原先使用order_id查询的接口会遍历各个分库查询数据
       a. 如果数据量不是特别大,可以不处理
       b. 本demo将user_id和order_id映射关系保存到t_order_mapping表中,使用order_id查询时先查询一次user_id,然后带上user_id查询订单
       c. 基因id 处理,参考美团订单分库分表实现,定制order_id,中间嵌入user_id后几位数字,分片查询时根据user_id基因数字路由,这种改造工程比较大
       d. 多列分库分表,在将user_id做一次分库分表的同时,也将order_id做一次分库分表,存了两份数据,存储较大

2. 分片算法

  • range

    按时间或id范围划分,如[1-10000]放到1库,[10001-20000]放到2库
    优点:单表大小可控,天然水平扩展。
    缺点:无法解决集中写入瓶颈的问题。

  • hash

    hash取模 2^n
    分2(库) * 4 (表), 即分2个库,每个库4张表
    则 路由规则是 库号= user_id % 2, 表号= (user_id/2) % 4

    优点: 易扩展,解决集中写入瓶颈的问题
    缺点: 扩容麻烦,需要重新hash,(注意避免在同一张表的数据,重新hash到不同的表), 需要迁移数据

    (本demo采用该方法)


3. 确定容量,考虑扩容

  • 生产中一次性分够,够用好几年,不用考虑扩容

    分32(库) * 32 (表) , 分成32个库,每个库32张表, 共1024张表(也可以16*64)
    路由规则是 库号= user_id % 32, 表号= (user_id/32) % 32

  • 初始逻辑分库,实际上可以部署4台机器,每台机器8库, 每个库32表

    当表数据超过单机限制后,可以部署8台机器,每台机器4各库,每个库32表 最多可以部署1024台机器,每台机器1张表

    扩容时,仅仅是改一下配置,迁移数据就可以了, 原来在同一张表的数据,扩容之后,还在同一张表

    更具体的介绍看美团实现


4. 唯一id

  • uuid 字符串

    占用空间较多, 不能做到递增

  • 雪花算法

    可以做到递增,性能较好
    维护workId比较麻烦
    存在时钟回拨问题
    其他的实现一般都是基于雪花算法进行改造

  • 美团leaf唯一id实现

    比较复杂,暂不考虑

  • 百度uid-generator

    比较简单,解决workId维护问题,解决了时钟回拨问题,暂未提供workId重用实现

    本demo采用该实现,已实现复用workId

    ecp-uid整合了美团leaf、百度UidGenerator、原生snowflake 实现,可以参考,
    由于直接使用uid-generator足够简单,且有效使用,暂不采用ecp-uid


5. 单库表 迁移 到分库

参考美团双写实现

  • ① 原系统中将需要订单表的关联查询(join)去掉

    改成在应用中用代码处理join, 为后续分库分表做准备

  • ② 改造系统中订单id的实现,统一使用uid-generator生成的唯一id

  • ③ 数据库双写(先写单库,后写分库,事务以单库为主,读数据以单库为主 ),同步订单历史数据到分库,然后校验补偿分库数据

    a. 先写单库,后写分库,事务以单库为主,读数据以单库为主

    b. 同时,使用datax同步历史订单数据到分库

    c. 校验单库和分库数据,补偿数据到分库(插入或根据更新时间比较更新)

  • ④ 在③步骤数据补平后,数据仍双写(先写分库,后写单库,事务以分库为主,读数据以分库为主),同时校验补偿单库数据

    a. 先写分库,后写单库,事务以分库为主,读数据以分库为主
    (如果服务众多,可以切一小部分服务灰度尝试读写, 如果有问题,可以回退到③)

    b. 校验单库和分库数据,补偿数据到单库(插入或根据更新时间比较更新)

  • ⑤ 观察几天没问题后,下线单库订单表


6. 分库分表中间件

  • mycat

    基于数据库代理实现,存在单点问题

  • shardingsphere

    目前成为apache顶级项目, 从前景上说,采用该技术应是最好的

    • sharding-jdbc

      shardingsphere子项目, jdbc代理

      a. 不存在单点问题
      b. 可能会存在多份配置
      c. 升级比较麻烦,需要统一升级
      d. 只能应用于java语言

    • sharding-proxy

      shardingsphere子项目, 数据库代理

      a. 存在单点问题
      b. 兼容性问题
      c. 优点是没有语言限制

    目前demo采用sharding-jdbc实现为主,sharding-proxy运维为辅


三、本demo使用流程

1. 创建单库

create database db_lab;

2. 运行数据初始数据库脚本

db_lab_table.sql 用于创建数据表
db_lab_data.sql 用于创建测试数据,里面有3万订单(t_order)数据,和9万订单项(t_order_item)数据

3. 创建2个分库

create database db_lab_orders_0;
create database db_lab_orders_1;

4. 配置uid-generator数据库

  • 创建uid数据库

    create database uid;
  • 创建uid数据表

    DROP TABLE IF EXISTS WORKER_NODE;
    CREATE TABLE WORKER_NODE
    (
    ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
    HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
    PORT VARCHAR(64) NOT NULL COMMENT 'port',
    TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
    LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
    MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
    CREATED TIMESTAMP NOT NULL COMMENT 'created time',
    PRIMARY KEY(ID)
    )
     COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
  • 修改uid数据库配置用户名密码

5. 修改对应数据库配置,用户名密码等

6. 搭建sharding-proxy后, 复制proxy配置, 启动运行

使用shrding-proxy创建分库分表后的订单表

CREATE TABLE `t_order` (
  `order_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `address_id` bigint(20) NOT NULL,
  `status` varchar(50) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
CREATE TABLE `t_order_item` (
  `order_item_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `status` varchar(50) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

7. 启动single-datasource-lab项目,用于模拟原使用单库web应用

注意以下配置,用于分库分表迁移的切换

# 1 使用uid的初始下单  2 下单双写,以单库读写为主  3  下单双写, 以分库读写为主 4 下单只写在分库
transfrom.step=2

可以打包放到idea外部部署运行,高配机器可以忽略

8. 启动single-datasource-lab中的模拟客户端

模拟用户访问订单服务

9. 下载datax部署同步订单历史数据

本项目使用datax+sharding-jdbc同步订单历史数据,查看使用方法

  • 复制配置到本机datx/job目录下,并修改对应账号密码
  • 使用命令运行'job-order.json','job-order_item.json'同步历史数据到分库,其中'job-order_mapping.json'是用于同步order_id和User_id的关系数据

10. 数据库双写(step=2)时,启动validate-step2校验补偿订单

在single-datasource-lab项目运行时配置是'transfrom.step=2'时, 启动 validate-step2项目,用于校验补偿分库订单数据

11. 校验补平分库数据后,切换数据库双写,此时以分库事务为主,读数据以分库为主

修改single-datasource-lab配置'transfrom.step=3',并重启 观察是否有问题,如果有问题,则回退到step=2

12. 重启validate-step2项目,做分库最后一次校验

因为step2启动时,会读取当前最大的订单id, 进行校验,重启是为了防止遗漏

13. 启动 validate-step3项目,进行验证单库数据校验

这里主要是为了可回滚

14. 继续观察single-datasource-lab,确认没问题后,下线单库订单表

真实项目需持续观察几天, 没有问题,则修改配置为'transfrom.step=4', 重启下线单库订单
有问题,则回退到step2, 注意,回退后,validate-step2也需重新运行校验


四、常见问题

  1. no table route info
    根据路由规则得到的表名在数据库中不存在,检查配置
    可以开启sharding日志进行排查
  props:
    sql:
      show: true # 打印 SQL

五、参考资料

大众点评订单系统分库分表实践

空文件

简介

sharding-jdbc 完整分库分表流程demo 展开 收起
Java
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/lynn_kong/sharding-datasource-demo.git
git@gitee.com:lynn_kong/sharding-datasource-demo.git
lynn_kong
sharding-datasource-demo
sharding-datasource-demo
master

搜索帮助