浅谈订单重构之 MySQL 分库分表实战篇

网友投稿 606 2023-06-03

浅谈订单重构之 MySQL 分库分表实战篇

浅谈订单重构之 MySQL 分库分表实战篇

一、背景

发布上篇文章浅谈订单重构之路之后,有很多小伙伴想知道,分库分表具体是如何实现的。那么这篇文章具体介绍下,分库分表实战。

二、目标

本文将完成如下目标:

* 分表数量: 256    分库数量: 4

* 以用户ID(user_id) 为数据库分片Key

* 最后测试订单创建,更新,删除, 单订单号查询,根据user_id查询列表操作。

架构图:

表结构如下:

CREATE TABLE `order_XXX` (   `order_id` bigint(20) unsigned NOT NULL,   `user_id` int(11) DEFAULT '0' COMMENT '订单id',   `status` int(11) DEFAULT '0' COMMENT '订单状态',   `booking_date` datetime DEFAULT NULL,   `create_time` datetime DEFAULT NULL,   `update_time` datetime DEFAULT NULL,   PRIMARY KEY (`order_id`),   KEY `idx_user_id` (`user_id`),   KEY `idx_bdate` (`booking_date`),   KEY `idx_ctime` (`create_time`),   KEY `idx_utime` (`update_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

注:  000<= XXX <= 255, 本文重点在于分库分表实践, 只保留具有代表性字段,其它场景可以在此基础上做改进。

全局唯一ID设计

要求:1.全局唯一 2:粗略有序 3:可反解出库编号

1bit + 39bit时间差 + 8bit机器号 + 8bit用户编号(库号) + 8bit自增序列

订单号组成项保留字段毫秒级时间差机器数用户编号(表编号)自增序列

所占字节(单位bit) 1 39 8 8 8

单机最大QPS: 256000 使用寿命: 17年

订单号生成规则说明详见 浅谈分布式唯一Id生成器之最佳实践

三、环境准备

1. 基本信息

2. 数据库环境准备

3. 建库 & 导入分表

* 在mysql master实例分别建库

172.30.1.21(   o rder_db_ 1) ,  172.30.1.22( order_db_2) ,

172.30.1.23( ord er_db_3) ,   172.30.1.24( order_db_4 )

*  依次导入建表SQL 命令为

mysql -uroot -pbytearch -h172.30.1.21 order_db_1

四、配置&实践

1. pom文件

2. 常量配置

3. yml 配置

4主4从数据库配置, 这里仅测试默认使用root用户密码,生产环境不建议使用root用户。

4. 分库分表策略

1). 根据order_id为shardKey分库分表策略

2). 根据user_id 为shardKey分库分表策略

5. dao层编写

1). OrderPartitionByIdDao

6. 单元测试

@SpringBootTest(classes = {Application.class}) @RunWith(SpringJUnit4ClassRunner.class) public class ShardingTest {     @Autowired     OrderPartitionByIdDao orderPartitionByIdDao;      @Autowired     OrderPartitionByUserIdDao orderPartitionByUserIdDao;      @Test     public void testCreateOrderRandom() {         for (int i = 0; i < 20; i++) {             int userId = ThreadLocalRandom.current().nextInt(1000,1000000);             OrderEntity orderEntity = new OrderEntity();             orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));             orderEntity.setStatus(1);             orderEntity.setUserId(userId);             orderEntity.setCreateTime(new Date());             orderEntity.setUpdateTime(new Date());             orderEntity.setBookingDate(new Date());             int ret = orderPartitionByIdDao.insertOrder(orderEntity);             Assert.assertEquals(1, ret);         }     }      @Test     public void testOrderAll() {         //insert         int userId = ThreadLocalRandom.current().nextInt(1000,1000000);         OrderEntity orderEntity = new OrderEntity();         orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));         orderEntity.setStatus(1);         orderEntity.setUserId(userId);         orderEntity.setCreateTime(new Date());         orderEntity.setUpdateTime(new Date());         orderEntity.setBookingDate(new Date());         int i = orderPartitionByIdDao.insertOrder(orderEntity);         Assert.assertEquals(1, i);          //get from master         OrderEntity orderInfo = orderPartitionByIdDao.getOrderByIdFromMaster(orderEntity.getOrderId());         Assert.assertNotNull(orderInfo);         Assert.assertEquals(orderInfo.getOrderId(), orderEntity.getOrderId());          //get from slave         OrderEntity slaveOrderInfo = orderPartitionByIdDao.getOrderById(orderEntity.getOrderId());         Assert.assertNotNull(slaveOrderInfo);         //update         OrderEntity updateEntity = new OrderEntity();         updateEntity.setOrderId(orderInfo.getOrderId());         updateEntity.setStatus(2);         updateEntity.setUpdateTime(new Date());         int affectRows = orderPartitionByIdDao.updateOrderByOrderId(updateEntity);         Assert.assertTrue( affectRows > 0);     }      @Test     public void testGetListByUserId() {         int userId = ThreadLocalRandom.current().nextInt(1000,1000000);         for (int i = 0; i < 5; i++) {             OrderEntity orderEntity = new OrderEntity();             orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));             orderEntity.setStatus(1);             orderEntity.setUserId(userId);             orderEntity.setCreateTime(new Date());             orderEntity.setUpdateTime(new Date());             orderEntity.setBookingDate(new Date());             orderPartitionByIdDao.insertOrder(orderEntity);         }         try {             //防止主从延迟引起的校验错误             Thread.sleep(1000);         } catch (InterruptedException e) {             e.printStackTrace();         }         List orderListByUserId = orderPartitionByUserIdDao.getOrderListByUserId(userId);         Assert.assertNotNull(orderListByUserId);         Assert.assertTrue(orderListByUserId.size() == 5);     } }

大功告成:

五、总结

本篇主要介绍Java版使用Mango框架实现Mysql分库分表实战,分库分表中间件也可以使用类似于ShardingJDBC,或者自研。

以上分库分表数量仅供演示参考,实际工作中分表数量、分库数量、是根据公司实际业务数据增长速度, 高峰期QPS,物理机器配置等等因素计算。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:面试官:Redis的事务满足原子性吗?
下一篇:Spring Boot + MyBatis + MySQL 实现读写分离!
相关文章