资讯专栏INFORMATION COLUMN

数据库分表后,并发环境下,生成全局id生成的几种方式

testbird / 323人阅读

摘要:对支持很好,分表后无需考虑全局的问题。但是这个项目使用的是进行开发,必须自己生成全局。语句的是为了保证并发环境下的值只增不减。每次生成全局前,先检测指定的是否存在。代码如下另外对于全局的生成,和也都公布了自己的方案。

最近一个项目由于数据量变大,需要进行数据分表。数据存储在淘宝的tddl上。分表后,原先的自增id就不能使用了。tddl对java支持很好,分表后无需考虑全局id的问题。但是这个项目使用的是php进行开发,必须自己生成全局id。以下列出几种分表方案,仅当抛砖引玉。

1:使用CAS(compare and swap)

其实这里并不是严格的CAS,而是使用了比较交换原子操作的思想。
生成思路如下:每次生成全局id时,先从sequence表中获取当前的全局最大id。然后在获取的全局id上做加1操作。把加1后的值更新到数据库。更新时是关键。
如加1后的值为203,表名是users,数据表结构如下:

CREATE TABLE `SEQUENCE` (
    `name` varchar(30) NOT NULL COMMENT "分表的表名",
    `gid` bigint(20) NOT NULL COMMENT "最大全局id",
    PRIMARY KEY (`name`)
) E

那么更新语句是。
update sequence set gid = 203 where name = "users" and gid < 203;
sql语句的 and gid < 203 是为了保证并发环境下gid的值只增不减。
如果update语句的影响记录条数为0说明,已经有其他进程提前生成了203这个值,并写入了数据库。需要重复以上步骤从新生成。
代码实现如下:

//$name 表名
function next_id_db($name){
    //获取数据库全局sequence对象
    $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();
    $threshold = 100; //最大尝试次数
    for($i = 0; $i < $threshold; $i++){
        $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
        $id = $last_id +1;
        $ret = $seq_dao->set_seq_id($name, $id);
        if($ret){
            return $id;
            break;
        }
    }
    return false;
}
2:使用全局锁

在进行并发编程时,一般都会使用锁机制。其实,全局id的生成也是解决并发问题。
生成思路如下:
在使用redis的setnx方法和memcace的add方法时,如果指定的key已经存在,则返回false。利用这个特性,实现全局锁。
每次生成全局id前,先检测指定的key是否存在。
如果不存在则使用redis的incr方法或者memcache的increment进行加1操作。这两个方法的返回值是加1后的值。
如果存在,则程序进入循环等待状态。循环过程中不断检测key是否还存在,如果key不存在就执行上面的操作。
代码如下:

//使用redis实现
//$name 为 逻辑表名
function next_id_redis($name){
    $redis = Wk_Redis_Util::getRedis();//获取redis对象
    $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();//获取存储全局id数据表对象
    if(!is_object($redis)){
        throw new Exception("fail to create redis object");
    }
    $max_times = 10; //最大执行次数 避免redis不可用的时候 进入死循环
    while(1){
        $i++;
        //检测key是否存在,相当于检测锁是否存在
        $ret = $redis->setnx("sequence_{$name}_flag",time());
        if($ret){
            break;
        }
        if($i > $max_times){
            break;
        }
        $time = $redis->get("sequence_{$name}_flag");
        if(is_numeric($time) && time() - $time > 1){//如果循环等待时间大于1秒,则不再等待。
            break;
        }
    }
    $id = $redis->incr("sequence_{$name}");
    //如果操作失败,则从sequence表中获取全局id并加载到redis
    if (intval($id) === 1 or $id === false) {
        $last_id = $seq_dao->get_seq_id($name);//从数据库获取全局id
        if(!is_numeric($last_id)){
            throw new Exception("fail to get id from db");
        }
        $ret = $redis->set("sequence_{$name}",$last_id);
        if($ret == false){
            throw new Exception("fail to set redis key [ sequence_{$name} ]");
        }
        $id = $redis->incr("sequence_{$name}");
        if(!is_numeric($id)){
            throw new Exception("fail to incr redis key [ sequence_{$name} ]");
        }
    }
    $seq_dao->set_seq_id($name, $id);//把生成的全局id写入数据表sequence
    $redis->delete("sequence_{$name}_flag");//删除key,相当于释放锁
    $db = null;
    return $id;
}
3:redis和db结合

使用redis直接操作内存,可能性能会好些。但是如果redis死掉后,如何处理呢?把以上两种方案结合,提供更好的稳定性。
代码如下:

function next_id($name){
    try{
        return $this->next_id_redis($name);
    }
    catch(Exception $e){
        return $this->next_id_db($name);
    }
}

另外对于全局id的生成,Flicker和Twitter也都公布了自己的方案。感兴趣的人,可以了解下。

http://my.oschina.net/u/142836/blog/174465

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21267.html

相关文章

  • MySQL性能管理及架构设计(三):SQL查询优化、分库分表 - 完结篇

    摘要:第二阶段依照执行计划和存储引擎进行交互这个阶段包括了多个子过程一条查询可以有多种查询方式,查询优化器会对每一种查询方式的存储引擎统计信息进行比较,找到成本最低的查询方式,这也就是索引不能太多的原因。 上一篇:MySQL性能管理及架构设计(二):数据库结构优化、高可用架构设计、数据库索引优化 一、SQL查询优化(重要) 1.1 获取有性能问题SQL的三种方式 通过用户反馈获取存在性能问...

    kycool 评论0 收藏0
  • mysql分表、分区

    摘要:分表和分区有什么联系呢都能提高的性高,在高并发状态下都有一个良好的表现。表分区相对于分表,操作方便,不需要创建子表。自定义规则分表范围这种模式允许将数据划分不同范围。例如可以建立一个对表主键进行分区的表。 什么是分表? 分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子...

    littlelightss 评论0 收藏0
  • 金币(积分)商城架构漫谈

    摘要:开篇金币积分商城下称商城是众多内的一个产品,随着使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色做为提高用户黏性的核心产品,在拥有很好用户体验的同时,也必须存在着一个高效稳定的系统。分析上述两点,得到结论按用户进行分库分表。 开篇 金币(积分)商城(下称商城)是众多App内的一个产品,随着App使用的用户越来越多,商城对于用户留存的提升,扮演着重要的角色;做为提高用户黏性的...

    Ethan815 评论0 收藏0
  • 谈谈服务端缓存几种用法

    摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...

    tianhang 评论0 收藏0
  • 谈谈服务端缓存几种用法

    摘要:缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。这是缓存最原始的意义,同时也引申出了缓存最普遍的用法。但是现实中还有一种缓存,是主动更新的。 缓存是一个常谈常新的话题,作为一名服务端的技术,如果你入行一年都还没用过memcached类产品,那只能说你的公司实在太小了,或者你干的活实在太边缘了。 说起...

    CocoaChina 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<