日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用同一uuid作为两个字段的值_分库设计中的主键选择

發布時間:2024/4/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用同一uuid作为两个字段的值_分库设计中的主键选择 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在先前的文章《又拍網架構中的分庫設計》中,我有提到過MySQL分庫設計中的主鍵選擇問題。在這篇文章里我想對這個問題進行展開討論,以此作為對上一篇文章的一個補充。

前面提到又拍網采用了全局唯一的字段作為主鍵。比如拿照片表為例,雖然不同用戶的照片數據存放在不同的Shard(或者說MySQL節點/實例, 請參考《又拍網架構中的分庫設計》)上,但是每一張照片擁有整個站點唯一的ID作為標示。

為什么要全局唯一?

我們在對數據庫集群作擴容時,為了保證負載的平衡,需要在不同的Shard之間進行數據的移動,如果主鍵不唯一,我們就沒辦法這樣隨意的移動數據。起初,我們考慮采用組合主鍵來解決這個問題。一般會以user_id和一個自增的photo_id來作為主鍵,這的確能解決移動數據可能帶來的主鍵沖突問題,但是就像在“又拍網架構中的分庫設計”中描述的那樣當Shard之間的數據發生關系后,我們需要用更多的字段來組成主鍵以保證唯一性,因此主鍵的索引會變的很大,從而影響查詢性能,同時也會影響寫入性能。

其次,每個Shard由兩臺MySQL服務器組成,而這兩臺服務器采用master-master的復制方式,以保證每個Shard一直可寫。master-master復制方式必須保證在兩臺服務器上各自插入的數據有不同的主鍵,不然當復制到另外一臺時就會出現主鍵重復錯誤。如果我們保證主鍵全局唯一,就自然的解決了這個問題。在沒有采用數據拆分的設計當中,如果要用自增字段,可以參考這篇文章里的解決辦法。

可能的解決方案

UUID

或許可以采用UUID作為主鍵,但是UUID好長的一串,放在URL里好難看啊,有木有?當然這個不是關鍵所在,更重要的原因還是性能。UUID的生成沒有順序性,所以在寫入時,需要隨機更改索引的不同位置,這就需要更多的IO操作,如果索引太大而不能存放在內存中的話就更是如此。而UUID索引時,一個key需要32個字節(當然如果采用二進制形式存儲的話可以壓縮到16個字節),因此整個索引也會相對比較大。

MySQL自增字段

在單個MySQL數據庫的應用中一般設置一個自增的字段就可以了,而在水平分庫的設計當中,這種方法顯然不能保證全局唯一。那么我們可以單獨建立一個庫用來生成ID,在Shard中的每張表在這個ID庫中都有一個對應的表,而這個對應的表只有一個字段,這個字段是自增的。當我們需要插入新的數據,我們首先在ID庫中的相應表中插入一條記錄,以此得到一個新的ID,然后將這個ID作為插入到Shard中的數據的主鍵。這個方法的缺點就是需要額外的插入操作,如果ID庫變的很大,性能也會隨之降低。所以一定要保證ID庫的數據集不要太大,一個辦法是定期清理前面的記錄。

引入其它工具

Redis、Memcached等都支持原子性的increment操作,而且因為它們的優秀性能可以減少寫入時的額外開銷,也許我們可以拿它們當作序列生成器。Memcached的問題在于不持久性,所以我們不會考慮。而Redis也不是實時持久的,當然也可以配置成實時的,但那樣怪怪的。當然也有一些持久的工具,比如Kyoto Cabinet、Tokyo Cabinet、MongoDB等等,傳說中性能都不錯,但是引入其它工具會增加架構的復雜程度,也會增加維護成本。我們的團隊很小,精力有限,我們奉行夠用就好的原則,也就是沒有特別的原因,在可以接受的情況下,盡量用我們熟悉的工具解決問題。所以,我們還是來考慮一下怎么樣用MySQL來解決這個問題吧。

更好的方案

我們一開始就是采用了上面所描述的MySQL自增字段的方法,后來看到《Ticket Servers: Distributed Unique Primary Keys on the Cheap》這篇文章里所描述的方法,豁然開朗。我經常這樣想:如果沒有那些開源產品、沒有那些無私分享經驗的人,光憑我們自己的能力能做到什么程度。很感謝那些人,所以我也盡量多的分享一些自己的經驗。

