1. 程式人生 > >MySQL auto_increment的坑

MySQL auto_increment的坑

lec not null date let 模式 value set update語句 from

背景:

  Innodb引擎使用B_tree結構保存表數據,這樣就需要一個唯一鍵表示每一行記錄(比如二級索引記錄引用)。

  Innodb表定義中處理主鍵的邏輯是:

  1.如果表定義了主鍵,就使用主鍵唯一定位一條記錄

  2.如果沒有定義主鍵,Innodb就生成一個全局唯一的rowid來定位一條記錄

auto_increment的由來:

  1.Innodb強烈推薦在設計表中自定義一個主鍵,因為rowid是全局唯一的,所以如果有很多表沒有定義主鍵,就會在生成rowid上產生爭用。

      /* Dictionary system struct */
      struct dict_sys_struct{
      mutex_t	mutex;
      row_id_t	row_id;
      ......
      }

  row_id由mutex保護,並在每次checkpoint的時候,寫入到數據字典的文件頭。

  2.當用戶自定義了主鍵後,由於大部分實際應用部署的分布式,所以主鍵值的生成上,采用集中式的方式,更容易實現唯一性,所以auto_increment非常合適。

  auto_increment也帶來兩個好處:

  1. auto_increment的值是表級別的,不會在db級別上產生爭用

  2. 由於auto_increment的順序性,減少了隨機讀的可能,保證了寫入的page的緩沖命中。(不可否認,寫入的並發足夠大時,會產生熱點塊的爭用)

auto_increment引起的bug:

  環境:MySQL 5.6.16版本, binlog_format=row

  case復現:

     create table test.kkk ( c int(11) default null, id int(11) not null auto_increment, d int(11) default null, primary key (id), unique key d (d) )
     engine=innodb default charset=latin1;
     insert into test.kkk values(5, 27,4);
     replace into test.kkk(c, id, d) values(6, 35, 4);
     commit;	
     show create table時:
     主庫:auto_increment=36
     備庫:auto_increment=28

  當進行主備切換後,導致主鍵沖突,slave恢復異常。

  同樣insert on duplication update 語句同樣存在這樣的問題。

aliyun rds分支bug修復

  問題的原因:Innodb對於auto_increment的處理,當語句是insert時,會進行遞增,而update,delete語句則不更新。

  當replace語句在主庫的執行時:

  1. 先按照insert語句執行,發現uk沖突。

  2. 演變成update語句進行更新。

  這樣在主庫,雖然insert失敗,但auto_increment也遞增上去了。但到備庫,row格式下,只產生了一個update row event,

  備庫無法知道主庫是一個replace語句,而且insert還失敗了, 所以auto_increment在備庫沒有遞增。

  修復方式:在備庫,對於update進行auto_increment遞增,可能會產生副作用,即auto_increment的浪費,但不會產生主鍵沖突。

那些年經歷的auto_increment坑:

  1. 實例重啟,主鍵沖突:

  內存中的autoinc值,在系統重啟後,使用select max(id) from table來初始化。所以,如果你設計的業務表,存在delete操作,那麽一旦你的實例crash過,重啟後,可能會復用以前使用過的id值。如果你需要持續對這個表進行邏輯備份,那麽就可能會碰到主鍵沖突的問題。

  2. load file阻塞:

  在設置innodb_autoinc_lock_mode=1的時候,MySQL為了維護單個statement語句的id連續性,當不確定插入條數的時候,會在語句整個執行過程中

  持有LOCK_AUTO_INC, /* locks the auto-inc counter of a table in an exclusive mode */

  這個鎖是表級別的,使用互斥模式。

  所以,在繁忙的表上,如果要導入數據,小心可能阻塞正常的業務寫入,並發寫入在這個時候也會阻塞的。

MySQL auto_increment的坑