最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mysql全局ID生成方法

 更新時間:2015年12月12日 10:47:21   作者:chenpingzhao  
本文給大家介紹mysql全局id生成方法,涉及到mysql全局id相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧

生產(chǎn)系統(tǒng)隨著業(yè)務(wù)增長總會經(jīng)歷一個業(yè)務(wù)量由小變大的過程,可擴(kuò)展性是考量數(shù)據(jù)庫系統(tǒng)高可用性的一個重要指標(biāo);在單表/數(shù)據(jù)庫數(shù)據(jù)量過大,更新量不斷飆漲時,MySQL DBA往往會對業(yè)務(wù)系統(tǒng)提出sharding的方案。既然要sharding,那么不可避免的要討論到sharding key問題,在有些業(yè)務(wù)系統(tǒng)中,必須保證sharding key全局唯一,比如存放商品的數(shù)據(jù)庫等,那么如何生成全局唯一的ID呢,下文將從DBA的角度介紹幾種常見的方案。

1、使用CAS思想

什么是CAS協(xié)議

Memcached于1.2.4版本新增CAS(Check and Set)協(xié)議類同于Java并發(fā)的CAS(Compare and Swap)原子操作,處理同一item被多個線程更改過程的并發(fā)問題

CAS的基本原理

基本原理非常簡單,一言以蔽之,就是“版本號”,每個存儲的數(shù)據(jù)對象,都有一個版本號。

我們可以從下面的例子來理解:

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

 •第一步,A取出數(shù)據(jù)對象X;
 •第二步,B取出數(shù)據(jù)對象X;
 •第三步,B修改數(shù)據(jù)對象X,并將其放入緩存;
 •第四步,A修改數(shù)據(jù)對象X,并將其放入緩存。

結(jié)論:第四步中會產(chǎn)生數(shù)據(jù)寫入沖突。

采用CAS協(xié)議,則是如下的情景。

 •第一步,A取出數(shù)據(jù)對象X,并獲取到CAS-ID1;

•第二步,B取出數(shù)據(jù)對象X,并獲取到CAS-ID2; 

•第三步,B修改數(shù)據(jù)對象X,在寫入緩存前,檢查CAS-ID與緩存空間中該數(shù)據(jù)的CAS-ID是否一致。結(jié)果是“一致”,就將修改后的帶有CAS-ID2的X寫入到緩存。

 •第四步,A修改數(shù)據(jù)對象Y,在寫入緩存前,檢查CAS-ID與緩存空間中該數(shù)據(jù)的CAS-ID是否一致。結(jié)果是“不一致”,則拒絕寫入,返回存儲失敗。

這樣CAS協(xié)議就用了“版本號”的思想,解決了沖突問題。(樂觀鎖概念)

其實(shí)這里并不是嚴(yán)格的CAS,而是使用了比較交換原子操作的思想。

生成思路如下:每次生成全局id時,先從sequence表中獲取當(dāng)前的全局最大id。然后在獲取的全局id上做加1操作,加1后的值更新到數(shù)據(jù)庫,如加1后的值為203,表名是users,數(shù)據(jù)表結(jié)構(gòu)如下:

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 = 203 where name = 'users' and gid < 203; 

sql語句的 and gid < 203 是為了保證并發(fā)環(huán)境下gid的值只增不減。

如果update語句的影響記錄條數(shù)為0說明,已經(jīng)有其他進(jìn)程提前生成了203這個值,并寫入了數(shù)據(jù)庫。需要重復(fù)以上步驟從新生成。

代碼實(shí)現(xiàn)如下:

//$name 表名
function next_id_db($name){
  //獲取數(shù)據(jù)庫全局sequence對象
  $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();
  $threshold = 100; //最大嘗試次數(shù)
  for($i = 0; $i < $threshold; $i++){
    $last_id = $seq_dao->get_seq_id($name);//從數(shù)據(jù)庫獲取全局id
    $id = $last_id +1;
    $ret = $seq_dao->set_seq_id($name, $id);
    if($ret){
      return $id;
      break;
    }
  }
  return false;
}

2、使用全局鎖

在進(jìn)行并發(fā)編程時,一般都會使用鎖機(jī)制。其實(shí),全局id的生成也是解決并發(fā)問題。

