成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL:Innodb 唯一索引出現重復值的場景分析

數據庫
當我們進行數據插入的時候,對于唯一索引,實際上大概會經歷數據查找,唯一性檢查、數據插入三個階段。

最近遇到類似案例,這里將可能出現這種情況的2個場景描述一下,其中一種情況在翻看老葉的公眾號有類似文章,如下,

故障案例:MySQL唯一索引有重復值,官方卻說This is not a bug

我們分別描述。

場景1 unique_checks = 0

1.原理

當我們進行數據插入的時候,對于唯一索引,實際上大概會經歷數據查找,唯一性檢查、數據插入 3個階段。而對于普通索引來講如果page不在buffer pool中則可能在數據查找階段就會寫入到ibuff,這種情況就等待后續的ibuff合并即可。

但是我們一旦設置了unique_checks=0,對于唯一索引(非主鍵)而言也可能走普通索引的方式,我們大概看看是如何改變的,首先根據設置,事務檢查唯一索引的標記會設置為如下,

  trx->check_unique_secondary =
      !thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS);

然后在插入階段,row_ins_sec_index_entry_low函數首先會根據是否檢查唯一性將search_mode 設置上BTR_IGNORE_SEC_UNIQUE標記,search_mode 的值很多,主要包含2類,A:讀寫鎖模式/B:操作方式,他們各自占用不同的bit位。

if (!thr_get_trx(thr)->check_unique_secondary) {search_mode |= BTR_IGNORE_SEC_UNIQUE;}

接下來就是查找數據調用btr_cur_search_to_nth_level上層函數,進行數據定位,然后在其中判定,

case BTR_INSERT:
      btr_op = (latch_mode & BTR_IGNORE_SEC_UNIQUE)
                   ? BTR_INSERT_IGNORE_UNIQUE_OP
                   : BTR_INSERT_OP;
      break;

如果為insert且latch_mode帶有BTR_IGNORE_SEC_UNIQUE,設置btr_op為BTR_INSERT_IGNORE_UNIQUE_OP。

最后在判定是否能夠使用ibuf上,我們看到如下,

    if (btr_op != BTR_NO_OP && 
        ibuf_should_try(index, btr_op != BTR_INSERT_OP)) { //是否進入 ibuf
      /* Try to buffer the operation if the leaf
      page is not in the buffer pool. */

      fetch = btr_op == BTR_DELETE_OP ? Page_fetch::IF_IN_POOL_OR_WATCH //這里和 WATCH和purge線程有光
                                      : Page_fetch::IF_IN_POOL; //bug page get gen 只看是否在buffer中
    }

而函數ibuf_should_try就是判定是否使用ibuf,一旦使用ibuf,當然修改的相關page就不一定非要在buffer pool中,因此對于insert操作定義為Page_fetch::IF_IN_POOL,而函數ibuf_should_try主要包含如下判定:

  • A:開啟了change buffer
  • B:不等于系統表空間
  • C:不能是聚集索引
  • D:不能處于export狀態下
  • E:insert操作不能是唯一索引
  • F:其他操作,唯一索引也可以使用ibuf,這里實際上就只剩下delete和ignore唯一性的insert了

