MySQL問題兩則

2021-02-20 沃趣技術

作者  沈剛 · 沃趣科技資料庫技術專家

出品  沃趣科技

1 | 前言

近期在給開發小夥伴解決問題的時候,收集了兩個個人覺得比較有意思的問題給大家分享一下。一個是在執行ALTER TABLE ADD COLUMN語句時,報了Duplicate entry的錯誤;另一個是關於在MySQL中正確存取emoji表情的問題。

2 | ALTER TABLE ADD COLUMN報Duplicate entry錯誤2.1 問題描述

某日系統上線,接到開發小夥伴電話說在上線時,執行一個增加欄位的DDL語句腳本時,報錯了,錯誤如下:
ERROR 1062 (23000) at line 1: Duplicate entry 'UR000021426347' for key 'T_CAP_CUST_MIDDLE_INFO_UNIQ_INDEX'
根據錯誤提示的條件去資料庫中查詢卻只能查到一條記錄,並沒有重複記錄。DDL腳本無法執行,影響後續上線步驟了。當時由於不在現場,了解到的信息只有:

DDL語句腳本中只有兩條DDL語句,且都是添加欄位的語句。

腳本正常運行只需要40-70秒。

當時並不是停業窗口。

考慮到資料庫版本為5.6.34,添加欄位並不會阻塞DML操作,讓開發小夥伴再運行一次試試,結果這次執行成功了,並沒有報衝突的錯誤。線上問題順利解決,具體原因得線下分析了。雖說解決問題是主要矛盾,但是搞清楚問題原因有著更深層次的意義。

2.2 原因定位

下面就是到了尋找問題原因的時候了,為什麼同樣的DDL語句腳本第一次執行的時候報了Duplicate entry錯誤,第二次卻順利運行了。其實問題原因很好找,打開Google,輸入關鍵字mysql alter table add column duplicate entry,搜索結果中很多關鍵字完全匹配的連結,說明很多人遇到過相同問題。搜索結果中一眼就看到一個連結MySQL Bugs:#76895:Adding new column OR Drop column causes duplicate PK error,看到MySQL Bug就莫名興奮。通過該Bug連結了解到該問題是Online DDL的一個限制問題,官方認為該問題是一種限制,並不是Bug,所以目前為止還沒有得到解決。

When running an online DDL operation, the thread that runs the ALTER TABLE statement applies an online log of DML operations that were run concurrently on the same table from other connection threads. When the DML operations are applied, it is possible to encounter a duplicate key entry error (ERROR 1062 (23000): Duplicate entry), even if the duplicate entry is only temporary and would be reverted by a later entry in the online log. This is similar to the idea of a foreign key constraint check in InnoDB in which constraints must hold during a transaction.
 解釋一下就是當執行Oline DDL操作時,MySQL實際上是將DML緩存(該緩存大小由變量 innodb_online_alter_log_max_size控制,默認128M)起來,等DDL執行完成後再將緩存中的DML重新應用到表上。如果有別的線程執行了DML操作,在DDL完成後,應用DML時,可能會出現duplicate entry錯誤。

2.3 實驗驗證

上面通過Google找到了理論上可能能解釋問題的原因描述,但是還沒有實際驗證,所以接下來就是線下復現環節。先去找開發同事問了下線上報錯的表只有一種操作insert into ... on duplicate key...,且報Duplicate entry的欄位上有唯一索引。如果沒有衝突的記錄則插入,否則就更新。那麼驗證測試步驟也比較簡單了,找一張測試表,執行ALTER TABLE ADD COLUMN操作,並同時執行insert into...on duplicate key...操作,觀察DDL語句是否會有報錯。

2.3.1 實驗環境

col1欄位的值與id欄位的值是一致的,test表中共有1600W+條記錄。

執行DDL同時執行insert into ... on duplicate key...操作。

 序號

          會話1

         會話2

1

select * from test limit 10

-

2

alter table test add column address varchar(50) default 'HangZhou';

3

insert into test (name,col1) values('san.zhang',7) on duplicate key update col1=29;
ERROR 1062 (23000): Duplicate entry '29' for key 'col1'