生成思路如下:

在使用redis的setnx方法和memcace的add方法時,如果指定的key已經(jīng)存在,則返回false。利用這個特性,實(shí)現(xiàn)全局鎖

每次生成全局id前,先檢測指定的key是否存在,如果不存在則使用redis的incr方法或者memcache的increment進(jìn)行加1操作。這兩個方法的返回值是加1后的值,如果存在,則程序進(jìn)入循環(huán)等待狀態(tài)。循環(huán)過程中不斷檢測key是否還存在,如果key不存在就執(zhí)行上面的操作。

代碼如下:

//使用redis實(shí)現(xiàn)
//$name 為 邏輯表名
function next_id_redis($name){
  $redis = Wk_Redis_Util::getRedis();//獲取redis對象
  $seq_dao = Wk_Sequence_Dao_Sequence::getInstance();//獲取存儲全局id數(shù)據(jù)表對象
  if(!is_object($redis)){
    throw new Exception("fail to create redis object");
  }
  $max_times = 10; //最大執(zhí)行次數(shù) 避免redis不可用的時候 進(jìn)入死循環(huán)
  while(1){
    $i++;
    //檢測key是否存在,相當(dāng)于檢測鎖是否存在
    $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){//如果循環(huán)等待時間大于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);//從數(shù)據(jù)庫獲取全局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寫入數(shù)據(jù)表sequence
  $redis->delete("sequence_{$name}_flag");//刪除key,相當(dāng)于釋放鎖
  $db = null;
  return $id;
} 

3、redis和db結(jié)合

使用redis直接操作內(nèi)存,可能性能會好些。但是如果redis死掉后,如何處理呢?把以上兩種方案結(jié)合,提供更好的穩(wěn)定性。
代碼如下:

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

4、Flicker的解決方案

因?yàn)閙ysql本身支持auto_increment操作,很自然地,我們會想到借助這個特性來實(shí)現(xiàn)這個功能。Flicker在解決全局ID生成方案里就采用了MySQL自增長ID的機(jī)制(auto_increment + replace into + MyISAM)。一個生成64位ID方案具體就是這樣的:
先創(chuàng)建單獨(dú)的數(shù)據(jù)庫(eg:ticket),然后創(chuàng)建一個表:

CREATE TABLE Tickets64 (
      id bigint(20) unsigned NOT NULL auto_increment,
      stub char(1) NOT NULL default '',
      PRIMARY KEY (id),
      UNIQUE KEY stub (stub)
  ) ENGINE=MyISAM 

當(dāng)我們插入記錄后,執(zhí)行SELECT * from Tickets64,查詢結(jié)果就是這樣的:

+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

在我們的應(yīng)用端需要做下面這兩個操作,在一個事務(wù)會話里提交:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID(); 

這樣我們就能拿到不斷增長且不重復(fù)的ID了。
到上面為止,我們只是在單臺數(shù)據(jù)庫上生成ID,從高可用角度考慮,
接下來就要解決單點(diǎn)故障問題:Flicker啟用了兩臺數(shù)據(jù)庫服務(wù)器來生成ID,
通過區(qū)分auto_increment的起始值和步長來生成奇偶數(shù)的ID。

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1
TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2 

最后,在客戶端只需要通過輪詢方式取ID就可以了。

 •優(yōu)點(diǎn):充分借助數(shù)據(jù)庫的自增ID機(jī)制,提供高可靠性,生成的ID有序。

 •缺點(diǎn):占用兩個獨(dú)立的MySQL實(shí)例,有些浪費(fèi)資源,成本較高。

以上內(nèi)容是小編給大家分享的Mysql全局ID生成方法,希望大家喜歡。

