posts - 15,comments - 21,trackbacks - 0
      之前因为时间匆忙,大区里产生唯一ID只是简单写了一个方法,今天趁着有时间,把里面一些过程写出来,也算是个总结。
      首先说说需求,现在游戏里数据库为了分散热点,都采用分表方式,以前那种一张表AUTO_INCREMENT的方式显然不行了,那么将唯一ID放到业务进程里可行吗?这个也不好,因为现在后台都是多个部署,每个进程可以保证ID唯一,但是存到数据库的时候就可能重复,所以我们还是得在DB上做文章。
      因此就有了上篇文章的解决方案,单独一张表,这张表的作用就是专门产生唯一ID,而且为多个业务提供唯一ID,多进程情况下也不需要锁表,效率比较高,是不是很像设计模式里的工厂。接着我来分析下这张表。我们再来看看表结构和用法
create table SeqTab 
   iSeqNo bigint(20) not null default 0,   //表示唯一ID
   iSeqType int(11) not null default 0,   //表示业务ID
   primary key(iSeqNo,iSeqType));
   insert into SeqTab values(0,1);        //初始化,假设一个业务编号为1
   update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;
   select last_insert_id();                   //这两句是获取一个业务的唯一ID,供业务进程使用。
   其实说到这张表,关键是说LAST_INSERT_ID()这个方法。它有两种形式LAST_INSERT_ID(),LAST_INSERT_ID(expr)。
   这个东西的特点是,1.属于每个CONNECTION,CONNECTION之间相互不会影响 2.不属于某个具体的表 3.返回最后一次INSERT AUTO_INCREMENT的值 4.假如以此使用INSERT插入    多行数据,只返回第一行数据产生的值 5.如果你UPDATE某个AUTO_INCREMENT的值,不会影响LAST_INSERT_ID()返回值
   LAST_INSERT_ID(expr)与LAST_INSERT_ID()稍有不同,首先它返回expr的值,其次它的返回值会记录在LAST_INSERT_ID()。
   我们这里主要使用到了第一和第二个特点,每个进程并发执行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;,就获取属于进程自己的iSeqNo并且记录在 LAST_INSERT_ID中,通过第二句取出该值。   
    接着我们在比较下其他一些办法。
    第一种是直接一张表,里面有一个ID字段,设置成AUTO_INCREMENT。这个的问题是每个业务ID不是连续的,是离散的。
    第二种是使用GUID或者UUID,但是这个问题我个人觉得是效率上的差异,字符串没有数字的效率好,另外数字ID今后也可以拼接一些区信息,之后跨区的时候可以方便获取对象是哪个区的.
posted on 2012-12-06 23:01 梨树阳光 阅读(2177) 评论(2)  编辑 收藏 引用 所属分类: 数据库

FeedBack:
# re: 大区中分配玩家唯一ID的办法(续)
2012-12-07 13:08 | Daly
以前设计过这样的方案, 也是一种常见的id分配方案了。

这个方案的短板在于:分配能力受DB处理能力限制,单点故障问题,另外这个SepTab表的数据至关重要,数据要从一而终,不能容许任何闪失。

利用划分号段的方法,比方说:server_id * 1000000 + local increment也是一种不错的解决方案。全球手机号码分配,IP分配也就是这么干嘛。
这方案的缺点是玩家最大ID数会受限制,而且所有玩家id长度相等,视乎你的玩家规模了。  回复  更多评论
  
# re: 大区中分配玩家唯一ID的办法(续)
2012-12-14 09:21 | 5
Guid不是字符串  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理