4

DDL執行報錯,檢查發現DDL執行失敗
ERROR 1062 (23000): Duplicate entry '7' for key 'col1'

5

順利的復現了線上的問題現象,那說明當時線上就是因為DML更新了相同的唯一屬性欄位鍵值導致DDL執行失敗,報錯。測試過程中想到insert into... on duplicate key...不行,那麼replace into 會不會也一樣導致問題呢,於是就同樣對replace into語句進行了測試。

 序號
            會話1
       會話2

1

select * from test limit 10

-

-

alter table test add column address varchar(50) default 'HangZhou';replace into test(id,name,col1) values(1,'gang.shen',13);
Query OK, 3 rows affected (0.00 sec)

-

-

DDL執行報錯,檢查發現DDL執行失敗
ERROR 1062 (23000): Duplicate entry '13' for key 'col1'select *  from test where col1=13

-

2.4 小結

在這對這個問題做幾點總結:

問題原因:Oline DDL的原理簡單一點理解就是將DML操作緩存起來,等到DDL執行完成後重新應用緩存中的DML語句,如果在Oline DDL執行過程中,DML操作產生了Duplicate entry錯誤,並不會直接影響DDL操作,而是在DDL執行完成最終應用DML時報錯,導致DDL執行失敗。關於Oline DDL執行步驟可以參考:https://yq.aliyun.com/articles/282290。

在MySQL Bug網站上,官方人員回復該現象並不是Bug,而是一種限制。但是個人認為是可以做一些改善的,因為在測試insert into ... on duplicate key...以及update和insert語句時,對於執行DML操作的客戶端已經直接返回報錯了,但是從現象上看MySQL仍然將報錯的DML語句放到了Oline DDL的緩存中,如果直接將報錯語句從緩存中去除則不會影響DDL的正常執行。這只是個人簡單認為,深入的話需要可以通過代碼去確認。

Google是位好老師。

3 | MySQL存取emoji的正確姿勢3.1 問題描述

開發小夥伴在測試環境測試過程中,需要往MySQL資料庫中插入emoji表情,但是發現一套測試環境可以插入成功,另一套測試環境不行,且插入成功的那套環境資料庫將數據查詢出來是????亂碼。經過與開發確認後了解到以下幾點信息:

那其實需要解決的問題其實是兩個問題:1、5.6版本下emoji存取亂碼問題 2、5.7版本emoji無法插入的問題。

3.2 實驗驗證3.2.1 MySQL-5.6 emoji存取亂碼問題。

我們先來看5.6版本下emoji存儲亂碼的問題,理論上從資料庫角度考慮,欄位字符集已經設置為utf8mb4,應用使用的是utf8字符集連接,插入emoji需要utf8mb4字符集,多半是連接字符集設置的問題。大膽假設已經完成,接下來就是小心求證的過程。

實驗環境

使用utf8字符集連接MySQL,並檢查參數設置。

表情的確能插入資料庫,但是insert時有兩條warning記錄提示是invalid的字符串,並且select查詢出來的數據也是一樣????亂碼。使用utf8mb4字符集插入emoji表情並查詢。

可以看到連接使用utf8mb4字符集插入、查詢emoji表情都是正常的,插入時也沒有warning提示,那說明emoji亂碼的問題就是因為連接字符集設置不合理導致的。
3.2.2 MySQL-5.7 emoji無法插入的問題

定位了亂碼的問題,再來看看emoji無法插入的問題。

實驗環境

資料庫版本:5.7.22

sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

default-character-set=utf8

插入的時候直接報錯了,同樣的字符集,同樣的語句,5.6版本下emoji就能插入,5.7就直接報錯了。5.6->5.7資料庫版本問題,如果踩坑比較多的話,還是比較容易聯想到是不是sql_mode參數的問題,因為5.7中sql_mode參數默認值多了很多項,對語句的限制加強了很多。

mysql> show variables like '%sql_mode%'\G
*************************** 1. row ***************************
Variable_name: sql_mode
    Value: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)

