程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> Mysql全局ID生成辦法

Mysql全局ID生成辦法

編輯:MySQL綜合教程

Mysql全局ID生成辦法。本站提示廣大學習愛好者:(Mysql全局ID生成辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是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 = 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);
  }
} 

4、Flicker的處理計劃

由於mysql自己支撐auto_increment操作,很天然地,我們會想到借助這個特征來完成這個功效。Flicker在處理全局ID生成計劃裡就采取了MySQL自增加ID的機制(auto_increment + replace into + MyISAM)。一個生成64位ID計劃詳細就是如許的:
先創立零丁的數據庫(eg:ticket),然後創立一個表:

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 

當我們拔出記載後,履行SELECT * from Tickets64,查詢成果就是如許的:

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

在我們的運用端須要做上面這兩個操作,在一個事務會話裡提交:

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

如許我們就可以拿到赓續增加且不反復的ID了。
到下面為止,我們只是在單台數據庫上生成ID,從高可用角度斟酌,
接上去就要處理單點毛病成績:Flicker啟用了兩台數據庫辦事器來生成ID,
經由過程辨別auto_increment的肇端值和步長來生成奇偶數的ID。

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

最初,在客戶端只須要經由過程輪詢方法取ID便可以了。

 •長處:充足借助數據庫的自增ID機制,供給高靠得住性,生成的ID有序。

 •缺陷:占用兩個自力的MySQL實例,有些糟蹋資本,本錢較高。

以上內容是小編給年夜家分享的Mysql全局ID生成辦法,願望年夜家愛好。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved