自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

數(shù)據(jù)庫表字段為何默認(rèn)為 NOT NULL?

數(shù)據(jù)庫 其他數(shù)據(jù)庫
假設(shè)有一張表,只有一個(gè)字段允許為 NULL,其他字段都是 NOT NULL。當(dāng)存儲(chǔ)一條記錄時(shí),如果這個(gè)可空字段的值為 NULL,那么就需要在 NULL 值列表中進(jìn)行標(biāo)記,并且在記錄頭部占用一個(gè)額外的字節(jié)作為標(biāo)志位。隨著記錄數(shù)量的增加,這種額外的存儲(chǔ)空間占用也會(huì)逐漸累積。

目前大部分的開發(fā)現(xiàn)狀來說,我們都會(huì)把字段全部設(shè)置成 NOT NULL 并且給默認(rèn)值的形式。

最近在 Review 代碼時(shí)候,仍然偶爾發(fā)現(xiàn)數(shù)據(jù)庫字段很多沒有設(shè)置 NOT NULL,為什么要設(shè)置成 NOT NULL 呢?

來自「高性能MySQL」中有這樣一段話:

盡量避免NULL
很多表都包含可為NULL(空值)的列,即使應(yīng)用程序并不需要保存NULL也是如此,這是因?yàn)榭蔀镹ULL是列的默認(rèn)屬性。通常情況下最好指定列為NOT NULL,除非真的需要存儲(chǔ)NULL值。
如果查詢中包含可為NULL的列,對(duì)MySql來說更難優(yōu)化,因?yàn)榭蔀镹ULL的列使得索引、索引統(tǒng)計(jì)和值比較都更復(fù)雜。可為NULL的列會(huì)使用更多的存儲(chǔ)空間,在MySql里也需要特殊處理。當(dāng)可為NULL的列被索引時(shí),每個(gè)索引記錄需要一個(gè)額外的字節(jié),在MyISAM里甚至還可能導(dǎo)致固定大小的索引(例如只有一個(gè)整數(shù)列的索引)變成可變大小的索引。
通常把可為NULL的列改為NOT NULL帶來的性能提升比較小,所以(調(diào)優(yōu)時(shí))沒有必要首先在現(xiàn)有schema中查找并修改掉這種情況,除非確定這會(huì)導(dǎo)致問題。但是,如果計(jì)劃在列上建索引,就應(yīng)該盡量避免設(shè)計(jì)成可為NULL的列。
當(dāng)然也有例外,例如值得一提的是,InnoDB使用單獨(dú)的位(bit)存儲(chǔ)NULL值,所以對(duì)于稀疏數(shù)據(jù)有很好的空間效率。但這一點(diǎn)不適用于MyISAM。

本文主要對(duì)表字段為 NOT NULL 情況的應(yīng)用影響匯總。

一、NOT NULL 的性能優(yōu)勢(shì)

數(shù)據(jù)庫表字段設(shè)置為 NOT NULL 在性能方面具有諸多優(yōu)勢(shì)。

(1)查詢優(yōu)化是一個(gè)重要方面

當(dāng)一列被標(biāo)記為 NOT NULL 時(shí),數(shù)據(jù)庫系統(tǒng)可以使用這個(gè)信息來優(yōu)化查詢。因?yàn)橄到y(tǒng)知道這一列的值永遠(yuǎn)不會(huì)為空,所以在執(zhí)行查詢時(shí)可以忽略 NULL 值,從而提高查詢性能。例如,在一個(gè)包含數(shù)百萬條記錄的大型數(shù)據(jù)庫中,如果某列被設(shè)置為 NOT NULL,那么在查詢這一列的值時(shí),數(shù)據(jù)庫系統(tǒng)可以直接忽略所有的 NULL 值,極大地提高了查詢速度。

(2)NOT NULL 可以減少存儲(chǔ)空間占用

NULL 列需要更多的存儲(chǔ)空間,因?yàn)樾枰粋€(gè)額外字節(jié)作為判斷是否為 NULL 的標(biāo)志位。如果把一些可填可不填的字段設(shè)置為 NOT NULL,就可以節(jié)省這些額外的存儲(chǔ)空間。例如,假設(shè)有一個(gè)包含大量記錄的表,其中有多個(gè)可填可不填的字段,如果這些字段都設(shè)置為 NOT NULL,那么隨著記錄數(shù)量的增加,節(jié)省的存儲(chǔ)空間會(huì)非??捎^。

