热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysqlflicker_Mysql全局ID生成方法

生产系统随着业务增长总会经历一个业务量由小变大的过程,可扩展性是考量数据库系统高可用性的一个重要指标;在单表数据库数据量过大,更新量不断飙涨时ÿ

生产系统随着业务增长总会经历一个业务量由小变大的过程,可扩展性是考量数据库系统高可用性的一个重要指标;在单表/数据库数据量过大,更新量不断飙涨时,MySQL DBA往往会对业务系统提出sharding的方案。既然要sharding,那么不可避免的要讨论到sharding key问题,在有些业务系统中,必须保证sharding key全局唯一,比如存放商品的数据库等,那么如何生成全局唯一的ID呢,下文将从DBA的角度介绍几种常见的方案。

1、使用CAS思想

什么是CAS协议

Memcached于1.2.4版本新增CAS(Check and Set)协议类同于Java并发的CAS(Compare and Swap)原子操作,处理同一item被多个线程更改过程的并发问题

CAS的基本原理

基本原理非常简单,一言以蔽之,就是“版本号”,每个存储的数据对象,都有一个版本号。

我们可以从下面的例子来理解:

不采用CAS,则有如下的情景:

•第一步,A取出数据对象X;

•第二步,B取出数据对象X;

•第三步,B修改数据对象X,并将其放入缓存;

•第四步,A修改数据对象X,并将其放入缓存。

结论:第四步中会产生数据写入冲突。

采用CAS协议,则是如下的情景。

•第一步,A取出数据对象X,并获取到CAS-ID1;

•第二步,B取出数据对象X,并获取到CAS-ID2;

•第三步,B修改数据对象X,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“一致”,就将修改后的带有CAS-ID2的X写入到缓存。

•第四步,A修改数据对象Y,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“不一致”,则拒绝写入,返回存储失败。

这样CAS协议就用了“版本号”的思想,解决了冲突问题。(乐观锁概念)

