這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
最近公司規範資料庫方面的一些東西,老程式員告訴我mysql裡不準使用null,說是含null值的列沒法索引,對效能影響很大。我第一次聽說這個,對此將信將疑,之前用null並索引並沒有感覺到不妥,可能是資料量小吧。於是我去google了一頓,但是並沒有找到權威的說法。
V2EX——關於null的索引不生效的傳說是真的麼?
by shiny:
由於資料庫的複雜性,以訛傳訛的空間非常大,快趕上中醫養生了。避免使用 NULL 的理由,在高效能MySQL裡有提到一段。建議大家多讀些書,少看網上的奇技淫巧。特意把書翻出來摘錄了下以供參考:
要盡量避免 NULL
要儘可能地把欄位定義為 NOT NULL。即使應用程式無須儲存 NULL(沒有值),也有許多表包含了可空列(Nullable Column),這僅僅是因為它為預設選項。除非真的要儲存 NULL,否則就把列定義為 NOT NULL。
MySQL難以最佳化引用了可空列的查詢,它會使索引、索引統計和值更加複雜。可空列需要更多的儲存空間,還需要在MySQL內部進行特殊處理。當可空列被索引的時候,每條記錄都需要一個額外的位元組,還可能導致 MyISAM 中固定大小的索引(例如一個整數列上的索引)變成可變大小的索引。
即使要在表中儲存「沒有值」的欄位,還是有可能不使用 NULL 的。考慮使用 0、特殊值或Null 字元串來代替它。
把 NULL 列改為 NOT NULL 帶來的效能提升很小,所以除非確定它引入了問題,否則就不要把它當作優先的最佳化措施。然後,如果計劃對列進行索引,就要盡量避免把它設定為可空。
stackoverflow——NULL in MySQL (Performance & Storage)
by Arian Acosta:
Advantage of using NULLS over Empty Strings or Zeros:
1 NULL requires 1 byte
1 Empty String requires 1 byte (assuming VARCHAR)
1 Zero requires 4 bytes (assuming INT)
You start to see the savings here:
8 NULLs require 1 byte
8 Empty Strings require 8 bytes
8 Zeros require 32 bytes
On the other hand, I suggest using NULLs over empty strings or zeros, because they're more organized, portable, and require less space. To improve performance and save space, focus on using the proper data types, indexes, and queries instead of weird tricks.
中英文使用者們對此都是爭論不休,在群裡問了一下老前輩,前輩表示mysql這個東西很玄乎,還是自己寫效能測是最靠譜,說的好那麼開始寫吧。
測試環境
- mysql 5.7
- golang 1.9.2
- mac os 10.13.1
測試思路
- 建兩張類似的表,有id,name,number三列,給name建索引,兩張表區別是一個允許null一個not_null
- 隨機產生若干條資料,包含一些name為空白的資料
- 將相同的資料插入兩個表中,區別空值是一個用null一個用‘’
- 測試SELECT * FROM table WHERE NAME = ? 的效能
測試代碼
https://github.com/win5do/pla...
進入檔案所在目錄運行:go test -v -run=none -bench=. -benchmem
測試結果
mock100w條資料的結果,null完全沒影響
儲存空間佔用也是相同的,null占空間 ‘’同樣占空間
寫測試代碼碰到的坑
為什麼要用golang,因為最近在學習golang,通過寫代碼來加深對語言的理解。而且golang內建測試載入器,做效能測試非常方便。
第一個坑,以前沒用過golang串連資料庫,寫create語句的時候一直提示syntax error,仔細檢查了好幾遍,沒有錯誤啊。後來翻了翻文檔發現串連是加上multiStatements=true參數才能使用多行sql。
第二個坑就是insert語句拼接的太長會導致write pipe broken,mock50000行時沒問題,但100000行就報錯了,解決辦法就是分塊插入,10000行插一下。
第三個坑,就是query之後是要close的
結論
當前穩定版本mysql5.7之中,null對索引沒有影響(比較懶,5.5沒測試,不想去安裝卸載mysql,有空補上)。不要一棒子把null打死,該用就用,null的語義化很好,配合orm使用基本無痛。比如時間列,不用null用個0000-00-00不要太蛋痛。
那句老話:
過早的最佳化是萬惡之源
僅憑中醫理論最佳化更是無可救藥
更重要的,用golang寫代碼真的很cool