从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(); //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生成数据表主键标识的更多相关文章
- 设置MySQL数据表主键
设置MySQL数据表主键: 使用“primary key”关键字创建主键数据列.被设置为主键列不允许出现重复的值,很多情况下与“auto_increment”递增数字相结合.如下SQL语句所示: My ...
- 使用GUID作为数据表主键的好处(转)
http://blog.itpub.net/3875/viewspace-789520/ 分类: 数据库开发技术 使用GUID作为数据表主键的好处 [@more@] 使用GUID作为数据表主键的好处 ...
- SQLITE数据表主键设置Id自增方法
SQLITE数据表主键设置Id自增方法 标签: sqliteintegerinsertnulltableapi 2010-01-12 08:39 35135人阅读 评论(8) 收藏 举报 分类: S ...
- django学习-13.通过pk值手动设置数据表主键
1.前言 通过django框架的Model层来新增数据库表时,如果在需要新增的表字段里任何一个表字段都没设置主键,框架会默认新增一个表字段id并把该表字段id设置为主键. 那么,如果我们想自己动手设置 ...
- Mybatis框架(9)---Mybatis自定义插件生成雪花ID做为表主键项目
Mybatis自定义插件生成雪花ID做为主键项目 先附上项目项目GitHub地址 spring-boot-mybatis-interceptor 有关Mybatis雪花ID主键插件前面写了两篇博客作为 ...
- MyBatis框架——mybatis插入数据返回主键(mysql、oracle)
向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...
- 解决getJdbcTemplate往oracle数据库中插入数据返回主键出错问题
我们使用Spring中的JdbcDaoSupport往Mysql中插入数据并返回主键代码,我们使用的mysql数据库,主键在数据库中设置为自增长:该类继承自JdbcDaoSupport,所以能直接使用 ...
- [Done]SnowFlake生成Long类型主键返回前台过长导致精度缺失的问题
问题描述: 在开发过程中,项目的主键生成器是SnowFlake,其生成的long主键是28位, 但是js中Long的最大值:https://blog.csdn.net/sunmerZeal/artic ...
- 如何准确高效的获取数据库新插入数据的主键id
例如我们新建了一张表UserInformation,字段如下Id,为主键,自增,其它字段Name,Pwd,Email 然后我们来执行一个新增插入操作: insert into UserInformat ...
随机推荐
- 最新ceph集群常用命令梳理
结合网络.官网.手动查询等多方渠道,整理ceph维护管理常用命令,并且梳理常规命令在使用过程中的逻辑顺序.另外整理期间发现ceph 集群的命令体系有点乱,详细情况各自体验. 一:ceph集群启动.重启 ...
- git克隆远程仓库的时候断电了,使用git-fetch断点续传
今天下载tensorflow serving 模型,但是因为主机电源线太长了,不知是我自己搞的还是同事,断电了都, 网速捉急,下载了挺长时间的,一看,git clone 到中途竟然断电,不过查看,还好 ...
- Truthy真值
在 JavaScript 中,Truthy (真值)指的是在 布尔值 上下文中转换后的值为真的值.所有值都是真值,除非它们被定义为 falsy (即除了 false,0,"",nu ...
- 迭代器模块 itertools
无限迭代器 itertools 包自带了三个可以无限迭代的迭代器.这意味着,当你使用他们时,你要知道你需要的到底是最终会停止的迭代器,还是需要无限地迭代下去. 这些无限迭代器在生成数字或者在长度未知的 ...
- MQTT服务器本地搭建
1.1 初认识MQTT协议. 2.1 下载压缩包 前往EMQ下载地址:http://emqtt.com/downloads ,下载您的系统的版本,一般选择稳定版. 2.2 解压并运行 C:\Users ...
- EF多字段求和(分组/不分组)
分组多字段求和 query.GroupBy(q => new { q.Year, q.Month }) .Select(q => new { Year = q.Key.Year, Mont ...
- linux用户和组管理,/etc/passwd 、/etc/shadow和/etc/group --学习
一./etc/passwd 和/etc/shadow解释 与用户相关的系统配置文件主要有/etc/passwd 和/etc/shadow,其中/etc/shadow是用户资讯的加密文件,比如用户的密码 ...
- HashMap的tableSizeFor方法解读
static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> ...
- JavaSE基础知识(5)—面向对象(5.2类的成员)
一.属性 1.语法 数据类型 属性名 [= 属性值]; 2.特点 ①属性的数据类型可以为任意类型,包含基本类型或引用类型②属性可以不用手动赋值,有默认值 int——0 double——0.0 char ...
- ie每次登陆出现:Windows安全性 iexplore.exe 正在连接到 记住我的凭证不起作用
解决方案: ie浏览器--设置--Intenet选项--安全--Internet--自定义级别--用户身份验证--登陆 勾选自动使用当前用户名和密码登陆 确定--确定