• 从Redis生成数据表主键标识


    对于MySql的全局ID(主键),我们一般采用自增整数列、程序生成GUID、单独的表作为ID生成器,这几种方案各有优劣,最终效率都不能说十分理想(尤其海量数据下),其实通过Redis的INCR可以很方便生成自增数,因为是操作缓存,生成的效率也不错。 

    插入数据库的主键也是连续增长的,配合索引,读取效率也很高。

    下面是从Redis中获取新的自增数的代码:

    public sealed class Utils
        {
            private static readonly object sequence_locker = new object();
    
            /// <summary>
            /// 从Redis获取一个自增序列标识
            /// </summary>
            /// <param name="key">键名</param>
            /// <param name="getting">获取序列标识替代生成方法(若缓存中不存在)</param>
            public static int NewSequenceFromRedis(string key, Func<int> alternative)
            {
                if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
                lock (sequence_locker)
                {
                    RedisHelper redis = new RedisHelper(1); //in db1
                    long value = redis.StringIncrement(key, 1);
                    if (value > Int32.MaxValue || value < Int32.MinValue) throw new OverflowException("The sequence overflow.");
                    if (value <= 1 && alternative != null)
                    {
                        value = alternative();
                        redis.StringSet(key, value.ToString());  //update
                    }
    
                    return (int)value;
                }
            }
        }

     我的项目用的Repository模式,所以获取新主键的方法我写到Repository父类中(在接口IRepository中有定义),这样各个Repository可以重载属性TableName,当然你完全可以把NewIdentity独立出去作为公共方法,只要传入TableName即可

    public abstract class RepositoryBase : IRepository
        {
            protected IDbConnection _db;
            public RepositoryBase(IDbConnection connection)
            {
                _db = connection;
            }
    
            protected virtual string TableName { get; }
    
            public virtual int NewIdentity()
            {
                if (string.IsNullOrEmpty(this.TableName))
                    throw new NoNullAllowedException("TableName is null.");
    
                var redisKey = $"Sequence_{TableName}.Id";  //eg. Sequence_lottery.Id, Sequence_player.Id
                var id = Utils.NewSequenceFromRedis(redisKey, () =>
                {
                    //如果从Redis中没获取到主键标识(比如Redis键被删除),则用数据表最大标识+1替代
                    return _db.ExecuteScalar<int>("SELECT MAX(id) AS MaxId FROM " + TableName) + 1;
                });
                return id;
            }
        }

     下面是测试代码,并且用StopWatch测试每次执行效率:

    using (var ctx = DI.Resolve<IRepositoryContext>())
    {
        System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        var userId = ctx.Resolve<IUserRepository>().NewIdentity();
        sw.Stop();
        Console.WriteLine("userId={0}, elapsed: {1}ms", userId, sw.ElapsedMilliseconds);
    
        sw.Restart();
        var gameId = ctx.Resolve<IGameRepository>().NewIdentity();
        sw.Stop();
        Console.WriteLine("gameId={0}, elapsed: {1}ms", gameId, sw.ElapsedMilliseconds);
    
        sw.Restart();
        var roomId = ctx.Resolve<IGameRepository>().NewRoomIdentity();
        sw.Stop();
        Console.WriteLine("roomId={0}, elapsed: {1}ms", roomId, sw.ElapsedMilliseconds);
    
        sw.Restart();
        var betItemId = ctx.Resolve<IGameRepository>().NewBetItemIdentity();
        sw.Stop();
        Console.WriteLine("betItemId={0}, elapsed: {1}ms", betItemId, sw.ElapsedMilliseconds);
    
        sw.Restart();
        var lotteryId = ctx.Resolve<ILotteryRepository>().NewIdentity();
        sw.Stop();
        Console.WriteLine("lotteryId={0}, elapsed: {1}ms", lotteryId, sw.ElapsedMilliseconds);
    
        //省略的代码。。。
    }

     运行结果如下,除第一次获取主键开销98毫秒(估计建立redis连接有关),后面的几乎都是0毫秒(Redis本来就飞快,这里不用考虑数据库连接开闭的时间消耗)

    查看Redis中的键值:

    当然,代码还需要完善,比如Redis挂了的情况,ID主键可以读取MAX(ID)+1来替代主键生成,但是Redis又恢复后,自增数怎么同步

  • 相关阅读:
    Solution -「CF 1025G」Company Acquisitions
    Solution -「Code+#4」「洛谷 P4370」组合数问题 2
    YApi,顶尖API接口管理平台
    Hibernate (开放源代码的对象关系映射框架)
    【LeetCode】5. 最长回文子串
    【LeetCode】105. 从前序与中序遍历序列构造二叉树
    【LeetCode】76. 最小覆盖子串
    【LeetCode】974. 和可被 K 整除的子数组
    【LeetCode】394. 字符串解码
    【LeetCode】5424. 数组中两元素的最大乘积
  • 原文地址:https://www.cnblogs.com/felixnet/p/7618411.html
Copyright © 2020-2023  润新知