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

MySQL的這個(gè)bug,坑了多少人?

數(shù)據(jù)庫(kù) MySQL
近期,線上有個(gè)重要Mysql客戶(hù)的表在從5.6升級(jí)到5.7后,master上插入過(guò)程中出現(xiàn)"Duplicate key"的錯(cuò)誤,而且是在主備及RO實(shí)例上都出現(xiàn)。

問(wèn)題描述

近期,線上有個(gè)重要Mysql客戶(hù)的表在從5.6升級(jí)到5.7后,master上插入過(guò)程中出現(xiàn)"Duplicate key"的錯(cuò)誤,而且是在主備及RO實(shí)例上都出現(xiàn)。

以其中一個(gè)表為例,遷移前通過(guò)“show create table” 命令查看的auto increment id為1758609, 遷移后變成了1758598,實(shí)際對(duì)遷移生成的新表的自增列用max求最大值為1758609。

用戶(hù)采用的是Innodb引擎,而且據(jù)運(yùn)維同學(xué)介紹,之前碰到過(guò)類(lèi)似問(wèn)題,重啟即可恢復(fù)正常。

內(nèi)核問(wèn)題排查

由于用戶(hù)反饋在5.6上訪問(wèn)正常,切換到5.7后就報(bào)錯(cuò)。因此,首先得懷疑是5.7內(nèi)核出了問(wèn)題,因此第一反應(yīng)是從官方bug list中搜索一下是否有類(lèi)似問(wèn)題存在,避免重復(fù)造車(chē)。經(jīng)過(guò)搜索,發(fā)現(xiàn)官方有1個(gè)類(lèi)似的bug,這里簡(jiǎn)單介紹一下該bug。

背景知識(shí)1

Innodb引擎中的auto increment 相關(guān)參數(shù)及數(shù)據(jù)結(jié)構(gòu)

主要參數(shù)包括:innodb_autoinc_lock_mode用于控制獲取自增值的加鎖方式,auto_increment_increment, auto_increment_offset用于控制自增列的遞增的間隔和起始偏移。

主要涉及的結(jié)構(gòu)體包括:數(shù)據(jù)字典結(jié)構(gòu)體,保存整個(gè)表的當(dāng)前auto increment值以及保護(hù)鎖;事務(wù)結(jié)構(gòu)體,保存事務(wù)內(nèi)部處理的行數(shù);handler結(jié)構(gòu)體,保存事務(wù)內(nèi)部多行的循環(huán)迭代信息。

背景知識(shí)2

  • mysql及Innodb引擎中對(duì)autoincrement訪問(wèn)及修改的流程
  • 數(shù)據(jù)字典結(jié)構(gòu)體(dict_table_t)換入換出時(shí)對(duì)autoincrement值的保存和恢復(fù)。換出時(shí)將autoincrement保存在全局的的映射表中,然后淘汰內(nèi)存中的dict_table_t。換入時(shí)通過(guò)查找全局映射表恢復(fù)到dict_table_t結(jié)構(gòu)體中。相關(guān)的函數(shù)為dict_table_add_to_cache及dict_table_remove_from_cache_low。
  • row_import, table truncate過(guò)程更新autoincrement。
  • handler首次open的時(shí)候,會(huì)查詢(xún)當(dāng)前表中最大自增列的值,并用最大列的值加1來(lái)初始化表的data_dict_t結(jié)構(gòu)體中的autoinc的值。
  • insert流程。相關(guān)對(duì)autoinc修改的堆棧如下:
  1. ha_innobase::write_row:write_row的第三步中調(diào)用handler句柄中的update_auto_increment函數(shù)更新auto increment的值 
  2.     handler::update_auto_increment: 調(diào)用Innodb接口獲取一個(gè)自增值,并根據(jù)當(dāng)前的auto_increment相關(guān)變量的值調(diào)整獲取的自增值;同時(shí)設(shè)置當(dāng)前handler要處理的下一個(gè)自增列的值。 
  3.         ha_innobase::get_auto_increment:獲取dict_tabel中的當(dāng)前auto increment值,并根據(jù)全局參數(shù)更新下一個(gè)auto increment的值到數(shù)據(jù)字典中 
  4.             ha_innobase::dict_table_autoinc_initialize:更新auto increment的值,如果指定的值比當(dāng)前的值大,則更新。 
  5.         handler::set_next_insert_id:設(shè)置當(dāng)前事務(wù)中下一個(gè)要處理的行的自增列的值。 