(3)索引效率也會(huì)得到提升

索引含有空值的列很難進(jìn)行查詢優(yōu)化,而且對(duì)表索引時(shí)不會(huì)存儲(chǔ) NULL 值。所以如果索引的字段可以為 NULL 值,索引的效率會(huì)下降,因?yàn)樗鼈兪沟盟饕⑺饕慕y(tǒng)計(jì)信息以及比較運(yùn)算更加復(fù)雜。應(yīng)該用 0、一個(gè)特殊的值或者一個(gè)空串代替 NULL 值。

綜上所述,數(shù)據(jù)庫表字段設(shè)置為 NOT NULL 在性能方面具有顯著優(yōu)勢(shì),可以提高查詢速度、減少存儲(chǔ)空間占用和提升索引效率。

二、對(duì)開發(fā)的友好性

(1)簡化代碼邏輯

數(shù)據(jù)庫表字段設(shè)置為 NOT NULL,可以極大地簡化開發(fā)人員的代碼邏輯。

在實(shí)際開發(fā)中,如果字段允許為 NULL,那么開發(fā)人員在處理這些字段時(shí),需要進(jìn)行大量的空值判斷。

例如,在 Java 語言中,如果實(shí)體類的某個(gè)字段允許為 NULL,那么在使用這個(gè)字段進(jìn)行操作時(shí),開發(fā)人員需要不斷地進(jìn)行空指針檢查,以避免出現(xiàn)空指針異常。這不僅增加了代碼的復(fù)雜度,還降低了代碼的可讀性和可維護(hù)性。

(2)提高數(shù)據(jù)一致性

NOT NULL 約束能夠在數(shù)據(jù)庫層面強(qiáng)制實(shí)施數(shù)據(jù)一致性約束,從而減少數(shù)據(jù)質(zhì)量問題。

當(dāng)數(shù)據(jù)庫表中的某一列被設(shè)置為 NOT NULL 時(shí),這意味著這一列的每一行都必須有值。這樣可以確保數(shù)據(jù)的完整性和一致性,避免出現(xiàn)數(shù)據(jù)不完整或不一致的情況。

例如,在一個(gè)電商系統(tǒng)中,如果用戶表中的用戶名、郵箱等關(guān)鍵信息字段被設(shè)置為 NOT NULL,那么在用戶注冊(cè)和登錄時(shí),系統(tǒng)可以確保這些關(guān)鍵信息都被正確地填寫,從而提高數(shù)據(jù)的質(zhì)量和可靠性。此外,NOT NULL 約束還可以防止開發(fā)人員在插入或更新數(shù)據(jù)時(shí)出現(xiàn)錯(cuò)誤。

三、應(yīng)用注意事項(xiàng)

為了更好的描述應(yīng)用注意事項(xiàng),我們初始化原始數(shù)據(jù),一個(gè)只有1列的表,很簡單:

圖片圖片

3.1 聚合函數(shù)

常見的聚合函數(shù)有 count、min、max、avg 以及 sum,這些聚合函數(shù)在遇到 NULL 值時(shí),處理方式各不相同:

  • max、min、avg 和 sum 函數(shù)對(duì) NULL 值采取的處理方式是直接忽略

圖片圖片

  • count 函數(shù)處理 NULL 值則需要分情況進(jìn)行討論

count () 返回的是所有記錄的總和,含有 NULL 值的記錄不會(huì)被忽略,也會(huì)被計(jì)算在內(nèi);

count (column_name) 如果這個(gè)列名中含有一個(gè)值為 NULL,則該條記錄會(huì)被忽略,此時(shí)的返回值為 count ()-1

圖片圖片

3.2 與其他值運(yùn)算規(guī)則

在數(shù)據(jù)庫中,NULL 和其他任何值進(jìn)行運(yùn)算的結(jié)果都是 NULL,會(huì)給數(shù)據(jù)處理帶來了很大的不確定性:

  • 當(dāng)進(jìn)行加法運(yùn)算時(shí),如果其中一個(gè)值為 NULL,那么結(jié)果也為 NULL
  • 在進(jìn)行乘法、除法等其他運(yùn)算時(shí),只要有一個(gè)操作數(shù)為 NULL,結(jié)果就會(huì)是 NULL

這種特性使得在處理包含可能為 NULL 值的字段時(shí),需要特別小心,否則很容易得到錯(cuò)誤的結(jié)果。