相關(guān)文章

  • 在MySQL中奏響數(shù)據(jù)庫操作的樂章(推薦)

    在MySQL中奏響數(shù)據(jù)庫操作的樂章(推薦)

    本文詳細(xì)介紹了如何在MySQL中進(jìn)行數(shù)據(jù)庫操作,包括創(chuàng)建、刪除、修改數(shù)據(jù)庫等,以及如何使用字符集和校驗(yàn)規(guī)則,以及備份和恢復(fù)數(shù)據(jù)庫的方法,同時,還討論了如何查看和修改數(shù)據(jù)庫的結(jié)構(gòu)和數(shù)據(jù),總的來說,本文為讀者提供了一份全面的MySQL數(shù)據(jù)庫操作指南
    2024-10-10
  • mysql如何查找連續(xù)出現(xiàn)n次以上的數(shù)

    mysql如何查找連續(xù)出現(xiàn)n次以上的數(shù)

    這篇文章主要介紹了mysql如何查找連續(xù)出現(xiàn)n次以上的數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • CentOS7.5 安裝MySql的教程

    CentOS7.5 安裝MySql的教程

    這篇文章主要介紹了CentOS7.5 安裝MySql的教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • MySQL的慢日志線上問題及優(yōu)化方案

    MySQL的慢日志線上問題及優(yōu)化方案

    給大家詳細(xì)分析了MySQL慢日志線上問題分析及功能優(yōu)化方案,需要的朋友跟著學(xué)習(xí)下吧。
    2017-12-12
  • mysql查詢今天、昨天、近7天、近30天、本月、上一月的SQL語句

    mysql查詢今天、昨天、近7天、近30天、本月、上一月的SQL語句

    這篇文章主要介紹了mysql查詢今天、昨天、近7天、近30天、本月、上一月的SQL語句,一般在一些統(tǒng)計(jì)報(bào)表中比較常用這個時間段,需要的朋友可以參考下
    2014-06-06
  • MySQL:reading initial communication packet問題解決方法

    MySQL:reading initial communication packet問題解決方法

    網(wǎng)站訪問出現(xiàn)如題錯誤,經(jīng)過檢查my.cnf,發(fā)現(xiàn)innodb_buffer_pool_size = 2048M 設(shè)置過大,調(diào)整為innodb_buffer_pool_size = 1024M即可,網(wǎng)上也有該問題的其他解決方法,但都不能解決我的問題
    2012-07-07
  • mysql中url時區(qū)的陷阱該如何規(guī)避詳解

    mysql中url時區(qū)的陷阱該如何規(guī)避詳解

    最近在工作中發(fā)現(xiàn)一個問題,是關(guān)于mysql中url時區(qū)的,發(fā)現(xiàn)這個陷阱如果大家不注意可能都會遇到,所以給大家總結(jié)下,這篇文章主要給大家介紹了關(guān)于mysql中url時區(qū)的陷阱該如何規(guī)避的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • mysql中over partition by的具體使用

    mysql中over partition by的具體使用

    在數(shù)據(jù)庫中,我們經(jīng)常需要對數(shù)據(jù)進(jìn)行分組排序等操作,MySQL的over partition by可以幫助我們更方便地進(jìn)行這些操作,本文主要介紹了mysql中over partition by的具體使用,感興趣的可以了解一下
    2024-02-02
  • 淺談mysql通配符進(jìn)行模糊查詢的實(shí)現(xiàn)方法

    淺談mysql通配符進(jìn)行模糊查詢的實(shí)現(xiàn)方法

    這篇文章主要介紹了淺談mysql通配符進(jìn)行模糊查詢,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Window中MySQL繞過密碼登錄的底層原理詳解

    Window中MySQL繞過密碼登錄的底層原理詳解

    在 MySQL 中,root 用戶的密碼存儲和認(rèn)證機(jī)制隨著版本的升級發(fā)生了變化,繞過密碼登錄主要依賴于 --skip-grant-tables 選項(xiàng),本文將詳細(xì)分析 MySQL 繞過密碼的底層原理,并解釋為什么某些操作在 --skip-grant-tables 模式下無效,以及如何正確生效修改
    2025-02-02

最新評論

三河市| 安龙县| 民丰县| 湖北省| 鄂托克旗| 民和| 崇左市| 资兴市| 楚雄市| 横峰县| 株洲市| 靖远县| 保靖县| 兴义市| 玉屏| 达拉特旗| 汉寿县| 岳阳县| 鹤峰县| 仙桃市| 静乐县| 监利县| 龙井市| 惠安县| 梁山县| 文山县| 南投市| 安国市| 呈贡县| 洛川县| 平阳县| 商水县| 宣武区| 怀柔区| 岳普湖县| 盐池县| 邵阳市| 遵化市| 普安县| 石狮市| 从江县|