(5) update_row。對(duì)于”INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE”語(yǔ)句,無(wú)論唯一索引列所指向的行是否存在,都需要推進(jìn)auto increment的值。相關(guān)代碼如下:

  1. if (error == DB_SUCCESS 
  2.       && table->next_number_field 
  3.       && new_row == table->record[0] 
  4.       && thd_sql_command(m_user_thd) == SQLCOM_INSERT 
  5.       && trx->duplicates)  { 
  6.       ulonglong    auto_inc; 
  7.               …… 
  8.       auto_inc = table->next_number_field->val_int(); 
  9.       auto_inc = innobase_next_autoinc(auto_inc, 1, increment, offset, col_max_value); 
  10.           error = innobase_set_max_autoinc(auto_inc); 
  11.               …… 
  12.   } 

從我們的實(shí)際業(yè)務(wù)流程來(lái)看,我們的錯(cuò)誤只可能涉及insert及update流程。

  1. BUG 76872 / 88321: "InnoDB AUTO_INCREMENT produces same value twice" 

(1) bug概述:當(dāng)autoinc_lock_mode大于0,且auto_increment_increment大于1時(shí),系統(tǒng)剛重啟后多線程同時(shí)對(duì)表進(jìn)行insert操作會(huì)產(chǎn)生“duplicate key”的錯(cuò)誤。

(2) 原因分析:重啟后innodb會(huì)把a(bǔ)utoincrement的值設(shè)置為max(id) + 1。此時(shí),首次插入時(shí),write_row流程會(huì)調(diào)用handler::update_auto_increment來(lái)設(shè)置autoinc相關(guān)的信息。首先通過(guò)ha_innobase::get_auto_increment獲取當(dāng)前的autoincrement的值(即max(id) + 1),并根據(jù)autoincrement相關(guān)參數(shù)修改下一個(gè)autoincrement的值為next_id。當(dāng)auto_increment_increment大于1時(shí),max(id) + 1 會(huì)不大于next_id。handler::update_auto_increment獲取到引擎層返回的值后為了防止有可能某些引擎計(jì)算自增值時(shí)沒(méi)有考慮到當(dāng)前auto increment參數(shù),會(huì)重新根據(jù)參數(shù)計(jì)算一遍當(dāng)前行的自增值,由于Innodb內(nèi)部是考慮了全局參數(shù)的,因此handle層對(duì)Innodb返回的自增id算出的自增值也為next_id,即將會(huì)插入一條自增id為next_id的行。handler層會(huì)在write_row結(jié)束的時(shí)候根據(jù)當(dāng)前行的值next_id設(shè)置下一個(gè)autoincrement值。如果在write_row尚未設(shè)置表的下一個(gè)autoincrement期間,有另外一個(gè)線程也在進(jìn)行插入流程,那么它獲取到的自增值將也是next_id。這樣就產(chǎn)生了重復(fù)。

(3) 解決辦法:引擎內(nèi)部獲取自增列時(shí)考慮全局autoincrement參數(shù),這樣重啟后第一個(gè)插入線程獲取的自增值就不是max(id) + 1,而是next_id,然后根據(jù)next_id設(shè)置下一個(gè)autoincrement的值。由于這個(gè)過(guò)程是加鎖保護(hù)的,其他線程再獲取autoincrement的時(shí)候就不會(huì)獲取到重復(fù)的值。

通過(guò)上述分析,這個(gè)bug僅在autoinc_lock_mode > 0 并且auto_increment_increment > 1的情況下會(huì)發(fā)生。實(shí)際線上業(yè)務(wù)對(duì)這兩個(gè)參數(shù)都設(shè)置為1,因此,可以排除這個(gè)bug造成線上問(wèn)題的可能性。

現(xiàn)場(chǎng)分析及復(fù)現(xiàn)驗(yàn)證

