DotNetNext / SqlSugar

.Net aot ORM Fastest ORM Simple Easy VB.NET Sqlite orm Oracle ORM Mysql Orm 虚谷数据库 postgresql ORm SqlServer oRm 达梦 ORM 人大金仓 ORM 神通ORM C# ORM , C# ORM .NET ORM NET5 ORM .NET6 ORM ClickHouse orm QuestDb ,TDengine ORM,OceanBase orm,GaussDB orm ,Tidb orm Object/Relational Mapping
https://www.donet5.com/Home/Doc
MIT License
5.24k stars 1.33k forks source link

分表获取系统表信息问题 #1159

Closed carldai0106 closed 1 year ago

carldai0106 commented 1 year ago

我在论坛中提过2次问题,最后都不了了之,应该是我没有描述清楚。 我大概看了下源码,发现所有的分表逻辑都会去调用原生sql语句去获取数据库创建的用户表信息: 查询的SQL语句是:

protected override string GetTableInfoListSql
{
    get
    {
        return @"select TABLE_NAME as Name,TABLE_COMMENT as Description from information_schema.tables
                 where  TABLE_SCHEMA=(select database())  AND TABLE_TYPE='BASE TABLE'";
    }
}

如果数据库中创建的用户表很多,会导致查询很慢,会加一个系统锁,锁的耗时大约是 1~2秒,当在k8s中开启一个应用的多个实例,每个实例中有多个线程在同步数据的时候,数据库监控端可以看到非常多的系统锁。会严重拖慢数据库的响应速度。 我尝试过实现 接口:ISplitTableService 但是没用,还是会调用 这个函数

public List<SplitTableInfo> GetTables()
{
    var oldIsEnableLogEvent = this.Context.Ado.IsEnableLogEvent;
    this.Context.Ado.IsEnableLogEvent = false;
    var tableInfos = this.Context.DbMaintenance.GetTableInfoList(false);
    List<SplitTableInfo> result = Service.GetAllTables(this.Context,EntityInfo,tableInfos);
    this.Context.Ado.IsEnableLogEvent = oldIsEnableLogEvent;
    return result;
}

实现的接口函数:

/// <summary>
/// 返回数据库中所有分表
/// </summary>
public List<SplitTableInfo> GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List<DbTableInfo> tableInfos)
{
    var result = new List<SplitTableInfo>();
    var regex = EntityInfo.DbTableName.Replace("{ssid}", "[0-9]{1,}");
    var currentTables = tableInfos.Where(it => Regex.IsMatch(it.Name, regex, RegexOptions.IgnoreCase))
                                  .Select(it => it.Name).Reverse().ToList();

    foreach (var item in currentTables)
    {               
        var data = new SplitTableInfo()
        {
            TableName = item
        };

        result.Add(data);
    }

    return result.OrderBy(it => it.TableName).ToList();
}

List<DbTableInfo> tableInfos 这里的信息就是来自 GetTables()。

其他实现的3个获取表名函数:GetTableName 这个可以不用从 GetTables() 去获取。 我是希望作者可以不写死 GetTables() 函数,可以从某个地方传一个参数,可以定时去拉取数据库的用户表信息。 而不是每次都去拉取。

DotNetNext commented 1 year ago

不是回复过你了吗,通过自定义分表实现

DotNetNext commented 1 year ago

新建一个类继承 ORM自带的 DateSplitTableService 这个类重写内部方法 ,然后用自定分表就可以实了

carldai0106 commented 1 year ago

大佬,我都不知道是不是我错了。请看下我重写的类。

public class StationSplitService : ISplitTableService
    {
        /// <summary>
        /// 返回数据库中所有分表
        /// </summary>
        public List<SplitTableInfo> GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List<DbTableInfo> tableInfos)
        {
            var result = new List<SplitTableInfo>();
            var regex = EntityInfo.DbTableName.Replace("{ssid}", "[0-9]{1,}");
            var currentTables = tableInfos.Where(it => Regex.IsMatch(it.Name, regex, RegexOptions.IgnoreCase))
                                          .Select(it => it.Name).Reverse().ToList();

            foreach (var item in currentTables)
            {               
                var data = new SplitTableInfo()
                {
                    TableName = item
                };

                result.Add(data);
            }

            return result.OrderBy(it => it.TableName).ToList();
        }

        public object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue)
        {
            var splitColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyInfo.GetCustomAttribute<SplitFieldAttribute>() != null);
            var value = splitColumn.PropertyInfo.GetValue(entityValue, null);
            return value;
        }

        public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo)
        {
            var id = db.Queryable<h_station>().OrderBy(x => x.ID, OrderByType.Asc).Select(x => x.ID).FirstAsync();           
            return entityInfo.DbTableName.Replace("{ssid}", id.ToString());
        }

        public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType type)
        {
            var id = db.Queryable<h_station>().OrderBy(x => x.ID, OrderByType.Asc).Select(x => x.ID).FirstAsync();
            return entityInfo.DbTableName.Replace("{ssid}", id.ToString());
        }

        public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue)
        {           
            if (fieldValue == null)
            {
                var name = entityInfo.DbTableName.Replace("_{ssid}", "");
                return name;
            }

            return entityInfo.DbTableName.Replace("{ssid}", fieldValue.ToString());
        }
    }

下面这个方法传入的第三个参数:List<DbTableInfo> tableInfos 是不是来自这个查询:select TABLE_NAME as Name,TABLE_COMMENT as Description from information_schema.tables where TABLE_SCHEMA=(select database()) AND TABLE_TYPE='BASE TABLE'

/// <summary>
        /// 返回数据库中所有分表
        /// </summary>
        public List<SplitTableInfo> GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List<DbTableInfo> tableInfos)
        {
            var result = new List<SplitTableInfo>();
            var regex = EntityInfo.DbTableName.Replace("{ssid}", "[0-9]{1,}");
            var currentTables = tableInfos.Where(it => Regex.IsMatch(it.Name, regex, RegexOptions.IgnoreCase))
                                          .Select(it => it.Name).Reverse().ToList();

            foreach (var item in currentTables)
            {               
                var data = new SplitTableInfo()
                {
                    TableName = item
                };

                result.Add(data);
            }

            return result.OrderBy(it => it.TableName).ToList();
        }

然后这里会不会返回 所有表

DbContext.SplitHelper().GetTables(),

会不会调用这个查询语句:select TABLE_NAME as Name,TABLE_COMMENT as Description from information_schema.tables where TABLE_SCHEMA=(select database()) AND TABLE_TYPE='BASE TABLE

这个查询会不会调用获取所有表的查询语句: .SplitTable(tabs => tabs.Take(3))

DbContext.Queryable() .Where(x => x.LookTime < DateTime.Now && x.LookTime > DateTime.Now.AddDays(-3)) .SplitTable(tabs => tabs.Take(3)) .ToList();

image

只要您说不会调用,我就不纠结了。谢谢,浪费了您的宝贵时间。

DotNetNext commented 1 year ago

知道你的意思了,源码加了个事件 StaticConfig.SplitTableGetTablesFunc=xxx 你在程序启动时给这个委托重新赋值就行了

DotNetNext commented 1 year ago

image

carldai0106 commented 1 year ago

好的,感谢回复。我试试,非常感谢。我可能不怎么会表达需求。