MySQL 那些常見的錯誤設計規範,你都知道嗎

2022-09-21 16:15:09 字數 3776 閱讀 7969

依託於網際網路的發達,我們可以隨時隨地利用一些等車或坐地鐵的碎片時間學習以及了解資訊。同時發達的網際網路也方便人們能夠快速分享自己的知識,與相同愛好和需求的朋友們一起共同討論。

但是過於方便的分享也讓知識變得五花八門,很容易讓人接收到錯誤的資訊。這些錯誤最多的都是因為技術發展迅速,而且沒有空閒時間去及時更新已經發布的內容所導致。為了避免給後面學習的人造成誤解,我們今天來看一看 mysql 設計規範中幾個常見的錯誤例子。

錯誤的設計規範:主鍵建議使用自增 id 值,不要使用 uuid,md5,hash,字串作為主鍵

這個設計規範在很多文章中都能看到,自增主鍵的優點有占用空間小,有序,使用起來簡單等優點。

下面先來看看自增主鍵的缺點:

因為自增值是在 mysql 服務端產生的值,需要有一把自增的 ai 鎖保護,若這時有大量的插入請求,就可能存在自增引起的效能瓶頸。比如在 mysql 資料庫中,引數 innodb_autoinc_lock_mode 用於控制自增鎖持有的時間。雖然,我們可以調整引數 innodb_autoinc_lock_mode 獲得自增的最大效能,但是由於其還存在其它問題。因此,在併發場景中,更推薦 uuid 做主鍵或業務自定義生成主鍵。

我們可以直接在 mysq l使用 uuid() 函式來獲取 uuid 的值。

mysql> select uuid();

+--------------------------------------+

| uuid() |

+--------------------------------------+

| 23ebaa88-ce89-11eb-b431-0242ac110002 |

+--------------------------------------+

1 row in set (0.00 sec)

需要特別注意的是,在儲存時間時,uuid 是根據時間位逆序儲存, 也就是低時間低位存放在最前面,高時間位在最後,即 uuid 的前 4 個位元組會隨著時間的變化而不斷「隨機」變化,並非單調遞增。而非隨機值在插入時會產生離散 io,從而產生效能瓶頸。這也是 uuid 對比自增值最大的弊端。

為了解決這個問題,mysql 8.0 推出了函式 uuid_to_bin,它可以把 uuid 字串:

下面我們將之前的 uuid 字串 23ebaa88-ce89-11eb-b431-0242ac110002 通過函式 uuid_to_bin 進行轉換,得到二進位制值如下所示:

mysql> select uuid_to_bin('23ebaa88-ce89-11eb-b431-0242ac110002',true) as uuid_bin;

+------------------------------------+

| uuid_bin |

+------------------------------------+程式設計客棧

| 0x11ebce8923ebaa88b4310242ac110002 |

+------------------------------------+

1 row in set (0.01 sec)

除此之外,mysql 8.0 也提供了函式 bin_to_uuid,支援將二進位制值反轉為 uuid 字串。

雖然 mysql 8.0 版本之前沒有函式 uuid_to_bin/bin_to_uuid,還是可以通過自定義函式的方式解決。應用層的話可以根據自己的程式語言編寫相應的函式。

當然,很多同學也擔心 uuid 的效能和儲存占用的空間問題,這裡我也做了相關的插入效能測試,結果如下表所示:

可以看到,mysql 8.0 提供的排序 uuid 效能最好,甚至比自增 id 還要好。此外,由於 uuid_to_bin 轉換為的結果是16 位元組,僅比自增 id 增加 8 個位元組,最後儲存占用的空間也僅比自增大了 3g。

而且由於 uuid 能保證全域性唯一,因此使用 uuid 的收益遠遠大於自增 id。可能你已經習慣了用自增做主鍵,但是在併發場景下,更推薦 uuid 這樣的全域性唯一值做主鍵。

當然了,uuid雖好,但是在分布式場景下,主鍵還需要加入一些額外的資訊,這樣才能保證後續二級索引的查詢效率,推薦根據業務自定義生成主鍵。但是在併發量和資料量沒那麼大的情況下,還是推薦使用自增 uuid 的。大家更不要以為 uuid 不能當主鍵了。