既然官方bug未能解決我們的問(wèn)題,那就得自食其力,從錯(cuò)誤現(xiàn)象開(kāi)始分析了。

(1) 分析max id及autoincrement的規(guī)律 由于用戶(hù)的表設(shè)置了ON UPDATE CURRENT_TIMESTAMP列,因此可以把所有的出錯(cuò)的表的max id、autoincrement及最近更新的幾條記錄抓取出來(lái),看看是否有什么規(guī)律。抓取的信息如下:

 

MySQL的這個(gè)bug,坑了多少人?

乍看起來(lái),這個(gè)錯(cuò)誤還是很有規(guī)律的,update time這一列是最后插入或者修改的時(shí)間,結(jié)合auto increment及max id的值,現(xiàn)象很像是最后一批事務(wù)只更新了行的自增id,沒(méi)有更新auto increment的值。聯(lián)想到【官方文檔】中對(duì)auto increment用法的介紹,update操作是可以只更新自增id但不觸發(fā)auto increment推進(jìn)的。按照這個(gè)思路,我嘗試復(fù)現(xiàn)了用戶(hù)的現(xiàn)場(chǎng)。復(fù)現(xiàn)方法如下:

 

MySQL的這個(gè)bug,坑了多少人?

同時(shí)在binlog中,我們也看到有update自增列的操作。如圖:

 

MySQL的這個(gè)bug,坑了多少人?

不過(guò),由于binlog是ROW格式,我們也無(wú)法判斷這是內(nèi)核出問(wèn)題導(dǎo)致了自增列的變化還是用戶(hù)自己更新所致。因此我們聯(lián)系了客戶(hù)進(jìn)行確認(rèn),結(jié)果用戶(hù)很確定沒(méi)有進(jìn)行更新自增列的操作。那么這些自增列到底是怎么來(lái)的呢?

(2) 分析用戶(hù)的表及sql語(yǔ)句 繼續(xù)分析,發(fā)現(xiàn)用戶(hù)總共有三種類(lèi)型的表(hz_notice_stat_sharding, hz_notice_group_stat_sharding,hz_freeze_balance_sharding),這三種表都有自增主鍵。但是前面兩種都出現(xiàn)了autoinc錯(cuò)誤,唯獨(dú)hz_freeze_balance_sharding表沒(méi)有出錯(cuò)。難道是用戶(hù)對(duì)這兩種表的訪問(wèn)方式不一樣?抓取用戶(hù)的sql語(yǔ)句,果然,前兩種表用的都是replace into操作,最后一種表用的是update操作。難道是replace into語(yǔ)句導(dǎo)致的問(wèn)題?搜索官方bug, 又發(fā)現(xiàn)了一個(gè)疑似bug。

  1. bug #87861: “Replace into causes master/slave have different auto_increment offset values” 

原因:

  • Mysql對(duì)于replace into實(shí)際是通過(guò)delete + insert語(yǔ)句實(shí)現(xiàn),但是在ROW binlog格式下,會(huì)向binlog記錄update類(lèi)型日志。Insert語(yǔ)句會(huì)同步更新autoincrement,update則不會(huì)。
  • replace into在Master上按照delete+insert方式操作, autoincrement就是正常的。基于ROW格式復(fù)制到slave后,slave機(jī)上按照update操作回放,只更新行中自增鍵的值,不會(huì)更新autoincrement。因此在slave機(jī)上就會(huì)出現(xiàn)max(id)大于autoincrement的情況。此時(shí)在ROW模式下對(duì)于insert操作binlog記錄了所有的列的值,在slave上回放時(shí)并不會(huì)重新分配自增id,因此不會(huì)報(bào)錯(cuò)。但是如果slave切master,遇到Insert操作就會(huì)出現(xiàn)”Duplicate key”的錯(cuò)誤。
  • 由于用戶(hù)是從5.6遷移到5.7,然后直接在5.7上進(jìn)行插入操作,相當(dāng)于是slave切主,因此會(huì)報(bào)錯(cuò)。

解決方案

業(yè)務(wù)側(cè)的可能解決方案:

  • binlog改為mixed或者statement格式
  • 用Insert on duplicate key update代替replace into