假設(shè)我們有一個(gè)數(shù)據(jù)庫表,其中包含兩個(gè)字段 A 和 B,A 字段的值可能為 NULL,B 字段的值為固定值。當(dāng)我們嘗試進(jìn)行 A+B 的運(yùn)算時(shí),如果 A 的值為 NULL,那么結(jié)果也會(huì)是 NULL,而不是我們期望的 B 的值。

圖片圖片

3.3 對(duì) distinct、group by、order by 的影響

在數(shù)據(jù)庫操作中,對(duì)于 distinct 和 group by 來說,所有的 NULL 值都會(huì)被視為相等。這意味著如果在進(jìn)行數(shù)據(jù)去重或者分組操作時(shí),含有 NULL 值的記錄會(huì)被歸為一類。

圖片圖片

對(duì)于 order by 來說,升序時(shí) NULL 會(huì)排在最前。這是因?yàn)樵谂判蜻^程中,數(shù)據(jù)庫系統(tǒng)將 NULL 視為一個(gè)特殊的值,按照特定的規(guī)則進(jìn)行排序。

圖片圖片

當(dāng)使用 distinct 對(duì)這個(gè)字段進(jìn)行去重操作時(shí),所有的 NULL 值會(huì)被視為同一個(gè)值,只顯示一次。同樣,在使用 group by 進(jìn)行分組操作時(shí),NULL 值的記錄會(huì)被分到同一組中。

圖片圖片

四、其他影響問題

4.1 索引問題

圖片圖片

索引列中存在大量 NULL 值可能會(huì)導(dǎo)致索引失效,影響查詢性能。因此,將數(shù)據(jù)庫表字段設(shè)置為 NOT NULL 可以減少這種情況的發(fā)生,提高索引的有效性和查詢性能。

(1)索引使用準(zhǔn)確性

在網(wǎng)上有很多說法認(rèn)為 NULL 不能使用索引,然而這種說法并不完全準(zhǔn)確。實(shí)際上,索引列中存在 NULL 值并不意味著完全不能使用索引,只是會(huì)使數(shù)據(jù)庫的優(yōu)化器在選擇索引時(shí)變得更加復(fù)雜。

查詢條件針對(duì)的是索引列中的非 NULL 值,數(shù)據(jù)庫可以使用索引進(jìn)行快速查找。但是,如果查詢條件涉及到 NULL 值的判斷,如 IS NULL 或 IS NOT NULL,優(yōu)化器可能需要考慮更多的因素來決定是否使用索引以及如何使用索引。

因?yàn)?NULL 值在數(shù)據(jù)庫中被視為未知的狀態(tài),與其他具體的值不同,所以在處理包含 NULL 值的索引列時(shí),優(yōu)化器需要評(píng)估各種情況,包括索引的選擇性、數(shù)據(jù)的分布等,以確定最佳的查詢執(zhí)行計(jì)劃。這就可能導(dǎo)致在某些情況下,優(yōu)化器選擇不使用索引,而采用全表掃描等其他方式來執(zhí)行查詢。

(2)索引失效情況

如果索引列上存在大量的 NULL 值,數(shù)據(jù)庫可能會(huì)認(rèn)為使用索引并不能顯著提高查詢性能,因此選擇不使用索引。

假設(shè)一個(gè)表,其中某個(gè)索引列上有很多 NULL 值。當(dāng)進(jìn)行查詢時(shí),如果查詢條件涉及到這個(gè)索引列,數(shù)據(jù)庫可能會(huì)發(fā)現(xiàn)使用索引進(jìn)行查找并不能有效地減少需要掃描的數(shù)據(jù)量,因?yàn)榇罅康?NULL 值使得索引的選擇性降低。在這種情況下,數(shù)據(jù)庫可能會(huì)選擇進(jìn)行全表掃描,而不是使用索引。

此外,當(dāng)索引列上的 NULL 值過多時(shí),還可能影響索引的統(tǒng)計(jì)信息。數(shù)據(jù)庫通常會(huì)根據(jù)索引的統(tǒng)計(jì)信息來評(píng)估查詢的執(zhí)行計(jì)劃,如果統(tǒng)計(jì)信息不準(zhǔn)確,可能會(huì)導(dǎo)致優(yōu)化器做出錯(cuò)誤的決策。

4.2 存儲(chǔ)空間考慮

數(shù)據(jù)庫中的一行記錄在最終磁盤文件中是以行的方式來存儲(chǔ)的,對(duì)于 InnoDB 來說,有 4 種行存儲(chǔ)格式:REDUNDANT、COMPACT、DYNAMIC 和 COMPRESSED。