查看官方文檔關於各個sql_mode選項的值,找到了懷疑對象STRICT_TRANS_TABLES,表示開啟嚴格模式,嚴格模式下如果插入的數據不在範圍之類會報錯中斷語句。

修改sql_mode參數值,再次插入emoji表情測試。

mysql> set session sql_mode='ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)

可以看到修改sql_mode值後,現象與5.6版本下一致。

使用utf8mb4字符集插入emoji表情並查詢。

3.3 小結

在此對emoji問題做一下小結:

MySQL中存儲emoji不僅需要將表結構中欄位字符集設置為utf8mb4,還需要考慮連接字符集的問題。

建議資料庫使用5.7.22及以上版本,並且sql_mode開啟嚴格模式,這樣當數據出現異常可以及時發現。

在sql_mode開啟嚴格模式的情況下,應用端連接資料庫也需要調整為utf8mb4字符集才可以正常插入emoji表情。

| 作者簡介沈 剛·沃趣科技資料庫技術專家熟悉MySQL資料庫運行機制,豐富的資料庫及複製架構故障診斷、性能調優、資料庫備份恢復及遷移經驗。

相關焦點

  • MySQL裡面的group by問題淺析
    mysql> select  backup_date ,count(*) piece_no  from redis_backup_result;+---++| backup_date | piece_no |+---++| 2018-08-14  |    40906 |+---++1 row in
  • MySQL Time Zone 時區問題
    MySQL Server時區詳解查看資料庫當前的時區設置:mysql> show variables like最初,會話變量從全局time_zone變量獲取其值,但客戶端可以使用此語句更改其自己的時區:mysql> SET time_zone = timezone;當前會話時區設置會影響到對區域敏感的是時間值的顯示和存儲。
  • 搞定MySQL安裝難安裝貴問題
    背景本方案解決了windows下安裝MySQL過程繁瑣的問題。是真正的免安裝綠色方法,不用配環境變量,不用執行install命令,不用配置my.ini文件。>命令成功生成data目錄,同時生成無密碼的root用戶啟動MySQLbin下執行mysqld--console設置root密碼執行mysql-uroot-p連入資料庫,密碼不用輸入,直接按回車進入mysql>
  • 第09期:有關 MySQL 字符集的亂碼問題
    那麼數據亂碼問題在這兒顯得就非常簡單了,或許說可能不會出現這樣的問題。數據之所以會亂碼,在 MySQL 裡無非有以下幾類情況:在數據寫入到表的過程中轉碼失敗,資料庫端也沒有進行恰當的處理,導致存放在表裡的數據亂碼。針對這種情況,前幾篇文章介紹過客戶端發送請求到服務端。其中任意一個編碼不一致,都會導致表裡的數據存入不正確的編碼而產生亂碼。
  • mysqldump備份時的數據一致性問題
    原標題:mysqldump備份時的數據一致性問題作者:魏新平,知數堂優秀校友。在日常運維當中,經常會用到mysqldump。使用mysqldump導出數據的時候,我們最關心的問題之一就是表的一致性。簡單的說就是所有表是不是同一時間的數據和結構。
  • 幾個常見而嚴重的 MySQL 問題分析 | 運維進階
    很多時候發生資料庫報錯時,不一定就是資料庫的問題,不一定非得急著呼叫資料庫人員解決。我們要形成這樣一種意識,我們不只是寫應用的,我們是寫金融系統的,我們理應具備一定的問題排查解決能力。本文將抽取幾個常見而嚴重的MySQL問題進行分析,並給出深度解答。藉以大家幫助思考。
  • Mysql資料庫update操作死鎖問題分析
    簡介問題是這樣的,我負責的一個線上模塊的功能是給裝有我們產品APP的手機設備根據業務功能打上特殊的推送標籤。每個設備有多個不同的標籤,每個標籤下包括很多設備。由於用戶在使用app時會觸發很多邏輯,隨時都可能有對標籤的增刪。
  • Mysql分區(PARTITION)實戰--解決Mysql大量刪除數據效率低的問題
    但是應用只需要保留1個小時內的數據,時間戳超過一個小時的數據要刪除掉。剛開始的時候採用一條一條刪除的方法,每5分鐘刪除一次,每次清除一個小時前的數據。但是遇到一個問題是,經常返回錯誤「Lock wait timeout exceeded; try restarting transaction」。發現mysql的netmon_history這張表被鎖定了。
  • mysql中變相解決排名問題,有點厲害
    為大家熟知的關係型資料庫有mysql、SQLServer、oracle、DB2等。它們的sql語句其實是有細微的差別的,不要想著有些語句在SQLServer上可以用就可以在mysql上使用。今天老韓就來講一下其中的一個知識點,mysql中如何排名。
  • MySQL sql_mode 說明(及處理一起 sql_mode 引發的問題)
    不說開發的問題,好端端的mysql怎麼突然就部分表寫入失敗呢?根據上面的問題很快能猜到是 sql_mode 問題: NOT NULL 列沒有默認值但代碼裡也沒給值,在非嚴格模式下,int列默認為0,string列默認為』』了,所以不成問題;但在嚴格模式下,是直接返回失敗的。
  • mysql安裝圖解 mysql圖文安裝教程
    打開下載的mysql安裝文件mysql-5.0.27-win32.zip,雙擊解壓縮,運行「setup.exe」,出現如下界面mysql安裝嚮導啟動,按「Next」繼續選擇安裝類型,有「Typical(默認)」、「Complete(完全)」、「Custom(用戶自定義)」三個選項,我們選擇「Custom」,有更多的選項,也方便熟悉安裝過程:在「Developer
  • MySQL mysqldump 數據導出詳解
    mysqldump  -uroot -p --all-databases --apply-slave-statements--character-sets-dir字符集文件的目錄mysqldump  -uroot -p --all-databases  --character-sets-dir=/usr/local/mysql/share/mysql
  • mysql 矩陣類型專題及常見問題 - CSDN
    介紹mysql進入mysqlmysql -u root -p-u root 其中 root為用戶名連接資料庫在連接資料庫之前,確保mysql已經打開了import pymysql# pymysql使用connect()方法連接資料庫db = pymysql.connect(host='127.0.0.1',user='root',password='123456',port=3306)# 獲取mysql的操作遊標cursor = db.cursor()# 使用execute
  • MySQL - mysqldump常用命令 - linux運維菜
    導出數據1、備份全部資料庫的數據和表結構mysqldump -uroot -ppassword -A >all.sql2、只備份表結構,不備份數據mysqldump -uroot -ppassword -A -d > database.sql3、只備份資料庫,不備份表結構mysqldump -uroot -ppassword -A -t > data.sql
  • MySQL教程之MySQL定時備份資料庫
    一、MySQL數據備份1.1、 mysqldump命令備份數據在MySQL中提供了命令行導出資料庫數據以及文件的一種方便的工具mysqldump,我們可以通過命令行直接實現資料庫內容的導出dump,首先我們簡單了解一下mysqldump命令用法:
  • MySQL與容器
    -v [path on host machine]/my.cnf:/etc/my.cnf -v [path on host machine]/datadir:/var/lib/mysql -d mysql:latest關於詳細的安裝方法可以參照:https://dev.mysql.com/doc/refman/8.0/en/linux-installation-docker.html
  • mysql實現php函數explode功能mysql_explode
    我article表中的記錄如下,因為多個關鍵詞存放在一個欄位上,不利於做排序統計操作,例如我想要統計哪個關鍵詞的數量最多就是個大問題了:id keywords1 九陽神功,萬川歸海,橫掃千軍,乾坤大挪移2 殺破狼,落日十三劍
  • 學會用 Mysql show processlist 排查問題
    mysql show full processlist 查看當前線程處理情況事發現場每次執行看到的結果應該都有變化,因為是實時的,
  • 讓MySQL與OpenOffice共舞
    -3.23.55-pc-linux-i686.tar.gz$ln -s mysql.3.23.55-pc mysql $cd mysql$scripts/mysql_install_db$chown -R root .
  • MySQL內存不釋放分析
    問題分析場景1 使用sysbench壓測資料庫場景2 load 一個很大事務的insert語句問題突破測試jemalloc場景1使用sysbench