zoukankan      html  css  js  c++  java
  • 从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又恢复后,自增数怎么同步

  • 相关阅读:
    Spring Boot2 系列教程(二十)Spring Boot 整合JdbcTemplate 多数据源
    Spring Boot 如何给微信公众号返回消息
    Spring Boot2 系列教程(十九)Spring Boot 整合 JdbcTemplate
    Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
    Spring Boot 开发微信公众号后台
    Spring Boot2 系列教程(十七)SpringBoot 整合 Swagger2
    Spring Boot2 系列教程(十六)定时任务的两种实现方式
    Spring Boot2 系列教程(十五)定义系统启动任务的两种方式
    Spring Boot2 系列教程(十四)CORS 解决跨域问题
    JavaScript二维数组
  • 原文地址:https://www.cnblogs.com/felixnet/p/7618411.html
Copyright © 2011-2022 走看看