MySQL--自增列學習

##=====================================================================================##數據庫

在數據庫表設計中會糾結於」天然鍵」和」代理鍵」的選擇,天然鍵在實現數據「軟刪除」時實現比較複雜,部分天然鍵由於鍵值過長或多列組合致使不適合做爲表主鍵,而比較常見兩種代理鍵爲自增列(auto incremnet)和全局惟一標識列(GUID)。

使用自增列做爲主鍵的優缺點:
一、    主鍵鍵值長度短,INT列須要4個字節,BIGINT列須要8個字節;
二、    自增主鍵順序遞增,在INSERT操做時」順序」寫入表;
三、    因爲數據集中插入到表尾部,在高併發狀況下容易形成」數據頁熱點」,影響插入效率;
四、    自增主鍵只能保證在表內數據惟一,對於分庫分表場景,可能因錯誤操做產生相同的「惟一值」。


使用GUID列的優缺點:
一、    32位GUID字符串須要更多的存儲空間來存放(具體存儲長度與字符集相關),影響主鍵和其餘索引的查詢性能。
二、    GUID可實現全局惟一,能保證在多個表之間的數據惟一性
三、    GUID將數據分散到全表,不會產生熱點數據頁,但會形成大量隨機IO讀寫


在實際使用過程當中,不多場景會使用GUID做爲主鍵,大部分業務按照數據量需求使用INT或BIGINT的自增列做爲主鍵,對於須要多表惟一的場景能夠經過程序實現全局惟一的自增ID。安全


##=====================================================================================##
MySQL在很早版本便支持自增列並在各版本中優化自增列功能,在MySQL 5.1.22版本引入輕量級互斥自增加實現機制,MySQL 5.5版本中引入change buffer特性,在MySQL 8.0版本引入自增列持久化。針對目前京東主要使用MySQL 5.5/5.6/5.7三個主版本,羅列部分使用自增列須要掌握的知識點:

知識點1:自增列數據類型選擇問題
自增列除常見的TINYINT/SMALLINT/INT/BIGINT等整數數據類型外,還可使用FLOAT等浮點數數據類型,但強烈建議不使用非整數數據類型做爲自增列。
選取數據類型時:
一、    按照所須要範圍值進行最小化選取,若是隻須要0-20的範圍值,則選擇能夠存放-128到127數值的TINYINT。
二、    當所需自增範圍值不肯定時,建議選擇足夠使用的數據類型,先保證數據安全再考慮操做性能,相同數據量下,使用BIGINT並不會比使用INT帶來太多性能影響。京東訂單號在前期設計時使用INT數據類型,當INT沒法知足需求時,商城花費大量資源進行INT到BIGINT的升級改造,同時影響諸多關聯繫統。服務器

 

##=====================================================================================##
知識點2:自增列跳號問題
一、不管MySQL仍是其餘關係型數據庫,爲提升自增列的生成效率,都將生成自增值的操做設計爲非事務性操做,表現爲當事務回滾時,事務中生成的自增值不會被回滾。
二、當對自增表進行批量插入時(INSERT … SELECT …),即便在單一會話下,MySQL仍不能保證兩次獲取到的自增ID值連續,批量插入數據量越大,產生的自增ID跳號範圍越大。

自增跳號對普通業務沒有太多影響,但對於像發票這類要求號碼連續的業務,不能經過自增列來實現。

##=====================================================================================##
知識點3:自增列持久化問題
在MySQL 5.5/5.6/5.7三個版本中,MySQL並不會將自增列分配的自增值信息固化到磁盤,當MySQL重啓後,會根據自增列上當前最大值和參數auto_increment_offset來肯定下一次的自增值,爲快速獲取自增列上最大值,MySQL要求自增列必須建有索引。若是一張自增表的數據在重啓實例前被清空,實例重啓後該表數據會從」1」開始自增(假設表的自增初始值定義爲1)。
 
在一次亞一數據庫升級過程當中,某張業務表」剛好」由於業務邏輯將表中全部數據刪除,重啓後該表自增值從1開始生成,當該表數據流轉到其餘表出現數據衝突,發現問題後,咱們緊急手動設置該表自增值,避免事故進一步惡化,並再後期相似操做時,重點關注此類自增表。
 
建議1:若是業務會對自增表數據進行硬刪除,在服務器重啓前應重點關注該自增表使用的自增值,能夠經過information_schema.tables中的auto_increment列來獲取。
 
PS1:在MySQL 8.0中引入自增列持久化特性,能夠避免上述問題。併發

##=====================================================================================##
知識點4:自增列初始值問題
在運維過程當中,會遇到研發同事問爲何新建立的表不是從1開始自增,該問題能夠從如下兩個角度排查:
一、    建表語句,在使用SHOW CREATE TABLE或MySQLDump等命令導出表結構時,會包含該表當前使用的自增值,如:
 

二、    全局參數auto_increment_increment和auto_increment_offset,這兩全局參數能夠做用實例下全部自增表,主要應用在分庫分表的場景。運維


##=====================================================================================##
知識點5:修改數據列爲自增數據列
當數據類型爲數值類型且表中數據惟一時,能夠將該數據列轉換爲自增列,修改操做會保持列中現有數據,不會從新生成新數據。

高併發

##=====================================================================================##
知識點6:修改普通表爲自增表
在MySQL中容許使用ALTER TABLE方式爲普通表新增一個自增列,但因爲ALTER操做爲DDL語句,在主從複製時會將該DDL語句傳遞給從庫執行,MySQL並不能保證相同記錄在主從服務器上得到相同的自增ID,會致使主從數據差別。
模擬測試:性能

主庫上建立表:
CREATE TABLE TB1001
(
  C1 INT
);

會話1開啓事務並執行:
START TRANSACTION;
INSERT INTO TB1001(C1) SELECT 1;

會話2執行:
INSERT INTO TB1001(C1) SELECT 2;

會話1提交事務。

而後將表修改成自增表:
ALTER TABLE TB1001 ADD ID INT PRIMARY KEY AUTO_INCREMENT;

主庫數據爲:
 
從庫數據爲:
 

緣由分析:
在主庫上,C1=2的數據晚於C1=1的數據被插入,但因爲C1=2的數據所在事務被先提交,所以C1=2的記錄先於C1=1的記錄在從庫上執行,所以兩條記錄在主庫和從庫上的插入順序不一樣,在生成自增ID時得到到自增ID不一樣,最終致使數據差別。

建議:在將普通表修改成自增表時,若是表中存在數據,請勿使用ALTER TABLE的方式修改,建議新建自增臨時表,而後將數據導入到該表中,再兌換表名。

測試

##=====================================================================================##優化

相關文章
相關標籤/搜索