弃用数据库自增ID,曝光一下我自己用到的解决方法之---终结篇

转载 2011年01月20日 14:17:00

http://www.cnblogs.com/repository/archive/2011/01/20/1939450.html

 

我写这篇随笔的目的旨在 澄清我在上一篇随笔 “弃用数据库自增ID,曝光一下我自己用到的解决方法“ 中的一些事实与看法,同时,我将继续在并发的问题的作题,

我将在原来的存储过程上得用锁来解决并问题并附加上我的测试代码与测试数据。

     我之所以放在首页,并不是代表我这篇文章多有水平,多专业,我只是想分享一个程序员内心里深藏着的一点点设计思路与项目经验。通过分享来提高个人!

 

    PS:我写作的初衷重在于分享自己的技术知识,对问题的设计思路以及通过分享来得到更好的建议,同样的一件东西,并不是也并不可能适合于所有人或都所有场景,就算拿来主义也需要先去其糟粕,而后取其精华!老赵原来写过一些文章的初衷被很多人误解,而且还有一部分人在不明白作者的原意初衷上进行攻击,真的是悲哀! 不过还好,老赵这厮足够扎实,顶得住压力,呵呵....

 

   同时我针对一些评论也发下自己的看法:

  有很多人说用GUID代替,这当然也是一种方法,我如果说要曝光的方法竟然是用GUID来解决数据移值、数据分割的问题,相信肯定会有大把砖头扔过来!

  有人说用自增ID,自增ID也可以设置起始值,没错,看项目需要吧,我的随笔中真的没有提到一定要你用我的方法啊! 但是我总觉得插入关联数据时不方便,总是需要先拿到关键的外键值,不过有人Peter.zhu说到,可以在插入前拿到这个值,这个我还真不知道,还望告之...,

      有人说活雷锋 Dorian Deng)每次看到为了数据移植等情况摒弃自增ID都觉得可笑... 我一直都想不通迁移的频率或者概率有多大,如果这是十年后的问题...我觉得这话说得有点绝对,未雨绸缪并不见得就是一件坏事,从全局性看问题绝对没错,没必要把自己的那点项目经验来一并否决所有项目需要。这得看是什么类型项目了,这个世界上没有绝对的事情,我就还真碰到过!

    最多人说的就是并发问题,没错,确实会有并发问题,我讲得很清楚这个是它的一个缺点。不过还是有人拿它作文章,哪有十全十美的事情,不过对于一些小的应用,应该可以满足了!当然我曾经也用过!


     最后,也得感谢很多人,如: llzhzhbb卡通一下sinxsoft周强,.........

 

  废话不多讲了!言归正传

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~separator~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

    1:测试前提:

         测试之前,我先对原来的存储过程做一个单词的修改(当然了,我个存储过程可以优化,上一篇中有人给过不少的建议),这里我就不再罗嗦了,先看看我改后之后一个存储过程:

 

 

01 create procedure up_get_table_key
02 (
03    @table_name     varchar(50),
04    @key_value      int output
05 )
06 as
07 begin
08      begin tran
09          declare @key  int
10            
11          --initialize the key with 1
12          set @key=1
13          --whether the specified table is exist
14          if not exists(select table_name from table_key where table_name=@table_name)
15             begin
16               insert into table_key values(@table_name,@key)        --default key vlaue:1
17             end
18          -- step increase
19          else    
20             begin
21                 select @key=key_value from table_key with (updlock) where table_name=@table_name
22                 set @key=@key+1
23                 --update the key value by table name
24                 update table_key set key_value=@key where table_name=@table_name
25             end
26         --set ouput value
27     set @key_value=@key
28   
29     --commit tran
30     commit tran
31         if @@error>0
32       rollback tran
33 end

 

     注意在查询的时候我使用了updlock,我在检索时对该行加锁,updlock的优点是允许用户读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。也就是说,我在本次事务调用中取到key值之后,直到更新该数据之后,锁可以保证了key值不会更改,确实不会的会话不会拿到相同的key值。

 

    2:测试

    我的测试环境:SQL server 2008,.net framework 3.5

  先附上我的程序代码:

 

 

