1. 程式人生 > >Mysql primary key主鍵衝突的可能性與解決方案

Mysql primary key主鍵衝突的可能性與解決方案

 在mysql中,資料的儲存其實相當於Oracle的IOT表。主鍵和其他列的值以b+tree的形式組織在一起,在其葉子節點上不僅包含了主鍵,還包含了其他列的值。所以,我們在訪問以主鍵作為where條件的查詢時,極大的提高了效率。當然,這也存在一個缺點,主鍵和其他列存在一起,導致主鍵作為index比其他二級index需要的空間開銷更大,所以當我們做recover index scan時,就會需要scan更多的block。除此之外,我們還需要資料按照主鍵升序排列,所以我們的主鍵就可能要設定為自增的,以免隨機的數字作為主鍵,導致資料塊頻繁分裂,而減緩dml的速度。所以,一般情況下,我們會預設給每張表都加上一個自增的id作為主鍵,這個id的型別依據實際存放的資料量的範圍來定。

  從第一段,我們瞭解到了主鍵產生的必要性,那麼主鍵的衝突又是如何產生的呢?

  在mysql中,一般存在兩種replication,一種是ms(master-slave),一種是mm(master-master),在ms中,主鍵衝突的可能性一般為0,除非應用程式自己插的主鍵值,而沒有使用主鍵的自增序列。

  在master-slave replication中,每次都是在master端寫,然後通過binlog傳到slave端,slave的IO_therad將資料寫到relaylog,然後SQL_therad讀取relaylog的資訊,來進行主庫執行sql的重演,這個過程基本上稱為邏輯複製。每次,slave端根據master.info中的資訊向主庫傳送請求,主庫接收到請求後啟用binlog dump程序將binlog的資訊傳送到slave端,slave端的io執行緒得到資訊後,將資訊寫入relay log,sql執行緒讀取relay log資訊進行重演,所以備庫的資訊和主庫是一致的,不會存在主鍵衝突的問題,因為寫資料總是在一端進行的。

  然而,在master-master replication中,主鍵衝突卻是個實實在在的問題。因為這種架構中,主庫既作為主庫,也作為另一個庫的備庫。這種架構主要是在寫比較嚴重的情況下,緩解單個庫的寫壓力。因此,在這種設計中,primary key collision的問題是個需要解決的問題。假如存在兩臺master相同表的primary key有相同的increment and offset,就會導致在A庫上插入的紀錄傳到B庫後,在往B庫中的表插入資料時會存在主鍵衝突的問題 。下面是在electrictoolbox摘抄的一句話:

If you have auto incremental primary keys then the seed for the increment value needs to be different for each server, otherwise you could potentially have multiple servers attempt to insert records at the same time resulting in primary key conflicts.

You will notice in the configuration there is a setting for auto_increment_increment. This should be set to the number of servers there are. In the example in this post, there are 2 servers so it should be 2. If you intend to add other masters in the future then set it to a higher value.

The second setting is auto_increment_offset which should be set to the same as the server-id value. As an example, if server 1 inserted 5 records and then server 2 inserted 2 records the PKs inserted would be 1, 3, 5, 7, 9, 10, 12

一般,在我們在有n(n>1)個Mysql庫作為MM的情況下,每個庫的incement都設定為n,我們一般設定第1個庫的

auto_increment_offset = 1

auto_increment_increment = n

所以在第1個mysql庫上的第k(k為大於0的正整數)次插入記錄的主鍵的值為1+n*(k-1)。

同理,第m(n=>m>1)個mysql庫的設定為

auto_increment_offset = m

auto_increment_increment = n

所以第m個mysql庫上的第h(h為大於0的正整數)次插入的記錄的主鍵值為m+n*(h-1)

假設1+n*(k-1)=m+n*(h-1)進一步得到 1=m+n(h-k),由於1<m<=n,所以在h=k的情況下,1=m與m>1矛盾,不成立。同樣,如果h-k<0,因為h和k都是正整數,所以h-k<=-1,所以m+n(h-k)<=m-n,而m<=n,所以m-n<=0,所以m+n(h-k)<=0即m+n(h-k)!=1,所以也不成立,同樣,在h-k>0時,由於m>1,n>1,所以m+n(h-k)>1與m+n(h-k)=1矛盾,綜上所述,不會存在主鍵衝突的情況。,所以這種設定不存在主鍵衝突的問題,有效地解決了mm複製的過程中主鍵衝突的問題。所以,我們在一個有n個Mysql庫作為MM複製的環境中,設定第k(1<=k<=n)個庫的auto_increment_increment =n,auto_increment_offset =k,如此,無論在哪個master進行insert,複製到其slave後,都不會出現主鍵衝突的問題。