对于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(); //in db1
long value = redis.StringIncrement(key, );
if (value > Int32.MaxValue || value < Int32.MinValue) throw new OverflowException("The sequence overflow.");
if (value <= && 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又恢复后,自增数怎么同步

从Redis生成数据表主键标识的更多相关文章

  1. 设置MySQL数据表主键

    设置MySQL数据表主键: 使用“primary key”关键字创建主键数据列.被设置为主键列不允许出现重复的值,很多情况下与“auto_increment”递增数字相结合.如下SQL语句所示: My ...

  2. 使用GUID作为数据表主键的好处(转)

    http://blog.itpub.net/3875/viewspace-789520/ 分类: 数据库开发技术 使用GUID作为数据表主键的好处 [@more@] 使用GUID作为数据表主键的好处 ...

  3. SQLITE数据表主键设置Id自增方法

    SQLITE数据表主键设置Id自增方法 标签: sqliteintegerinsertnulltableapi 2010-01-12 08:39 35135人阅读 评论(8) 收藏 举报  分类: S ...

  4. django学习-13.通过pk值手动设置数据表主键

    1.前言 通过django框架的Model层来新增数据库表时,如果在需要新增的表字段里任何一个表字段都没设置主键,框架会默认新增一个表字段id并把该表字段id设置为主键. 那么,如果我们想自己动手设置 ...

  5. Mybatis框架(9)---Mybatis自定义插件生成雪花ID做为表主键项目

    Mybatis自定义插件生成雪花ID做为主键项目 先附上项目项目GitHub地址 spring-boot-mybatis-interceptor 有关Mybatis雪花ID主键插件前面写了两篇博客作为 ...

  6. MyBatis框架——mybatis插入数据返回主键(mysql、oracle)

    向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...

  7. 解决getJdbcTemplate往oracle数据库中插入数据返回主键出错问题

    我们使用Spring中的JdbcDaoSupport往Mysql中插入数据并返回主键代码,我们使用的mysql数据库,主键在数据库中设置为自增长:该类继承自JdbcDaoSupport,所以能直接使用 ...

  8. [Done]SnowFlake生成Long类型主键返回前台过长导致精度缺失的问题

    问题描述: 在开发过程中,项目的主键生成器是SnowFlake,其生成的long主键是28位, 但是js中Long的最大值:https://blog.csdn.net/sunmerZeal/artic ...

  9. 如何准确高效的获取数据库新插入数据的主键id

    例如我们新建了一张表UserInformation,字段如下Id,为主键,自增,其它字段Name,Pwd,Email 然后我们来执行一个新增插入操作: insert into UserInformat ...

随机推荐

  1. 面向对象的css less 和sass

    Css 初始化   reset.css      或者  normalise .   Near.css兼容IE6以及现代浏览器. Oocss  也就是面向对象的css         面向对象是将cs ...

  2. JQuery/JS插件 数组转换为Table

    //数组 转换为 table var arr = [{ "D_AlarmValue": 7.00, "D_Code": "002", &qu ...

  3. Django_models下划线__正反查询,对象正反查询

    1.我们使用models对数据库进行查询的时候,想去拿到结果的时候有时直接使用".字段",有时是'[0].字段',区别就是我们使用的语句返回的到底是一个对象还是列表: obj=mo ...

  4. mysql学习笔记--列属性

    一.是否为空----null || not null 二.默认值----default 三.自动增长----auto_increment 四.主键----primary key 1. 主键:唯一标识表 ...

  5. 100-days: seventeen

    Title: How 'Bohemian Rhapsody(波西米亚狂想曲)' ended up in 'Wayne's World(反斗智多星)' and became a phenomenon(现 ...

  6. 654. Maximum Binary Tree 最大节点劈开,然后左边、右边排序

    [抄题]: Given an integer array with no duplicates. A maximum tree building on this array is defined as ...

  7. java命令分析线程死锁以及内存泄漏

    一.介绍 jstack是java虚拟机自带的一种堆栈跟踪工具.jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项&qu ...

  8. ES6 proxy(代理拦截) &&Reflect

  9. python基础 (编码进阶,文件操作和深浅copy)

    1.编码的进阶 字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码. 即先将其他编码的字符串解码(decode)成unicode,再从uni ...

  10. boost学习 泛型编程之traits 学习

    traits使用的场景一般有三种  分发到不同处理流程 解决C++代码中某些无法编译的问题 比如一个图书馆的代码,接受书籍并收入到不同类别中 template<class T> // T表 ...