其实这里并不是严格的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`)

) ENGINE=innodb

sql语句

update sequence set gid &#61; 203 where name &#61; &#39;users&#39; and gid <203;

sql语句的 and gid <203 是为了保证并发环境下gid的值只增不减。

如果update语句的影响记录条数为0说明&#xff0c;已经有其他进程提前生成了203这个值&#xff0c;并写入了数据库。需要重复以上步骤从新生成。

代码实现如下&#xff1a;

//$name 表名

function next_id_db($name){

//获取数据库全局sequence对象

$seq_dao &#61; Wk_Sequence_Dao_Sequence::getInstance();

$threshold &#61; 100; //最大尝试次数

for($i &#61; 0; $i <$threshold; $i&#43;&#43;){

$last_id &#61; $seq_dao->get_seq_id($name);//从数据库获取全局id

$id &#61; $last_id &#43;1;

$ret &#61; $seq_dao->set_seq_id($name, $id);

if($ret){

return $id;

break;

}

}

return false;

}

2、使用全局锁

在进行并发编程时&#xff0c;一般都会使用锁机制。其实&#xff0c;全局id的生成也是解决并发问题。

生成思路如下&#xff1a;

在使用redis的setnx方法和memcace的add方法时&#xff0c;如果指定的key已经存在&#xff0c;则返回false。利用这个特性&#xff0c;实现全局锁

每次生成全局id前&#xff0c;先检测指定的key是否存在&#xff0c;如果不存在则使用redis的incr方法或者memcache的increment进行加1操作。这两个方法的返回值是加1后的值&#xff0c;如果存在&#xff0c;则程序进入循环等待状态。循环过程中不断检测key是否还存在&#xff0c;如果key不存在就执行上面的操作。

代码如下&#xff1a;

//使用redis实现

//$name 为 逻辑表名

function next_id_redis($name){

$redis &#61; Wk_Redis_Util::getRedis();//获取redis对象

$seq_dao &#61; Wk_Sequence_Dao_Sequence::getInstance();//获取存储全局id数据表对象

if(!is_object($redis)){

throw new Exception("fail to create redis object");

}

$max_times &#61; 10; //最大执行次数 避免redis不可用的时候 进入死循环

while(1){

$i&#43;&#43;;

//检测key是否存在&#xff0c;相当于检测锁是否存在

$ret &#61; $redis->setnx("sequence_{$name}_flag",time());

if($ret){

break;

}

if($i > $max_times){

break;

}

$time &#61; $redis->get("sequence_{$name}_flag");

if(is_numeric($time) && time() - $time > 1){//如果循环等待时间大于1秒&#xff0c;则不再等待。

break;

}

}

$id &#61; $redis->incr("sequence_{$name}");

//如果操作失败&#xff0c;则从sequence表中获取全局id并加载到redis

if (intval($id) &#61;&#61;&#61; 1 or $id &#61;&#61;&#61; false) {

$last_id &#61; $seq_dao->get_seq_id($name);//从数据库获取全局id

if(!is_numeric($last_id)){

throw new Exception("fail to get id from db");

}

$ret &#61; $redis->set("sequence_{$name}",$last_id);

if($ret &#61;&#61; false){

throw new Exception("fail to set redis key [ sequence_{$name} ]");

}

$id &#61; $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&#xff0c;相当于释放锁

$db &#61; null;

return $id;

}

3、redis和db结合

使用redis直接操作内存&#xff0c;可能性能会好些。但是如果redis死掉后&#xff0c;如何处理呢&#xff1f;把以上两种方案结合&#xff0c;提供更好的稳定性。

代码如下&#xff1a;

function next_id($name){

try{

return $this->next_id_redis($name);

}

catch(Exception $e){

return $this->next_id_db($name);

}

}

4、Flicker的解决方案

因为mysql本身支持auto_increment操作&#xff0c;很自然地&#xff0c;我们会想到借助这个特性来实现这个功能。Flicker在解决全局ID生成方案里就采用了MySQL自增长ID的机制(auto_increment &#43; replace into &#43; MyISAM)。一个生成64位ID方案具体就是这样的&#xff1a;

先创建单独的数据库(eg:ticket)&#xff0c;然后创建一个表&#xff1a;

CREATE TABLE Tickets64 (

id bigint(20) unsigned NOT NULL auto_increment,

stub char(1) NOT NULL default &#39;&#39;,

PRIMARY KEY (id),

UNIQUE KEY stub (stub)

) ENGINE&#61;MyISAM

当我们插入记录后&#xff0c;执行SELECT * from Tickets64&#xff0c;查询结果就是这样的&#xff1a;

&#43;-------------------&#43;------&#43;

| id                | stub |

&#43;-------------------&#43;------&#43;

| 72157623227190423 |    a |

&#43;-------------------&#43;------&#43;

在我们的应用端需要做下面这两个操作&#xff0c;在一个事务会话里提交&#xff1a;

REPLACE INTO Tickets64 (stub) VALUES (&#39;a&#39;);

SELECT LAST_INSERT_ID();

这样我们就能拿到不断增长且不重复的ID了。

到上面为止&#xff0c;我们只是在单台数据库上生成ID&#xff0c;从高可用角度考虑&#xff0c;

接下来就要解决单点故障问题&#xff1a;Flicker启用了两台数据库服务器来生成ID&#xff0c;

通过区分auto_increment的起始值和步长来生成奇偶数的ID。

TicketServer1:

auto-increment-increment &#61; 2

auto-increment-offset &#61; 1

TicketServer2:

auto-increment-increment &#61; 2

auto-increment-offset &#61; 2

最后&#xff0c;在客户端只需要通过轮询方式取ID就可以了。

•优点&#xff1a;充分借助数据库的自增ID机制&#xff0c;提供高可靠性&#xff0c;生成的ID有序。

•缺点&#xff1a;占用两个独立的MySQL实例&#xff0c;有些浪费资源&#xff0c;成本较高。

以上内容是小编给大家分享的Mysql全局ID生成方法&#xff0c;希望大家喜欢。

本文标题: Mysql全局ID生成方法

本文地址: http://www.cppcns.com/shujuku/mysql/137064.html



推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了python面试题——数据库和缓存(46题)相关的知识,希望对你有一定的参考价值。1、列举常见的关系型数据库和非关系型都有那些? ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 数据库基本介绍
    1、数据库基本知识概念:数据库:database(DB),是一种存储数据的仓库数据库是根据数据结构组织、存储和 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 开发笔记:Memcached高性能内存对象缓存系统
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Memcached高性能内存对象缓存系统相关的知识,希望对你有一定的参考价值。一、Memcached概述 ... [详细]
  • 架构师必读:日均500万数据,如何进行数据存储选型?
    点击上方关注我,选择“置顶或者星标”作者:麦田里的老农来源:https:zhuanlan.zhihu.comp37964096小编公司有一 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • oracle avg row len,Oracle 估算数据库大小的方法
    一.说明一网友问我将一个查询的结果集存放到临时表里,如果估算临时表的大小,当时想的方法是通过统计block来计算。后来想,此方法的操作性也 ... [详细]
  • 一,织梦后台后台设置进入系统后台,在[系统基本参数]下面的性能选项卡当中,关于memcache进行如下配置:cfg_memcache_enable:是否启用memcache缓存,如果为否(N) ... [详细]
author-avatar
117942101-brsh
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有