01 private void GetIncreateID()
02         {
03             while (!isStop)
04             {
05                 int a = Database.GetIncreaseID("stud");
06                 //int b = Database.GetIncreaseID("stud");
07                 //int c = Database.GetIncreaseID("stud");
08                 //int d = Database.GetIncreaseID("stud");
09                 Database.AddStudent(a,a.ToString());
10                 //Debug.WriteLine(string.Format("a={0},b={1},c={2},d={3}", a, b, c, d));
11   
12                 Thread.Sleep(200);
13             }
14         }
15   
16         protected void start_Click(object sender, EventArgs e)
17         {
18             Thread thread1 = new Thread(new ThreadStart(GetIncreateID));
19             Thread thread2 = new Thread(new ThreadStart(GetIncreateID));
20             Thread thread3 = new Thread(new ThreadStart(GetIncreateID));
21             Thread thread4 = new Thread(new ThreadStart(GetIncreateID));
22   
23             thread1.Start();
24             thread2.Start();
25             thread3.Start();
26             thread4.Start();
27   
28             for(int i = 0;i<1000;i++)
29             {
30                 Thread thread = new Thread(new ThreadStart(GetIncreateID));
31                 thread.Start();
32             }
33         }
34   
35         protected void stop_Click(object sender, EventArgs e)
36         {
37             isStop = true;
38         }

 

 

  数据访问层代码:

 

 

 

  我开启了至少1000个线程来插入数据,插入数据 id,name ,其中 id是表stud表的主键值,如果存在相同的ID值,插入数据时肯定会抛异常!

 

  再看看我的结果:

 

     我插入了700多万条数据,,纯粹只为测试并发性,没有发生相同的ID情况!

 

 

   BTW,我根据 llzhzhbb 的建议,也试着去实现它来测试并发性!这是一种全新的做法,先看看思路:

   1:定义静态int变量来取代自增ID

   2:应用程序启动时初始化该静态变量为表MAX ID值

   3:插入数据时,先对静态变量Interlocked.Increment自增1,然后再插入新的数据

  这种做法实际就是绕过数据库,利用应用程序来解决并发性,Interlocked.Increment会以生成原子操作来进行递增并存储结果,它可以保证多线程的并发时同步性,从而使得自增ID的唯一性。

   如果不是使用Interlocked.Increment会有同样的并发问题,因为很大一部分计算机上,自增操作并不是一种原子操作,因为在递增的过程中CPU会先把你要递增的变量值先读取到寄存器中,然后再对它进行自增操作,最后再把寄存器中已自增的值保存到变量中,明显,这中间发生了三个操作。因此无法保证在多线程下的并发问题。

 

  当然,这只是一种思路,来看看它的实际应用情况吧,还是用数据说话,先看看代码:

 

 

01 public static class TableMappingVariable
02     {
03         public static int CourseMappingID = 0;
04   
05         static TableMappingVariable()
06         {
07             DbCommand command = Database.CreateCommand();
08             command.CommandType = System.Data.CommandType.Text;
09             command.CommandText = "select max(cour_id) from course";
10   
11   
12             try
13             {
14                 command.Connection.Open();
15                 DbDataReader reader = command.ExecuteReader();
16                 if (reader.Read())
17                 {
18                     CourseMappingID = reader.GetInt32(0);
19                 }
20             }
21             catch (DbException ex)
22             {
23                 throw ex;
24             }
25             finally
26             {
27                 command.Connection.Close();
28             }
29         }
30     }

 

 

  为了简单起见,我直接把它写在静态构造函数了,初始化时取最大ID值。

同样,我在测试程序中,开启了至少1000个线程来插入数据。

 

 