內(nèi)核側(cè)可能解決方案:

  • 在ROW格式下如果遇到replace into語(yǔ)句,則記錄statement格式的logevent,將原始語(yǔ)句記錄到binlog。
  • 在ROW格式下將replace into語(yǔ)句的logevent記錄為一個(gè)delete event和一個(gè)insert event。

心得

  • autoincrement的autoinc_lock_mode及auto_increment_increment這兩個(gè)參數(shù)變化容易導(dǎo)致出現(xiàn)重復(fù)的key,使用過(guò)程中要盡量避免動(dòng)態(tài)的去修改。
  • 在碰到線上的問(wèn)題時(shí),首先應(yīng)該做好現(xiàn)場(chǎng)分析,明確故障發(fā)生的場(chǎng)景、用戶(hù)的SQL語(yǔ)句、故障發(fā)生的范圍等信息,同時(shí)要對(duì)涉及實(shí)例的配置信息、binlog甚至實(shí)例數(shù)據(jù)等做好備份以防過(guò)期丟失。只有這樣才能在找官方bug時(shí)精準(zhǔn)的匹配場(chǎng)景,如果官方?jīng)]有相關(guān)bug,也能通過(guò)已有線索獨(dú)立分析。

 

責(zé)任編輯:未麗燕 來(lái)源: 今日頭條
相關(guān)推薦

2020-07-20 09:40:49

MySQLBUG數(shù)據(jù)庫(kù)

2013-08-20 15:27:59

Linux操作系統(tǒng)

2021-09-25 13:05:10

MYSQL開(kāi)發(fā)數(shù)據(jù)庫(kù)

2025-02-11 08:00:00

閉包JavaScript開(kāi)發(fā)

2024-02-20 08:23:35

LookupSpringbeans

2010-07-01 14:35:57

Windows 7

2023-04-09 15:23:58

Python編程開(kāi)發(fā)

2019-08-08 16:27:36

自動(dòng)駕駛無(wú)人駕駛駕駛

2022-05-10 12:20:04

JDKversion故障

2019-01-23 11:08:13

Windows微軟操作系統(tǒng)

2021-07-29 06:28:13

網(wǎng)絡(luò)網(wǎng)工網(wǎng)絡(luò)中斷

2022-10-11 16:28:42

比特幣加密貨幣資產(chǎn)

2021-04-15 11:07:01

比特幣貨幣加密貨幣

2022-11-12 12:32:39

.NET微軟

2018-06-21 07:40:23

無(wú)線充電無(wú)線供電無(wú)線輸電

2021-10-15 06:49:37

MySQL

2021-12-20 09:46:26

代碼開(kāi)發(fā)GitHub

2021-02-24 09:43:36

MySQL數(shù)據(jù)庫(kù)雙引號(hào)

2020-11-13 10:25:41

人臉數(shù)據(jù)

2024-07-04 11:44:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: av大片| 一级欧美视频 | 欧美久久精品一级黑人c片 91免费在线视频 | 午夜羞羞 | 九九精品在线 | 亚洲精品国产第一综合99久久 | 亚州综合在线 | 国产精品久久7777777 | 国产97在线看 | 免费观看av| 久久久久久久久久久久久久av | 国产综合一区二区 | 亚洲精品日韩视频 | 亚洲精品国产偷自在线观看 | 成人免费在线播放视频 | 久久精品国产免费一区二区三区 | 一区二区三区久久久 | 国产精品久久久久久久久久久久久 | 中文字幕一二三 | www.国产精品 | 中文字幕欧美在线观看 | 黄色av网站在线免费观看 | 欧美一区二区三区一在线观看 | 国产一级毛片视频 | 久久精品视频免费观看 | 有码一区| 亚洲福利| 在线观看亚洲一区二区 | 日韩在线观看一区 | 日韩免费中文字幕 | 青草青草久热精品视频在线观看 | 在线不卡一区 | 黄视频国产| 精品粉嫩超白一线天av | 国产在线不卡视频 | 免费看黄视频网站 | 国内精品视频在线观看 | 国产成人精品免费视频大全最热 | 国产一级精品毛片 | 久久精品亚洲一区二区三区浴池 | 在线色网|