最近剛?cè)肼毿鹿?,發(fā)現(xiàn)數(shù)據(jù)庫設(shè)計有點小問題,數(shù)據(jù)庫字段很多沒有NOT NULL,對于強迫癥晚期患者來說,簡直難以忍受,因此有了這篇文章。 基于目前大部分的開發(fā)現(xiàn)狀來說,我們都會把字段全部設(shè)置成 通常,對于默認值一般這樣設(shè)置:
但是,考慮下原因,為什么要設(shè)置成NOT NULL? 來自高性能Mysql中有這樣一段話:
書中的描述說了幾個主要問題,我這里暫且拋開MyISAM的問題不談,這里我針對InnoDB作為考量條件。
默認值對于MySql而言,如果不主動設(shè)置為NOT NULL的話,那么插入數(shù)據(jù)的時候默認值就是NULL。 NULL和NOT NULL使用的空值代表的含義是不一樣,NULL可以認為這一列的值是未知的,空值則可以認為我們知道這個值,只不過他是空的而已。 舉個例子,一張表中的某一條 而對于大多數(shù)程序的情況而言,沒有什么特殊需要非要字段要NULL的吧,NULL值反而會對程序造成比如空指針的問題。 對于現(xiàn)狀大部分使用 值計算聚合函數(shù)不準確 對于NULL值的列,使用聚合函數(shù)的時候會忽略NULL值。 現(xiàn)在我們有一張表,
=失效 對于NULL值的列,是不能使用 與其他值運算 NULL和其他任何值進行運算都是NULL,包括表達式的值也是NULL。
可以再看下下面的例子,任何和NULL進行運算的話得出的結(jié)果都會是NULL,想象下你設(shè)計的某個字段如果是NULL還不小心進行各種運算,最后得出的結(jié)果。。。 distinct、group by、order by 對于 其他問題 表中只有一條有名字的記錄,此時查詢名字 索引問題為了驗證NULL字段對索引的影響,分別對 關(guān)于網(wǎng)上很多說如果NULL那么不能使用索引的說法,這個描述其實并不準確,根據(jù)引用官方文檔[3]里描述,使用is NULL和范圍查詢都是可以和正常一樣使用索引的,實際驗證的結(jié)果好像也是這樣,看以下例子。 然后接著我們往數(shù)據(jù)庫中繼續(xù)插入一些數(shù)據(jù)進行測試,當NULL列值變多之后發(fā)現(xiàn)索引失效了。 我們知道,一個查詢SQL執(zhí)行大概是這樣的流程: 首先連接器負責(zé)連接到指定的數(shù)據(jù)庫上,接著看看查詢緩存中是否有這條語句,如果有就直接返回結(jié)果。 如果緩存沒有命中的話,就需要分析器來對SQL語句進行語法和詞法分析,判斷SQL語句是否合法。 現(xiàn)在來到優(yōu)化器,就會選擇使用什么索引比較合理,SQL語句具體怎么執(zhí)行的方案就確定下來了。 最后執(zhí)行器負責(zé)執(zhí)行語句、有無權(quán)限進行查詢,返回執(zhí)行結(jié)果。 從上面的簡單測試結(jié)果其實可以看到,索引列存在NULL就會存在書中所說的導(dǎo)致優(yōu)化器在做索引選擇的時候更復(fù)雜,更加難以優(yōu)化。 存儲空間數(shù)據(jù)庫中的一行記錄在最終磁盤文件中也是以行的方式來存儲的,對于InnoDB來說,有4種行存儲格式: InnoDB的默認行存儲格式是 變長字段長度列表:有多個字段則以逆序存儲,我們只有一個字段所有不考慮那么多,存儲格式是16進制,如果沒有變長字段就不需要這一部分了。 NULL值列表:用來存儲我們記錄中值為NULL的情況,如果存在多個NULL值那么也是逆序存儲,并且必須是8bit的整數(shù)倍,如果不夠8bit,則高位補0。1代表是NULL,0代表不是NULL。如果都是NOT NULL那么這個就存在了。 ROW_ID:一行記錄的唯一標志,沒有指定主鍵的時候自動生成的ROW_ID作為主鍵。 TRX_ID:事務(wù)ID。 ROLL_PRT:回滾指針。 最后就是每列的值。 為了說明清楚這個存儲格式的問題,我弄張表來測試,這張表只有 可變字段長度列表: NULL值列表:因為存在允許為NULL的列,所以 其他字段我們暫時不管他,最后第一條記錄的結(jié)果就是,當然這里我們就不考慮編碼之后的結(jié)果了。 這樣就是一個完整的數(shù)據(jù)行數(shù)據(jù)的格式,反之,如果我們把所有字段都設(shè)置為NOT NULL,并且插入一條數(shù)據(jù) 雖然我們發(fā)現(xiàn)NULL本身并不會占用存儲空間,但是如果存在NULL的話就會多占用一個字節(jié)的標志位的空間。
|
|