InnoDB 的默認(rèn)行存儲(chǔ)格式是 COMPACT,存儲(chǔ)格式如下所示,虛線部分代表可能不一定會(huì)存在。

圖片圖片

(1)存儲(chǔ)格式詳解

  1. 變長字段長度列表:有多個(gè)字段則以逆序存儲(chǔ),存儲(chǔ)格式是 16 進(jìn)制,如果沒有變長字段就不需要這一部分了。
  2. NULL 值列表:用來存儲(chǔ)我們記錄中值為 NULL 的情況,如果存在多個(gè) NULL 值那么也是逆序存儲(chǔ),并且必須是 8bit 的整數(shù)倍,如果不夠 8bit,則高位補(bǔ) 0。1 代表是 NULL,0 代表不是 NULL。如果都是 NOT NULL 那么這個(gè)就不存在了。
  3. ROW_ID:一行記錄的唯一標(biāo)志,沒有指定主鍵的時(shí)候自動(dòng)生成的 ROW_ID 作為主鍵。
  4. TRX_ID:事務(wù) ID。
  5. ROLL_PRT:回滾指針。
  6. 每列的值。

(2)NOT NULL 對(duì)存儲(chǔ)空間的影響

如果存在允許為 NULL 的列,就會(huì)多占用一個(gè)字節(jié)的標(biāo)志位空間。

假設(shè)有一張表,只有一個(gè)字段允許為 NULL,其他字段都是 NOT NULL。當(dāng)存儲(chǔ)一條記錄時(shí),如果這個(gè)可空字段的值為 NULL,那么就需要在 NULL 值列表中進(jìn)行標(biāo)記,并且在記錄頭部占用一個(gè)額外的字節(jié)作為標(biāo)志位。隨著記錄數(shù)量的增加,這種額外的存儲(chǔ)空間占用也會(huì)逐漸累積。

然而,如果將所有字段都設(shè)置為 NOT NULL,就不會(huì)有 NULL 值列表和標(biāo)志位的占用。例如,插入一條數(shù)據(jù),所有字段都有確定的值,存儲(chǔ)格式更加緊湊,不會(huì)有額外的空間浪費(fèi)。

綜上所述,將數(shù)據(jù)庫表字段設(shè)置為 NOT NULL 可以減少存儲(chǔ)空間的占用,使數(shù)據(jù)庫的存儲(chǔ)更加高效。特別是在處理大量數(shù)據(jù)時(shí),這種節(jié)省空間的效果會(huì)更加明顯。

責(zé)任編輯:武曉燕 來源: 架構(gòu)精進(jìn)之路
相關(guān)推薦

2023-11-13 15:03:49

MySQL數(shù)據(jù)庫

2010-05-31 15:23:02

MySQL數(shù)據(jù)庫NUL

2023-08-30 09:00:00

向量數(shù)據(jù)庫大語言模型

2009-06-19 16:47:22

數(shù)據(jù)庫表字段J2EE應(yīng)用

2011-05-26 14:18:49

Oracle數(shù)據(jù)庫字段屬性

2018-07-30 10:34:14

時(shí)間序列數(shù)據(jù)庫

2021-07-30 06:58:28

數(shù)據(jù)庫分布式映射

2010-05-05 14:13:52

Oracle數(shù)據(jù)

2023-05-10 16:15:58

javaScript算法開發(fā)

2011-09-27 13:41:09

數(shù)據(jù)庫

2020-10-12 14:37:38

數(shù)據(jù)庫HiveSpark

2010-04-23 14:32:01

Oracle數(shù)據(jù)庫

2018-07-25 18:40:06

數(shù)據(jù)庫MySQL多字段過濾

2019-08-28 07:11:00

Oracle數(shù)據(jù)庫LOB

2017-12-01 05:40:56

數(shù)據(jù)庫中間件join

2022-11-02 08:00:00

數(shù)據(jù)庫多區(qū)域應(yīng)用程序云平臺(tái)

2011-09-27 10:44:07

NewSQL云計(jì)算

2011-07-11 10:42:23

SQL數(shù)據(jù)庫橫向數(shù)據(jù)縱向字段

2019-10-29 05:00:11

Redis數(shù)據(jù)庫集群

2011-08-22 11:12:45

SQL Server 更改賬戶默認(rèn)數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)