• .NET ORM 分表分库【到底】怎么做?


    安装NUGET安装

    只需要引用一个dll 即可 : SqlSugarCore 5.0.4.3-preview06

    注意:需要升级5.0.4.3-preview06,nuget 勾选允许预览版本

    一、分表使用场景

     (1).NET可扩展架构设计,比如一个ERP用5年不卡,到了10就卡了因为数据太多了,这个时候很多人都是备份然后清空数据

     (2).NET 数据量太多 ,例如每天都有 几十上百万的数据进入库,如果不分表后面查询将会非常缓慢

       (3)   .NET性能瓶颈 ,数据库现有数据超过1个亿,很多情况下索引会莫名失效,性能大大下降

    二、 内置分表使用

    .NET 自带分表支持按年、按季、按月、按周、按日进行分表

    2.1 定义实体

    我们定义一个实体,主键不能用自增或者int ,设为long或者guid都可以,我例子就用自带的雪花ID实现分表

    [SplitTable(SplitType.Year)]//按年分表 (自带分表支持 年、季、月、周、日)
    [SugarTable("SplitTestTable_{year}{month}{day}")]//生成表名格式 3个变量必须要有
     public class SplitTestTable
     {
         [SugarColumn(IsPrimaryKey =true)]
         public long Id { get; set; }
     
         public string Name { get; set; }
         
         [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
         public DateTime CreateTime { get; set; }
     } 

    2.2 同步表和结构

    假如分了20张表,实体类发生变更,那么 20张表可以自动同步结构,与实体一致

    db.CodeFirst.SplitTables().InitTables<SplitTestTable>(); //程序启动时加这一行,如果一张表没有会初始化一张

    2.3 查询分表

    (1)时间过滤

      通过开始时间和结束时间自动生成CreateTime的过滤并且找到对应时间的表

    var list=db.Queryable<OrderSpliteTest>().SplitTable(DateTime.Now.Date.AddYears(-1),DateTime.Now).ToPageList(1,2); 

    (2) 选择具体表

    如果下面是按年分表 Take(3)  表示 只查 最近3年的 分表数据 

    var list=db.Queryable<OrderSpliteTest>()
                .Where(it=>it.Pk==Guid.NewGuid())
                .SplitTable(tabs => tabs.Take(3))//近3张,也可以表达式选择
                .ToList();

    (3)精准定位一张表

    根据分表字段的值可以精准的定位到具体是哪一个分表,比Take(N)性能要高出很多

    var name=Db.SplitHelper<SplitTestTable>().GetTableName(data.CreateTime);//根据时间获取表名
    Db.Queryable<SplitTestTable>().Where(it => it.Id==data.Id).SplitTable(tas => tas.InTableNames(name)).ToList();

    (4) 表达式定位哪几张表

    Db.Queryable<SplitTestTable>()
                   .Where(it => it.Id==data.Id)
                    .SplitTable(tas => tas.Where(y=>y.TableName.Contains("2019")))//表名包含2019的表
                    .ToList();

    (5)分表Join正常表

    //分表使用联表查询
    var list=db.Queryable<Order>() // Order是分表
    .SplitTable(tabs=>tabs.Take(3)) 
    .LeftJoin<Custom>((o,c)=>o.CustomId==c.Id)//Custom正常表
    .Select((o,c)=>new { name=o.Name,cname=c.Name }).ToPageList(1,2); 

    (6)分表JOIN分表

    var rightQuery=db.Queryable<Custom>().SplitTable(tabs=>tabs.Take(3)) ;
    var list=db.Queryable<Order>().SplitTable(tabs=>tabs.Take(3)) 
    .LeftJoin<Custom>(rightQuery,(o,c)=>o.CustomId==c.Id) // Join  rightQuery
    .Select((o,c)=>new { name=o.Name,cname=c.Name }).ToPageList(1,2); 

    (7)性能优化

    条件尽可能写在SplitTable前面,因为这样会先过滤在合并

    2.3 插入 

    因为我们用的是Long所以采用雪花ID插入(guid也可以禁止使用自增列), 实体结构看上面 3.1

    注意:.SplitTable不要漏掉了

    var data = new SplitTestTable()
    {
          CreateTime=Convert.ToDateTime("2019-12-1"),
          Name="jack"
     };
     db.Insertable(data).SplitTable().ExecuteReturnSnowflakeId();//插入并返回雪花ID 
     
    // sql 如下
    //INSERT INTO [SplitTestTable_20190101] --如果表不存在会自动建表
    //           ([Id],[Name],[CreateTime])
    //     VALUES
    //           (@Id,@Name,@CreateTime)

    批量插入 因为我们是根据CreateTime进行的分表,生成的SQL语句如下:

    var datas = new List<SplitTestTable>(){
    new SplitTestTable(){CreateTime=Convert.ToDateTime("2019-12-1"),Name="jack"} ,
    new SplitTestTable(){CreateTime=Convert.ToDateTime("2022-02-1"),Name="jack"},
    new SplitTestTable(){CreateTime=Convert.ToDateTime("2020-02-1"),Name="jack"},
    new SplitTestTable(){CreateTime=Convert.ToDateTime("2021-12-1"),Name="jack"}
    };
    
    db.Insertable(datas).SplitTable().ExecuteReturnSnowflakeIdList();//插入返回雪花ID集合

     执行完生成的表

    生成的Sql: 

    自动识别4条记录,分别插入4个不同的表中  

    2.4 删除数据 

    (1)最近3张表都执行一遍删除

    db.Deleteable<SplitTestTable>().In(id).SplitTable(tas=>tas.Take(3)).ExecuteCommand();

    (2)精准删除     

     相对于上面的操作性能更高,可以精准找到具体表

    var deldata = new SplitTestTable()
                {
                    Id = id,
                    CreateTime = DateTime.Now
                };
     var tableName = db.SplitHelper(deldata).GetTableNames();
     db.Deleteable<SplitTestTable>().Where(deldata).SplitTable(tas=>tas.InTableNames(tableName)).ExecuteCommand();
     //DELETE FROM [SplitTestTable_20210101] WHERE [Id] IN (1454676863531089920)

    2.5 更新数据    

    更新的用法基本上和删除一样

    //更新近3张表
    db.Updateable(deldata).SplitTable(tas=>tas.Take(3)).ExecuteCommand();
    
    //精准找到表名并且更新数据
    var tableName = db.SplitHelper(deldata).GetTableNames();
    db.Updateable(deldata).SplitTable(tas => tas.InTableNames(tableName)).ExecuteCommand();
    
    //通过表达式过滤出要更新的表
    db.Updateable(deldata).SplitTable(tas => tas.Where(y=>y.TableName.Contains("_2019"))).ExecuteCommand();
     

    三、 自定义分表

    上面的分表功能是我们自带集成的,比如我想实现自定义的分表我该如何实现呢?

    3.1 按首字母拼音分表

     我们就写个按24个字母进行分表的小例子,来学习一下如何自定义分表

    3.2  创建分表类

    我们新建一个类继承成ISplitTableService 接口 

     public interface ISplitTableService
     {
            //获取表名用于 SplitTable tas 筛选
            List<SplitTableInfo> GetAllTables(ISqlSugarClient db,EntityInfo EntityInfo,List<DbTableInfo> tableInfos);
            //获取默认表名
            string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo);
            string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo, SplitType type);
            //根据分表字段获取表名
            string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue);
            //获取分表字段的值
            object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue);
     }

    3.3  使用自定义分表

    创建一个WordSplitService.cs继承ISplitTableService

    用例下载: USplit.zip

    //配置自定义分表
    db.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService =new WordSplitService();
    
    //插入数据
    db.Insertable(new WordTestTable(){CreateTime=DateTime.Now,Name="BC"}).SplitTable().ExecuteReturnSnowflakeId();
    db.Insertable(new WordTestTable(){CreateTime=DateTime.Now,Name="AC"}).SplitTable().ExecuteReturnSnowflakeId();
    db.Insertable(new WordTestTable(){CreateTime=DateTime.Now,Name="ZBZ"}).SplitTable().ExecuteReturnSnowflakeId();  

     执行完数据库就多了3张表,因为是按首字母分的表 ,插入了3条记录自动创建了3张表,插入生成的SQL:

    INSERT INTO [WordTestTable_FirstB]
               ([Id],[Name],[CreateTime])
         VALUES
               (@Id,@Name,@CreateTime) ;
    INSERT INTO [WordTestTable_FirstA]
               ([Id],[Name],[CreateTime])
         VALUES
               (@Id,@Name,@CreateTime) ;
    INSERT INTO [WordTestTable_FirstZ]
               ([Id],[Name],[CreateTime])
         VALUES
               (@Id,@Name,@CreateTime) ;

    查询分表

    //查询字母A开头的分
    var listall = db.Queryable<WordTestTable>().Where(it => it.Name == "all").SplitTable(tas => tas.ContainsTableNames("_FirstA")).ToList();    
    //生成的SQL:
    //SELECT * FROM  (SELECT [Id],[Name],[CreateTime] FROM [WordTestTable_FirstA]  WHERE ( [Name] = @Name0UnionAll1 )) unionTable 

    原码地址:https://github.com/donet5/SqlSugar

    .NET 生态还处于较弱的状态,呼吁大家支持、踊跃参与开源项目,为下一个 .NET 开源社区五年计划做贡献。

    希望正在使用的、善良的您能动一动小手指,把文章转发一下,让更多人知道 .NET 有这样一个好用的 ORM 存在。谢谢了!!

  • 相关阅读:
    [Windows] 使用SC 命令管理与配置服务
    [SharePoint 2010] SharePoint 2010 部署、收回和删除解决方案----STSADM和PowerShell
    [SharePoint 2010] SharePoint 2010 FBA 配置以及自定义首页
    [Log]ASP.NET之HttpModule 事件执行顺序
    [SQL] 命令远程恢复数据库
    [工具] 各种主流 SQLServer 迁移到 MySQL 工具对比
    [工具] TreeSizeFree 查看每个文件夹的大小
    [APP] Android 开发笔记 006-使用短信验证SDK进行短信验证
    [APP] Android 开发笔记 004-Android常用基本控件使用说明
    [APP] Android 开发笔记 003-使用Ant Release 打包与keystore加密说明
  • 原文地址:https://www.cnblogs.com/sunkaixuan/p/15554623.html
Copyright © 2020-2023  润新知