mysql的這幾個坑你踩過沒?真是防不勝防

1、前言

對於從事互聯網開發的同窗來講,mysql可謂是再熟悉不過的了。不管是DBA、開發或測試,基本上每天要跟它打交道,不少同窗可能已經身經百戰了。可是,筆者遇到過的這些坑不知道大家都經歷過沒?mysql

2、有符號和無符號

之前咱們公司在項目開發之初制定開發規範時,對mysql的int類型字段定義成有符號,仍是無符號問題專門討論過。sql

觀點一:數據庫

對於可以肯定裏面存的值必定是正數的字段,定義成UNSIGNED無符號的,能夠節省一半的存儲空間。建立無符號字段的語句以下:函數

create table test_unsigned(a int UNSIGNED, b int UNSIGNED);

觀點二:測試

建議都定義成有符號的,使用起來比較簡單,mysql默認int類型就是有符號的,建立有符號字段的語句以下:編碼

create table test_signed(a int);
insert into test_signed values(-1);

執行結果:spa

attachments-2020-12-OfrJdymp5fd9bc84b3cf2.png

在字段a中插入-1,咱們看到是能夠操做成功的。server

這兩個方案,通過咱們激烈討論以後,選擇了使用有符號定義int類型字段。爲何呢?blog

create table test_unsigned(a int UNSIGNED, b int UNSIGNED);
insert into test_unsigned values(1,2);

先建立test_unsigned表,裏面包含兩個無符號字段a和b,再插入一條數據a=1,b=2排序

select b - a from test_unsigned;

沒有問題,返回1

可是若是sql改爲這樣:

select a - b from test_unsigned;

執行結果:

attachments-2020-12-EX8SEQjt5fd9bc8e559ac.png

報錯了。。。

因此,在使用無符號字段時,千萬要注意字段相減出現負數的坑,建議仍是使用有符號字段,避免沒必要要的問題。

3、自動增加

建過表的同窗都知道,對於表的主鍵能夠定義成自動增加的,這樣一來,就能夠交給數據庫本身生成主鍵值,而無需在代碼中指定,並且生成的值是遞增的。通常狀況下,建立表的語句以下:

create table test_auto_increment (a int auto_increment primary key);

但若是改爲這樣的會怎樣?

create table test_auto_increment (a int auto_increment);

執行結果:

attachments-2020-12-A9akaPnx5fd9bc98cf93c.png

報錯了。。。

截圖中沒有所有顯示出來,完整的提示語是這樣的:

1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key, Time: 0.006000

意思是自動增加字段,必須被定義成key,因此咱們須要加上primary key。

此外,還有一個有趣的實驗:

insert into test_auto_increment(a) values (null),(50),(null),(8),(null);

你們猜猜執行結果會是什麼樣的?

attachments-2020-12-aolRYDFa5fd9bca309c1a.png

第一個null插入1,而後按真實的數字大小排序後插入,後面兩個null,是在最大的數字上面加1。

再看看這條sql主鍵中插入負數,能執行成功嗎?

insert into test_auto_increment values(-3);

答案是能夠,主鍵能夠插入負數。

attachments-2020-12-3AN717ZH5fd9bcac05a1a.png

還有這條sql呢,主鍵中插入0?

insert into test_auto_increment values(0);

執行結果:

attachments-2020-12-Eq2UW5PO5fd9bcb4d19ff.png

也能夠執行成功,可是沒有插入數據

4、字段長度

咱們在建立表的時候,給字段定義完類型以後,緊接着須要指定字段的長度,好比:varchar(20),biginit(20)等。那麼問題來了,varchar表明的是字節長度,仍是字符長度呢?

create table test_varchar(a varchar(20));
insert into test_varchar values('蘇三說技術');
select length(a),CHARACTER_LENGTH(a) from test_varchar;

執行後的結果:

attachments-2020-12-XGpVl3rp5fd9bcbe59df6.jpg

咱們看到中文的5個字length函數統計後長度爲15,表明佔用了15個字節,而使用charcter_length函數統計長度是5,表明有5個字符。因此varchar表明的是字符長度,由於有些複雜的字符或者中文,一個字節表示不了,utf8編碼格式的一箇中文漢字佔用3個字節。不一樣的數據庫編碼格式,佔用不一樣的字節數對照表以下:

attachments-2020-12-mtI5bybQ5fd9bccaea9c2.png

mysql除了varchar和char是表明字符長度以外,其他的類型都是表明字節長度。

int(n) 這個n表示什麼意思呢?從一個列子出發:

create table test_bigint (a bigint(4) ZEROFILL);
insert into test_bigint values(1);
insert into test_bigint values(123456);
select * from test_bigint;

ZEROFILL表示長度不夠填充0

執行結果:

attachments-2020-12-jV2rMw6f5fd9bcd47b78f.png

mysql經常使用數字類型字段佔用字節數對照表:

attachments-2020-12-9QUSlIEC5fd9bcdbeea9e.png

從表中能夠看出bigint實際長度是8個字節,可是咱們定義的a顯示4個字節,因此在不滿4個字節時前面填充0。滿了4個字節時,按照實際的長度顯示,好比:123456。可是,須要注意的是,有些mysql客戶端即便滿了4個字節,也可能只顯示4個字節的內容,好比顯示:1234。

因此bigint(4),這裏的4表示顯示的長度爲4個字節,實際長度仍是8個字節。

5、忽略大小寫

咱們知道在英文字母中有大小寫問題,好比:a 和 A 是同樣的嗎?咱們認爲確定是不同的,可是數據庫是如何處理的呢?

create table test_a(a varchar(20));
insert into test_a values('a');
insert into test_a values('A');
select * from test_a where a = 'a';

執行結果是什麼呢?

attachments-2020-12-MTgFnNJS5fd9bce5d7a6b.png

本覺得只會返回a,可是實際上把A也返回了,這是爲何呢?

attachments-2020-12-zcqrnsSW5fd9bcecacbd9.png

該表默認的Collation是utf8_general_ci,這種Collation會忽略大小寫,因此纔會出現查詢小寫字母a的值,意外把大寫字母A的值也查詢出來了。

那麼若是咱們只想查詢出小寫a的值該怎麼辦?先看看mysql支持哪些Collation?

show collation;

attachments-2020-12-2h0H0Qmn5fd9bcf4a91da.png

從上圖中咱們能夠找到utf8_bin,這個表示二進制格式的數據,咱們設置成種類型的試試。

attachments-2020-12-61PdNb8j5fd9bcff955f6.png

修改一下字段類型

ALTER TABLE test_a MODIFY COLUMN a VARCHAR(20BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;

再查看一下數據

select * from test_a where a = 'a';

執行結果:

attachments-2020-12-0DqPEwBA5fd9bd0cc1c5a.png

果真,結果對了。

6、特殊字符

筆者以前作項目的時候,提供過一個留言的功能,結果客戶端用戶輸入了一個emoji表情,直接致使接口報錯了。

attachments-2020-12-6rc33gMR5fd9bd1529868.png

最後定位緣由是因爲當時數據庫和表的字符編碼都是用的utf8,mysql的utf8編碼的一個字符最多3個字節,可是一個emoji表情爲4個字節,因此utf8不支持存儲emoji表情。

該如何解決這個問題呢?

將字符編碼改爲utf8mb4,utf8mb4最多能有4字節,不過,在mysql5.5.3或更高的版本才支持。

在mysql 的配置文件 my.cnf 或 my.ini 配置文件中修改以下:

[client]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server     = utf8mb4_general_ci

重啓MySQL,而後使用如下命令查看編碼,應該所有爲utf8mb4,這是修改整個數據庫的編碼方式。

SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';ji

結果爲:

attachments-2020-12-PF9jBUiK5fd9bd1e78eda.png

也能夠單獨修改某張表的編碼方式:

alter table test_a convert to character set utf8mb4 collate utf8mb4_bin;

以及修改某個字段的編碼方式:

ALTER TABLE test_a CHANGE a a VARCHAR(20CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

此外,建議同窗們在建立數據庫和表的時候字符編碼都定義成utf8mb4,避免一些沒必要要的問題。

 

相關文章
相關標籤/搜索