而在底層修改操作實際上只有insert和delete操作,而這里滿足的是F條件因此insert操作的查找page動作被標記為Page_fetch::IF_IN_POOL,接下來buf_page_get_gen函數就不會再去訪問物理磁盤了,這個時候可能返回的page為NULL,那就要走這個邏輯了:

  if (block == nullptr) { //如果block沒有在innodb buffer中進行操作
...
    switch (btr_op) {
      case BTR_INSERT_OP:
      case BTR_INSERT_IGNORE_UNIQUE_OP: //注意這里
...
        if (ibuf_insert(IBUF_OP_INSERT, tuple, index, page_id, page_size,
                        cursor->thr)) {
          cursor->flag = BTR_CUR_INSERT_TO_IBUF;
          goto func_exit;
        }

也就是插入到ibuf中,那么我們可以想象,如果設置了unique_checks=0,這個時候如果重復的數據在磁盤上(因為innodb buffer查詢不到page返回NULL),則會將接下來的數據本該重復的數據插入到ibuf,而不會去檢測重復值。然后等到需要讀取這個page到buffer pool的時候比如select,那就需要做ibuf的合并,合并后重復的數據就出現了。

2.測試

測試可以根據老葉公眾號的方式測試,主體思想就是做一個大一點的表,然后重啟數據庫,并且不要開啟啟動時加載page到buffer pool,下面是我測試的結果:

這里b列是一個唯一索引,我們看到了第二查詢出現了2個相同的值。

3.其他和總結

當出現這種情況的時候可以看到,第一個查詢只出現了一行,這看起來好像是對的,但是實際上索引上有2行不同的值,對于唯一索引來講如果訪問到一行值,訪問就會停止,因此出現了這種情況,看起來也是比較奇特。 因此我們在考慮使用unique_checks=0加速導入數據的時候需要特別注意一下這個問題,除非能夠保證數據都是唯一的否則不建議設置,現在我們知道實際上加速就是讓唯一索引也能夠使用ibuf這個特性,這里我們再來會看一下官方的這句話

For big tables, this saves a lot of disk I/O because InnoDB can use its change buffer to write secondary index records in a batch. Be certain that the data contains no duplicate keys.

很顯然和我們分析一致。

場景2 RR隔離級別相關

這個地方主要和隔離級別有關了,雖然提出這個BUG的時間有點久了,但是這不是BUG,并且8.0也能重現,如下, https://bugs.mysql.com/bug.php?id=69979

重現如下:

建表和插入數據
create table testuniq(id int primary key,a int unique key);
 insert into testuniq values (10, 100), (20, 200);
mysql> select * from testuniq;
+----+------+
| id | a    |
+----+------+
| 10 |  100 |
| 20 |  200 |
+----+------+

TRX1

TRX2

1.begin;


2.select * from testuniq;



3.update testuniq set a=300 where id=10;

4.update testuniq set a=100 where id=20;


5.select * from testuniq;


完成第四步的時候數據就是:

mysql> select * from testuniq;
+----+------+
| id | a    |
+----+------+
| 10 |  100 |
| 20 |  100 |
+----+------+

可以看到唯一索引出現了重復值,對于這個問題,只要不阻止第4步的update testuniq set a=100 where id=20操作按照原理上來講就會出現重復值,因為RR有一個read view在begin開始后第一個select語句后一直存在,而update屬于當前讀訪問的當前記錄已經被修改了,因此第4步并沒有訪問歷史記錄,因此update通過,最終出現這種現象。同時在BUG中也詳細描述了這是符合設計的PG也是類似的結果,可以自行參考。

責任編輯:趙寧寧 來源: MySQL學習
相關推薦

2023-02-26 00:00:06

MySQL索引故障

2016-08-05 14:33:19

MySQL索引數據庫

2024-08-19 09:43:00

2024-05-24 09:29:28

2022-08-04 08:22:49

MySQL索引

2018-09-14 09:12:00

數據庫MySQL索引約束

2018-09-16 23:14:18

MySQL索引約束主鍵

2011-08-18 11:18:25

Oracle唯一約束唯一索引

2023-01-03 07:44:53

MySQL查詢重復

2015-05-20 13:48:26

MySQL索引

2021-04-12 10:52:10

InnoDB索引數據庫

2010-10-12 14:40:03

mysql索引

2021-09-01 08:58:15

項目 UTFailed

2022-01-27 11:02:04

索引數據存儲

2024-04-19 13:57:30

索引數據庫查詢

2010-11-25 14:40:27

MySQL查詢

2023-02-10 10:14:59

普通索引唯一索引

2010-03-02 16:50:34

WCF返回值

2014-09-29 09:48:54

MySQLMySQL實踐

2011-06-15 18:59:16

算法SEO
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国产综合久久久久久丝袜 | 欧美4p | av免费网站在线观看 | 国产第一亚洲 | 欧美美女被c | 亚洲一区二区三区免费在线观看 | 99成人| 免费观看一级特黄欧美大片 | 精品九九 | 激情av免费看 | 精品视频一区二区三区在线观看 | 国产欧美日韩一区 | 狠狠av | 国产乱码精品一区二区三区忘忧草 | 男人的天堂亚洲 | 欧美激情亚洲激情 | 一区二区日本 | 四虎最新视频 | 日韩精品一区二区三区在线 | 一级做a毛片 | 九一精品 | 久久99国产精一区二区三区 | 欧美亚洲一区二区三区 | 日韩在线91 | 亚洲在线成人 | 国产精品亚洲视频 | 一级做受毛片免费大片 | 亚洲精品一区二区三区蜜桃久 | 久久久久欧美 | 精品一级| 成人一区二区三区在线 | 亚洲视频在线观看 | 亚洲看片 | 成人久久18免费 | 成人精品在线观看 | 中文字幕在线精品 | 国产精品区二区三区日本 | 一区二区三区免费 | 久久久久国色av免费观看性色 | 91视在线国内在线播放酒店 | 综合第一页 |