01 protected void btnStart_Click(object sender, EventArgs e)
02         {
03             Thread thread1 = new Thread(new ThreadStart(GetIncreateID));
04             Thread thread2 = new Thread(new ThreadStart(GetIncreateID));
05             Thread thread3 = new Thread(new ThreadStart(GetIncreateID));
06             Thread thread4 = new Thread(new ThreadStart(GetIncreateID));
07   
08             thread1.Start();
09             thread2.Start();
10             thread3.Start();
11             thread4.Start();
12   
13             for (int i = 0; i < 1000; i++)
14             {
15                 Thread thread = new Thread(new ThreadStart(AddCourse));
16                 thread.Start();
17             }
18         }
19   
20         public void AddCourse()
21         {
22             while (!isStop)
23             {
24                 Database.AddCourse(Interlocked.Increment(ref TableMappingVariable.CourseMappingID), TableMappingVariable.CourseMappingID.ToString());
25                 Thread.Sleep(200);
26             }
27         }
28   
29         protected void btnStop_Click(object sender, EventArgs e)
30         {
31             isCourseStop = true;
32         }

 

 

    再看看测试数据:

 

 

我插入了400多万的数据。测试结果非常正确!这种方法相对于MAX(ID)+1来说,确实减少了数据库访问的次数,MAX(ID)+1在每次插入时都需要去表中检索一次来得到MAX(ID). 这种方法,我只需要读取一次就够了!当然不包括程序挂掉的情况........

 

   总结,我不想讲太多,当然了,你可以继续使用GUID,自增ID,或者MAX(ID)+1.....等来作为你数据库的主键,不过这些对我来说,并不重要,我不懂金谍K3,SQLLITE,对ORACLE懂得也不是很多,更不知道什么是copmiere,更不懂得他们的设计思想,如果能公开,那就最好不过了。这个东西纯粹只为测试并发性!有可能并不适合你!

   欢迎评论!

 

   最后,附上我的测试代码:(点击下载)

相关文章推荐

弃用数据库自增ID,曝光一下我自己用到的解决方法

在平时的项目开发中,我相信有很大一批人都在用这个数据库自增ID,用数据库自增ID有利也有弊。     优点:节省时间,根本不用考虑怎么来标识唯一记录,写程序也简单了,数据库帮我们维护着这一批ID号。...

ID自增JAVA类方法

  • 2013年12月20日 17:06
  • 1KB
  • 下载

oracle数据库中怎么给已经在使用的表添加主键id的解决方法

新建id字段(这个就不用说了)因为已经使用的表中有一些旧数据了,在不能删除的情况下必须批量添加id,方法是:update T_TEST  set I_ID=rownum commit;这样 所有旧数据...

mysql数据库自增id用法大全_MySQL

点击打开链接
  • Qiluoao
  • Qiluoao
  • 2014年11月10日 13:03
  • 4623

mysql 数据库自增id 的总结

有一个表StuInfo,里面只有两列 StuID,StuName其中StuID是int型,主键,自增列。现在我要插入数据,让他自动的向上增长,insert into StuInfo(StuID,Stu...

实现兼容各种数据库表字段的主键id自增

在数据库操作中,插入数据库信息的时候往往需要使其数据库的中的表字段的主键id能够自增,虽然有的数据库的表字段可也直接在设计的时候就写成自增,比如mysql,但是有的数据库却在实现表字段的自增上却不是很...
  • cselmu9
  • cselmu9
  • 2012年08月14日 10:02
  • 4005

如何在MySQl数据库中给已有的数据表添加自增ID?

由于使用MySQL数据库还没有多久的缘故,在搭建后台往数据库导入数据的时候发现新增的表单是没有自增id的,因次就有了上面这个问题。 解决方法1、给某一张表先增加一个字段,这里我们就以node_tabl...

mysql插入数据后返回自增ID的方法

mysql插入数据后返回自增ID的方法   mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数...

Orcale 建立 自增 id 主键的方法--艾克峰网络科技

Orcale  建立 自增 id 主键的方法 初次从SQL转移到Orcale上来第一个不适应的地方就是不知道怎么建立自增的ID或主键。Orcale数据库建立自增的方法确实不如MSSQL来的方便,在MS...

mongoDB 自增ID C++实现方法

1、实现原理 假设要为CollectionB实现自增ID,,需要引入另外一个计算“_id”的CollectionA。 CollectionA中存放一条记录:{'_id':'Collection...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:弃用数据库自增ID,曝光一下我自己用到的解决方法之---终结篇
举报原因:
原因补充:

(最多只允许输入30个字)