摘要:對支持很好,分表后無需考慮全局的問題。但是這個項目使用的是進行開發,必須自己生成全局。語句的是為了保證并發環境下的值只增不減。每次生成全局前,先檢測指定的是否存在。代碼如下另外對于全局的生成,和也都公布了自己的方案。
最近一個項目由于數據量變大,需要進行數據分表。數據存儲在淘寶的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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/21267.html
摘要:開篇金幣積分商城下稱商城是眾多內的一個產品,隨著使用的用戶越來越多,商城對于用戶留存的提升,扮演著重要的角色做為提高用戶黏性的核心產品,在擁有很好用戶體驗的同時,也必須存在著一個高效穩定的系統。分析上述兩點,得到結論按用戶進行分庫分表。 開篇 金幣(積分)商城(下稱商城)是眾多App內的一個產品,隨著App使用的用戶越來越多,商城對于用戶留存的提升,扮演著重要的角色;做為提高用戶黏性的...
摘要:緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。這是緩存最原始的意義,同時也引申出了緩存最普遍的用法。但是現實中還有一種緩存,是主動更新的。 緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過memcached類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。 說起...
摘要:緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。這是緩存最原始的意義,同時也引申出了緩存最普遍的用法。但是現實中還有一種緩存,是主動更新的。 緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過memcached類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。 說起...
閱讀 1645·2021-09-26 09:55
閱讀 1379·2021-09-23 11:22
閱讀 2739·2021-09-06 15:02
閱讀 2648·2021-09-01 11:43
閱讀 3969·2021-08-27 13:10
閱讀 3684·2021-08-12 13:24
閱讀 2076·2019-08-30 12:56
閱讀 3002·2019-08-30 11:22