分库分表之历史表如何选择最佳分片路由规则

分库分表之历史表如何选择最佳分片路由规则

前言

背景

首先我相信很多人使用分表分库一定有这么一个情况,就是目前我们的系统有一张表可能会非常的庞大,然后希望通过分片技术将其进行水平拆分,但是如何拆分或者说如何拆分可以保证让目前的数据性能达到最优解,是一个很值得探讨的问题。

这边简单举一个例子,譬如我们的订单表,目前我们的订单表可能已经达到一定的数量级了比如百万或者千万级别了,可能光是简单的查询性能是很高的,但是新增订单可能就没这么乐观了,随着索引的增多新增的数目也会不断地变慢,不仅仅是查询一个维度迫使你选择分表。
基于这个简单的案例我们来延伸一下如何水平拆分成为目前最关键的一个问题。

按月份表

这边我们如果将订单表按月进行水平分表那么我们可以了解到哪怕是随着时间的推移,数据库的瓶颈也会慢慢的变成容量的瓶颈了而不仅仅是单表的上限了。

假设我们这边的订单是从2016年开始的,一直到2022年3月我们发现订单表可以分成近70张表,而且针对分片我们有个天然的优势就是按时间分片可以拥有顺序查询这一特性,所以说这么来分片将是一个比较完美的实现

但是随着系统的运行我们发现这种分片方式虽然看着比较完美,但是存在一个很严重的问题就是数据的分布不均匀,因为可能系统刚上线那段时间我们的系统使用量并不是那么多,导致了系统内部的订单数量不会那么的多,所以虽然我们把订单表按月来分了,但是之前的历史数据因为使用量的原因导致按月分表的每张表里面可能拥有的数据很少很少。

导致了分片在各个表中的数据分布极其不均匀。会造成很多不必要的跨表聚合问题,那么我们希望的方案是什么呢?

多维度分片

什么是多维度分片

  • 2018年及以前的数据我们将其归集到Order_History表中
  • 2019到2021年份的我们按年分表
  • 2022年开始的数据我们按月分表

通过上述纬度分片我们保证了各个分片表之间的数据都是区域平均,并且不会产生过多的跨分片聚合。

时间分片遇到的问题

随着系统的不断升级迭代,我们的系统也慢慢地拆分成了多个微服务,在各个微服务之间针对订单的调用我们将会传递一个订单id作为各个微服务之间交互的手段。

但是也是因为这种方式,让我们认识到分片如果按时间来分配那么微服务之间交互的id那么如果不是雪花id那么最好是带时间的或者说可以反解析出创建时间的。

但是因为订单历史原因导致2022年之前的订单全部采用的是guid那种无序的id,分表后我们将无法通过无序的guid来进行分片路由的指定,没办法用多字段分片辅助路由这个特性了。

针对这个问题我们该如何解决呢?

引入redis来辅助分片

虽然我们没办法通过历史订单id,guid来进行路由的辅助,但是我们可以借助第三方高速缓存来实现乱序id在分片环境下的辅助路由。

具体我们的实现原理是什么呢

  • 采用订单id进行辅助路由
  • 将历史数据全部导入到redis,redis只需要存储id和时间即可
  • 程序利用辅助路由来实现乱序guid进行实际分片辅助

直接进入实战

第一步安装依赖

# ShardingCore核心框架 版本6.4.2.4+
PM> Install-Package ShardingCore
# 数据库驱动这边选择的是mysql的社区驱动 efcore6最新版本即可
PM> Install-Package Pomelo.EntityFrameworkCore.MySql
# redis驱动
PM> Install-Package CSRedisCore

第二步添加订单表和数据库上下文

添加订单表

    public class Order
    {
        public string Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public OrderStatusEnum OrderStatus { get; set; }
        public DateTime? PayTime { get; set; }
        public DateTime CreateTime { get; set; }
    }

    public enum OrderStatusEnum
    {
        NoPay=1,
        Paid=1<<1
    }

添加数据库上下文和Order对象的数据库映射

    public class MyDbContext:AbstractShardingDbContext,IShardingTableDbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
            
        }

        public IRouteTail RouteTail { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Order>(builder =>
            {
                builder.HasKey(o => o.Id);
                builder.Property(o => o.Id).HasMaxLength(50).IsRequired().IsUnicode(false);
                builder.Property(o => o.Title).HasMaxLength(50).IsRequired();
                builder.Property(o => o.Description).HasMaxLength(255).IsRequired();
                builder.Property(o => o.OrderStatus).HasConversion<int>();
                builder.ToTable(nameof(Order));
            });
        }
    }

第三步添加按创建时间按月路由

    public class OrderRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
    {
        public override void Configure(EntityMetadataTableBuilder<Order> builder)
        {
            builder.ShardingProperty(o => o.CreateTime);
        }

        public override bool AutoCreateTableByTime()
        {
            return true;
        }

        public override DateTime GetBeginTime()
        {
            ret
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值