mysql字符串字段的字符集不同可能會導(dǎo)致索引不生效

前言,在探索這個問題之前,我們需要了解 mysql 的一個基本概念。mysql 有三種數(shù)據(jù)類型,數(shù)值型、字符串型、日期型,其中字符串類型需要設(shè)置字符集。如果不設(shè)置,在創(chuàng)建表結(jié)構(gòu)的時候會默認(rèn)使用數(shù)據(jù)庫的字符集。

1.準(zhǔn)備兩個測試表(test_a, test_b)。

CREATE TABLE `test_a` (
  `id` bigint NOT NULL COMMENT '主鍵編號',
  `test_bigint` bigint DEFAULT NULL COMMENT 'utf8的bigint',
  `test_varchar_utf8` varchar(255) DEFAULT NULL COMMENT 'utf8的varchar',
  `test_varchar_utf8mb4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'utf8mb4的varchar',
  PRIMARY KEY (`id`),
  KEY `idx_test_bigint` (`test_bigint`) USING BTREE COMMENT 'test_bigint索引',
  KEY `idx_test_varchar_utf8` (`test_varchar_utf8`) USING BTREE COMMENT 'test_varchar_utf8索引',
  KEY `idx_test_varchar_utf8mb4` (`test_varchar_utf8mb4`) USING BTREE COMMENT 'test_varchar_utf8mb4索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `test_b` (
  `id` bigint NOT NULL COMMENT '主鍵編號',
  `test_bigint` bigint DEFAULT NULL COMMENT 'utf8的bigint',
  `test_varchar_utf8` varchar(255) DEFAULT NULL COMMENT 'utf8的varchar',
  `test_varchar_utf8mb4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'utf8的varchar',
  PRIMARY KEY (`id`),
  KEY `idx_test_bigint` (`test_bigint`) USING BTREE COMMENT 'test_bigint索引',
  KEY `idx_test_varchar_utf8` (`test_varchar_utf8`) USING BTREE COMMENT 'test_varchar_utf8索引',
  KEY `idx_test_varchar_utf8mb4` (`test_varchar_utf8mb4`) USING BTREE COMMENT 'test_varchar_utf8mb4索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

注意:這兩個表的 test_varchar_utf8 字段使用的是utf8字符集,test_varchar_utf8mb4 字段使用的是 utf8mb4 字符集。mysql 的 utf8 使用最大3字節(jié)長度來編碼字符,而 utf8mb4 是使用最大4字節(jié)長度編碼字符,可以理解為 utf8mb4 是 utf8 的超集。

2.插入幾條測試數(shù)據(jù)

在 test_a 表中插入一條數(shù)據(jù),在 test_b 表中插入10條數(shù)據(jù)。多少條數(shù)據(jù)無所謂,只需要能達(dá)到測試效果即可。

INSERT INTO `test_a`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (1, 1, '1', '1');

INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (1, 1, '1', '1');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (2, 2, '2', '2');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (3, 3, '3', '3');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (4, 4, '4', '4');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (5, 5, '5', '5');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (6, 6, '6', '6');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (7, 7, '7', '7');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (8, 8, '8', '8');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (9, 9, '9', '9');
INSERT INTO `test_b`(`id`, `test_bigint`, `test_varchar_utf8`, `test_varchar_utf8mb4`) VALUES (10, 10, '10', '10');

3.準(zhǔn)備測試的 sql 語句,并執(zhí)行

通過 test_a 表的數(shù)據(jù)關(guān)聯(lián)查詢 test_b 表的數(shù)據(jù)。

-- 測試非字符串類型的查詢
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_bigint = ta.test_bigint WHERE ta.test_bigint = 1;
-- 測試字符串類型,使用utf8編碼的字段查詢utf8編碼的字段
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_varchar_utf8 = ta.test_varchar_utf8 WHERE ta.test_varchar_utf8 = '1';
-- 測試字符串類型,使用utf8mb4編碼的字段查詢utf8編碼的字段
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_varchar_utf8 = ta.test_varchar_utf8mb4 WHERE ta.test_varchar_utf8 = '1';
-- 測試字符串類型,使用utf8編碼的字段查詢utf8mb4編碼的字段
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_varchar_utf8mb4 = ta.test_varchar_utf8 WHERE ta.test_varchar_utf8 = '1';
-- 測試字符串類型,使用utf8mb4編碼的字段查詢utf8mb4編碼的字段
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_varchar_utf8mb4 = ta.test_varchar_utf8mb4 WHERE ta.test_varchar_utf8 = '1';

以下是執(zhí)行結(jié)果
結(jié)果1:

圖 3-1.png

結(jié)果2:
圖 3-2.png

結(jié)果3:
圖 3-3.png

結(jié)果4:
圖 3-4.png

結(jié)果5:
圖 3-5.png

4.分析執(zhí)行結(jié)果

4-1.對于 table ta 的查詢始終是走索引的

觀察5個結(jié)果中 table ta 的 key 字段,都是有值的。這說明了在使用 where 條件顯式查詢時,不管目標(biāo)字段的字符集是什么,都會在查詢的過程中默認(rèn)使用該字符集進行編譯,然后就可以走索引。

4-2.對于 table tb 的查詢,會因為字符集的不同導(dǎo)致不走索引

觀察5個結(jié)果中的 table tb 的 key 字段,只有結(jié)果3的 key 字段為 null,再看看結(jié)果3的查詢語句,使用 utf8mb4 編碼的字段查詢 utf8 編碼的字段,聯(lián)想開篇提到的內(nèi)容,utf8mb4 是 utf8 的超集,那我們使用 utf8mb4 編碼的字段去查詢 utf8 編碼的字段會出現(xiàn)不兼容的情況,沒法走索引。

4-3. 對于 table tb 的查詢,如果目標(biāo)字段的字符集可以兼容源字段的字符集,還是可以走索引。

觀察第4條 sql 語句,使用 utf8 編碼的字段查詢 utf8mb4 編碼的字段,再看它的執(zhí)行結(jié)果,table tb的 key 字段是有值的,代表走了索引,聯(lián)想開篇提到的內(nèi)容,utf8mb4 是 utf8 的超集,那么就可以理解為啥字符集不同也可以走索引了。

5.結(jié)論

mysql 字符串字段的字符集不同可能會導(dǎo)致索引不生效

6.建議

為了方便使用,還是統(tǒng)一數(shù)據(jù)庫字符集比較好。
如果沒法修改字段的字符集的話,也可以使用強轉(zhuǎn)字符集的方式,讓字段走索引。我們將第3條sql語句按照下面的方式進行調(diào)整。

6-1.將 utf8 強轉(zhuǎn)成 utf8mb4,不走索引

將 utf8 強轉(zhuǎn)成 utf8mb4 時,需要設(shè)置字符集的排序規(guī)則

EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON CONVERT(tb.test_varchar_utf8 USING utf8mb4) COLLATE utf8mb4_general_ci = ta.test_varchar_utf8mb4 WHERE ta.test_varchar_utf8mb4 = '1';

執(zhí)行結(jié)果如下:

圖 6-1.png.png

從圖 6-1中可以看到,table tb 的 key 字段的值為 null,代表它沒走索引。也就是說,將 utf8 強轉(zhuǎn)成 utf8mb4,并不會走索引。

6-2.將 utf8mb4 強轉(zhuǎn)成 utf8,走索引

將 utf8mb4 強轉(zhuǎn)成 utf8 時,不需要設(shè)置字符集的排序規(guī)則

-- 測試字符串類型,通過utf8mb4查詢utf8,將utf8mb4強轉(zhuǎn)成utf8
EXPLAIN SELECT * FROM test_a ta INNER JOIN test_b tb ON tb.test_varchar_utf8 = CONVERT(ta.test_varchar_utf8mb4 USING utf8) WHERE ta.test_varchar_utf8mb4 = '1';

執(zhí)行結(jié)果如下:

圖 6-2.png

從圖 6-2中可見,table tb 的 key 字段是有值的,這代表它走了索引,完結(jié)撒花!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容