顯示定義ID
表定義的自增值ID達到上限后,在申請下一個ID時,得到的值保持不變
-- (2^32-1) = 4,294,967,295
-- 建議使用 BIGINT UNSIGNED
CREATE TABLE t (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY) AUTO_INCREMENT=4294967295;
INSERT INTO t VALUES (null);
-- AUTO_INCREMENT沒有改變
mysql> SHOW CREATE TABLE t;
+-------+------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------+
| t | CREATE TABLE `t` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4294967295 DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------+
mysql> INSERT INTO t VALUES (null);
ERROR 1062 (23000): Duplicate entry '4294967295' for key 'PRIMARY'
InnoDB row_id
1、如果創建的InnoDB表沒有指定主鍵,那么InnoDB會創建一個不可見的,長度為6 Bytes的row_id
2、InnoDB維護一個全局的dict_sys.row_id值,所有無主鍵的InnoDB表,每插入一行數據
- 都將當前的dict_sys.row_id值作為要插入數據的row_id,然后把dict_sys.row_id的值+1
3、代碼實現上,row_id是一個8 Bytes的BIGINT UNSIGNED
- 但InnoDB設計時,給row_id只保留了6 Bytes的空間,寫到數據表時只會存放最后的6 Bytes
- row_id的取值范圍:0 ~ 2^48-1
- 達到上限后,下一個值就是0
4、在InnoDB里面,申請到row_id=N后,就將這行數據寫入表中
- 如果表中已經有row_id=N的行,新寫入的行就會覆蓋原有的行
5、推薦顯示創建自增主鍵
- 表自增ID達到上限后,再插入數據時會報主鍵沖突的錯誤,影響的是可用性
- 而覆蓋數據,意味著數據丟失,影響的是可靠性
- 一般來說,可靠性優于可用性
XID
1、redolog和binlog相配合的時候,有一個共同的字段XID,對應一個事務
2、生成邏輯
- MySQL內部維護一個全局變量global_query_id
- 每次執行語句的時候將global_query_id賦值給Query_id,然后global_query_id+1
- 如果當前語句是這個事務執行的第一條語句,把Query_id賦值給這個事務的XID
3、global_query_id是一個純內存變量,重啟之后清零
- 因此,在同一個數據庫實例中,不同事務的XID也有可能是相同的
- MySQL重啟之后,會重新生成新的binlog
- global_query_id達到上限后,就會繼續從0開始計數
- 因此理論上,同一個binlog還是會出現相同的XID,只是概率極低
4、global_query_id是8 Bytes,上限為2^64-1
- 執行一個事務,假設XID是A
- 接下來執行2^64次查詢語句,讓global_query_id回到A
- 再啟動一個事務,這個事務的XID也是A
InnoDB trx_id
1、XID是由Server層維護的
2、InnoDB內部使用的是trx_id,為的是能夠在InnoDB事務和Server層之間做關聯
3、InnoDB內部維護一個max_trx_id的全局變量
- 每次需要申請一個新的trx_id,就獲得max_trx_id的當前值,然后max_trx_id+1
4、InnoDB數據可見性的核心思想
- 每一行數據都記錄了更新它的trx_id
- 當一個事務讀到一行數據的時候,判斷數據可見性的方法
5、對于正在執行的事務,可以通過information_schema.innodb_trx看到事務的trx_id
操作序列
時刻 |
session A |
session B |
T1 |
BEGIN;
SELECT * FROM t LIMIT 1; |
|
T2 |
|
USE information_schema;
SELECT trx_id,trx_mysql_thread_id FROM innodb_trx; |
T3 |
INSERT INTO t VALUES (null); |
|
T4 |
|
SELECT trx_id,trx_mysql_thread_id FROM innodb_trx; |
-- T2時刻
mysql> SELECT trx_id,trx_mysql_thread_id FROM innodb_trx;
+-----------------+---------------------+
| trx_id | trx_mysql_thread_id |
+-----------------+---------------------+
| 281479812572992 | 30 |
+-----------------+---------------------+
-- T4時刻
mysql> SELECT trx_id,trx_mysql_thread_id FROM innodb_trx;
+-----------------+---------------------+
| trx_id | trx_mysql_thread_id |
+-----------------+---------------------+
| 7417540 | 30 |
+-----------------+---------------------+
mysql> SHOW PROCESSLIST;
+----+-----------------+-----------+--------------------+---------+--------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+--------------------+---------+--------+------------------------+------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 344051 | Waiting on empty queue | NULL |
| 30 | root | localhost | test | Sleep | 274 | | NULL |
| 31 | root | localhost | information_schema | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+--------------------+---------+--------+------------------------+------------------+
1、trx_mysql_thread_id=30就是線程ID,即session A所在的線程
2、T1時刻,trx_id的值其實為0,而很大的值只是為了顯示用的(區別于普通的讀寫事務)
3、T2時刻,trx_id是一個很大的數字,因為在T1時刻,session A并未涉及更新操作,是一個只讀事務
4、session A在T3時刻執行INSERT語句時,InnoDB才真正分配trx_id
只讀事務
1、在上面的T2時刻,很大的trx_id是由系統臨時計算出來的
- 把當前事務的trx變量的指針地址轉成整數,再加上2^48
2、同一個只讀事務在執行期間,它的指針地址是不會變的
- 不論是在innodb_trx還是innodb_locks表里,同一個只讀事務查出來的trx_id都是一樣的
3、如果有多個并行的只讀事務,每個事務的trx變量的指針地址肯定是不同的
4、加上2^48的目的:保證只讀事務顯示的trx_id值比較大,用于區別普通的讀寫事務
5、trx_id與row_id的邏輯類似,定義長度為8 Bytes
- 在理論上,可能會出現一個讀寫事務與一個只讀事務顯示的trx_id相同的情況
- 但概率極低,并且沒有什么實質危害
6、只讀事務不分配trx_id的好處
- 可以減少事務視圖里面活躍數組的大小
- 當前正在運行的只讀事務,是不影響數據的可見性判斷
- 因此,在創建事務的一致性視圖時,只需要拷貝讀寫事務的trx_id
- 可以減少trx_id的申請次數
- 在InnoDB里,即使只執行一條普通的SELECT語句,在執行過程中,也要對應一個只讀事務
- 如果普通查詢語句不申請trx_id,就可以大大減少并發事務申請trx_id的鎖沖突
- 由于只讀事務不分配trx_id,trx_id的增加速度會變慢
7、max_trx_id會持久化存儲,重啟不會重置為0,只有到達2^48-1的上限后,才會重置為0
thread_id
1、SHOW PROCESSLIST的第一列就是thread_id
2、系統保存了一個環境變量thread_id_counter
- 每新建一個連接,就將thread_id_counter賦值給這個新連接的線程變量
3、thread_id_counter定義為4 Bytes,因此達到2^32-1后就會重置為0
- 但不會在SHOW PROCESSLIST里面看到兩個相同的thread_id
- 因為MySQL設計了一個唯一數組的邏輯,給新線程分配thread_id,邏輯代碼如下
do {
new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);
參考資料
《MySQL實戰45講》
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- 關于mysql自增id,你需要知道的
- MySQL表自增id溢出的故障復盤解決
- 關于MySQL自增ID的一些小問題總結
- 關于Mysql自增id的這些你可能還不知道
- mysql自增id超大問題的排查與解決
- MySQL分表自增ID問題的解決方法
- 兩種mysql對自增id重新從1排序的方法
- 利用mysql事務特性實現并發安全的自增ID示例
- mysql自增ID起始值修改方法
- MySQL的自增ID(主鍵) 用完了的解決方法