錯誤的設計規範:同財務相關的金額類資料必須使用 decimal 型別 由於 float 和 double 都是非精準的浮點數型別,而 decimal 是精準的浮點數型別。所以一般在設計使用者餘額,商品**等金融類字段一般都是使用 decimal 型別,可以精確到分。

但是在程式設計客棧海量網際網路業務的設計標準中,並不推薦用 decimal 型別,而是更推薦將 decimal 轉化為整型型別。 也就是說,金融型別更推薦使用用分單位儲存,而不是用元單位儲存。如1元在資料庫中用整型型別 100 儲存。

下面是 bigint 型別的優點:

錯誤的設計規範:避免使用 enum 型別

在以前開發專案中,遇到使用者性別,商品是否上架,評論是否隱藏等字段的時候,都是簡單的將字段設計為 tinyint,然後在字段裡備註 0 為什麼狀態,1 為什麼狀態。

這樣設計的問題也比較明顯:

這種固定選項值的字段,推薦使用 enum 列舉字串型別,外加 sql_mode 的嚴格模式

在mysql 8.0.16 以後的版本,可以直接使用check約束機制,不需要使用enum列舉字段型別

而且我們一般在定義列舉值的時候使用"y","n"等單個字元,並不會占用很多空間。但是如果選項值不固定的情況,隨著業務發展可能會增加,才不推薦使用列舉字段。

錯誤的設計規範:限制每張表上的索參數量,一張表的索引不能超過 5 個

mysql 單錶的索引沒有個數限制,業務查詢有具體需要,建立即可,不要迷信個數限制

錯誤的設計規範:避免使用子查詢

其實這個規範對老版本的 mysql 來說是對的,因為之前版本的 mysql 資料庫對子查詢優化有限,所以很多 oltp 業務場合下,我們都要求**業務盡可能不用子查詢。

然而,mysql 8.0 版本中,子查詢的優化得到大程式設計客棧幅提公升,所以在新版本的mysql中可以放心的使用子查詢。

子查詢相比 join 更易於人類理解,比如我們現在想檢視2023年沒有發過文章的同學的數量

select count(*)

from user

where id not in (

select user_id

from blog

where publish_time >= "2020-01-01" and publish_time <= "2020-12-31"

)可以看到,子查詢的邏輯非常清晰:通過 not in 查詢文章表的使用者有哪些。

如果用 left join 寫

select count(*)

from user left join blog

on user.id = blog.user_id and blog.publish_time >= "2020-01-01" and blog.publish_time <= "2020-12-31"

where blog.user_id is null;

可以發現,雖然 left join 也能完成上述需求,但不容易理解。

我們使用 explain檢視兩條 sql 的執行計畫,發現都是一樣的

通過上圖可以很明顯看到,不論是子查詢還是 left join,最終都被轉換成了left hash join,所以上述兩條 sql 的執行時間是一樣的。即,在 mysql 8.0 中,優化器會自動地將 in 子查詢優化,優化為最佳的 join 執行計畫,這樣一來,會顯著的提公升效能。

MySql常見錯誤

總結一些使用mysql過程中遇到的錯誤以及解決辦法 建立表時錯誤 error121 這是外關鍵字名字重複的錯誤,即使是在不同的表中,外關鍵字的名字也不能重複。error150 這類錯誤包括3種情況 1.外來鍵和被引用外來鍵型別不一樣,比如integer和double 2.找不到要被引用的列 3.表的...

mysql常見錯誤

mysql也符合sql語句的格式,所以,我們對其進行不當的操作時,mysql就會報出一系列的錯誤。每個錯誤對應著乙個錯誤編碼,當出現了錯誤別緊張,我們先看錯誤寫的是什麼意思,然後才對症下藥地解決掉。1062錯誤 解決方案 我將tag表的tname欄位設定為unique屬性,也就是tname具有唯一性...

mysql常見錯誤

error 1044 42000 access denied for user localhost to database mysql 原來是因為mysql資料庫的user表裡,存在使用者名為空的賬戶即匿名賬戶,導致登入的時候是雖然用的是root,但實際是匿名登入的,通過錯誤提示裡的可以看出來。解決...