我先描述一下Flickr那篇文章里所描述的方法,他們使用了REPLACE INTO這個MySQL的擴展功能。REPLACE INTO和INSERT的功能一樣,但是當使用REPLACE INTO插入新數據行時,如果新插入的行的主鍵或唯一鍵(UNIQUE Key)已有的行重復時,已有的行會先被刪除,然后再將新數據行插入。你可以放心,這是原子操作。

建立類似下面的表:

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;

當需要獲得全局唯一ID時,執行下面的SQL語句:

REPLACE INTO `tickets64` (`stub`) VALUES ('a'); SELECT LAST_INSERT_ID();

第一次執行這個語句后,ticket64表將包含以下數據:

+--------+------+ | id | stub | +--------+------+ | 1 | a | +--------+------+

以后再次執行前面的語句,stub字段值為’a’的行已經存在,所以MySQL會先刪除這一行,再插入。因此,第二次執行后,ticket64表還是只有一行數據,只是id字段的值為2。這個表將一直只有一行數據。

Flickr為Photo, Group, Account, Task各自建立了一張ticket表以保持各自的ID的連續性。其它業務表的ID都使用同一個ticket表產生。

不錯吧,其實還可以更棒。比如,只需要一張ticket表就可以為所有的業務表提供各自連續的ID。下面,來看一下我們的方法。首先來看一下表結構:

CREATE TABLE `sequence` ( `name` varchar(50) NOT NULL, `id` bigint(20) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`name`) ) ENGINE=InnoDB;

注意區別,id字段不是自增的,也不是主鍵。在使用前,我們需要先插入一些初始化數據:

INSERT INTO `sequence` (`name`) VALUES ('users'), ('photos'), ('albums'), ('comments');

接下來,我們可以通過執行下面的SQL語句來獲得新的照片ID:

UPDATE `sequence` SET `id` = LAST_INSERT_ID(`id` + 1) WHERE `name` = 'photos'; SELECT LAST_INSERT_ID();

我們執行了一個更新操作,將id字段增加1,并將增加后的值傳遞到LAST_INSERT_ID函數,從而指定了LAST_INSERT_ID的返回值。

實際上,我們不一定需要預先指定序列的名字。如果我們現在需要一種新的序列,我們可以直接執行下面的SQL語句:

INSERT INTO `sequence` (`name`) VALUES('new_business') ON DUPLICATE KEY UPDATE `id` = LAST_INSERT_ID(`id` + 1); SELECT LAST_INSERT_ID();

這里,我們采用了INSERT … ON DUPLICATE KEY UPDATE這個MySQL擴展,這個擴展的功能也和INSERT一樣插入一行新的記錄,但是當新插入的行的主鍵或唯一鍵(UNIQUE Key)和已有的行重復時,會對已有行進行UPDATE操作。

需要注意的是,當我們第一次執行上面的語句時,因為還沒有name為’new_business’的字段,所以正常的執行了插入操作,沒有執行UPDATE,所以也沒有為LAST_INSERT_ID傳遞值。所以之后執行SELECT LAST_INSERT_ID()返回的值不可確定,要看當前連接在此之前執行過什么操作,如果沒有執行過會影響LAST_INSERT_ID值的操作,那么返回值將是0,不然就是該操作產生的值。所以,我們應該盡量避免使用這種方式。

UPDATE: 這個方法更容易解決單點問題,也不局限于兩個服務器,只要對不同的服務器設置不同的初始值(但必須是連續的),然后將增量變為服務器數就行了。

總結一下

我還是那句話,夠用就好。當然,也不是說就不要去了解其它產品、方案了。又拍網也在使用一些新興的產品,比如Redis(在10年3月就開始在正式環境下使用了,算是比較早的使用者),因為它的引入的確能夠更好、更方便、更高效的解決我們的某些問題。關鍵還是需要在使用前對其進行足夠的了解。我會在后面的文章中介紹一下Redis的使用情況。

總結

以上是生活随笔為你收集整理的用同一uuid作为两个字段的值_分库设计中的主键选择的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。