博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【数据库设计】“Max加一”生成主键的注意点
阅读量:6706 次
发布时间:2019-06-25

本文共 3488 字,大约阅读时间需要 11 分钟。

Sql Server数据库的主键设计有多种,比如经典的自增长,欢乐的,按照时间生成id(有通过程序生成的方式,还有一种也是通过数据库),按照业务模型组合生成有意义的id等等。最近项目中接触到一种模拟自增长自动编号主键的方式,即“Max加一”。

Max加一的原理看上去和自增长是相似的,表的唯一主键也设计成数字型(如bigint)的,只是把自动增长去掉了(表设计器标识规范一栏,“是标识”的选项选择否即可)。在Insert记录的时候,通常情况下的流程大致是这样的:读取当前表的Max主键值后加一,然后按照传递的相关参数,显式插入主键及其他列的值。这种生成主键方式的一个最显著的优点是可以按照自己的规则生成主键。比如有如下生成主键的用户自定义的存储过程usp_GetNewID:

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].usp_GetNewID') and OBJECTPROPERTY(id, N'IsProcedure') = 1)drop procedure [dbo].usp_GetNewIDGOCREATE PROCEDURE [dbo].usp_GetNewID@tableName nvarchar(30),  --表名@columnName nvarchar(30), --字段名@NewId int output          --Max(ID)生成的新ID    ASBEGINDECLARE @MaxId bigintDECLARE @MaxIdTemp bigintDECLARE @SQL nvarchar(500)DECLARE @SQLDBId nvarchar(8)set @SQLDBId='10'--获取原最大IDset @SQL ='SELECT @Mymaxid= isnull(MAX('+@columnName+'),101) From '+ @tableName;-- select @MaxId if @@error=0begin    exec sp_executesql @SQL,N'@Mymaxid bigint output',@MaxId outputend--生成新IDif @@error=0begin     select @MaxIdTemp=SUBSTRING(cast(@MaxId as nvarchar),3,100)+1; endif @@error=0begin    set @NewId=@SQLDBId+cast(@MaxIdTemp as nvarchar)endelsebegin    set @NewId=-1endEND

通过将@SQLDBId='10'和set @NewId=@SQLDBId+cast(@MaxIdTemp as nvarchar)这种方式的组合,我们可以控制不同的数据库服务器(或者不同的库)生成的主键都有规律可循,比如第一台服务器生成的id都以10开头,第二台都以20开头,依此类推,这样多少有利于数据库的分布式管理。

下面简单说说这种方式的两个重大缺陷:

1、效率问题

虽然主键有聚集索引,但是当我们的数据表数据达到一定数量级的时候(比如千万),那么通过聚合函数Max取值肯定会有不小的代价,这样显然会影响一点效率。但是到底效率几何,和自增长的性能比较又如何?这个我真的还没有这方面的测试数据,如果有童鞋有这方面的经验请不吝赐教,恳求告知。

UPDATE】:根据今天的性能测试,在表已有1百万数据基础上,继续插入数据,每次插入10000条记录,自增和Max加一这种方式的时间相差不足1秒,总体上自增长的方式会稍快一点,但是并不明显,在可接受范围内。测试结果见下图:

2、并发插入问题

当我们在程序中有顺序的先后插入数据的时候,这个问题当然不会发生。但是在大部分应用中,经常会并发处理一些数据,这个时候通过Max加一的方式就会造成插入上的并发问题。因为如果同时有两个或者多个插入请求读到相同的MAX值加一以后,在插入的时候就会发生插入重复主键的错误。

我们可以做一个简单的测试:

(1)、添加用户的存储过程usp_AddUser

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].usp_AddUser') and OBJECTPROPERTY(id, N'IsProcedure') = 1)drop procedure [dbo].usp_AddUserGOCREATE PROCEDURE [dbo].usp_AddUserASBEGINDECLARE @Id bigintEXEC  usp_GetNewID 'Users','Id',@Id OUT SELECT @Id if @@ERROR=0BEGININSERT INTO Users(    Id,    Name)VALUES(   @Id,   'jeff wong')    ENDEND

用户表简单设计成有Id和Name两个字段,插入的时候,Name的值不受任何干扰,固定为”jeff wong”。

(2)、然后在应用程序中调用如下:

static void Main(string[] args)        {            int counter = 2000;            Action action = null;            for (int i = 0; i < counter; i++)            {                action = AddUser;                action.BeginInvoke((a) =>                 {                     Action method = a.AsyncState as Action;                     method.EndInvoke(a);                 }, action);            }            Console.Read();        }        private static void AddUser()        {            try            {                using (var conn = new SqlConnection(sqlConnString))                {                    SqlCommand cmd = new SqlCommand("usp_AddUser", conn);                    cmd.CommandType = CommandType.StoredProcedure;                    conn.Open();                    int result = cmd.ExecuteNonQuery();                }            }            catch (Exception ex)            {                Console.WriteLine(ex.ToString());            }        }

在今晚本地的几组测试中,无一例外地都抛出了插入重复主键的异常。这个问题在这几天的一个数据同步程序中竟然没有发现,原因就是当时数据库没有或者很少符合条件的需要同步的数据。当然现在所有同步都已经改成通过在存储过程中利用游标顺序处理,这样就合理地解决掉并发插入问题了。

最后,我感觉主键的生成选择还有很多东西可以挖掘,有一些知识可以拿过来深入讨论一下,比如自增长是如何控制并发插入的,诸如此类,欢迎您的意见和建议。

本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2011/07/09/2102020.html,如需转载请自行联系原作者

你可能感兴趣的文章
windows2008部署服务器(WDS)服务器安装
查看>>
Keepalived+lvs
查看>>
RHCS集群理论暨最佳实践
查看>>
libgdx游戏引擎开发笔记(一)引擎介绍和Helloworld
查看>>
关于Linux的core dump
查看>>
Linux下Putty的复制与粘贴实现
查看>>
Thrift异步IO服务器源码分析
查看>>
转载:手把手教你做iOS推送
查看>>
RedHat 5.4+ Postfix +Extmail实现基于虚拟用户的邮件系统(五)
查看>>
通用权限管理系统组件 (GPM - General Permissions Manager) 中实现高性能的ASP.NET管理页面自动生成...
查看>>
[Android]文本框实现搜索和清空效果
查看>>
Linux的capability深入分析
查看>>
android app 集成 支付宝支付 微信支付
查看>>
Powershell进阶学习(3) Powershell实现远程管理
查看>>
LVM磁盘管理之扩展与缩小LV
查看>>
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十七:TFT模块 - 显示
查看>>
HOWTO:在 Hyper-V Server 2008 下安装驱动
查看>>
日志完美清理总结
查看>>
离线安装GCC
查看>>
[翻译]ASP.NET 3.5中的ListView控件和DataPager控件
查看>>