近期由于複習了一下mysql的内容,有些心得。随手講其中一部分知識,都是一些煙哥自己平時工作的總結以及經驗。大家看完,其實能避開很多坑。而且很多問題,都是面試中實打實會問到的!
比如

OK,具體有下面這些問題
1、為什麼一定要設一個主鍵?
2、你們主鍵是用自增還是UUID?
3、主鍵為什麼不推薦有業務含義?
4、表示枚舉的字段為什麼不用enum類型?
5、貨币字段用什麼類型?
6、時間字段用什麼類型?
7、為什麼不直接存儲圖檔、音頻、視訊等大容量内容?
8、字段為什麼要定義為NOT NULL?
其實上面這些問題,我最早想法是,每個問題都可以啰嗦出一篇文章。後來由于良心發現,煙哥就決定用一篇文章将這些問題都講明白。
當然,我給的回答可能并非标準答案,畢竟是自己的一些工作總結。各位讀者有更好的回答,也歡迎交流!
這裡我要說一下,我用mysql隻用過<code>innodb</code>存儲引擎,其他的引擎真沒用過。是以我的回答,都是基于<code>innodb</code>存儲引擎中的。
問題1:為什麼一定要設一個主鍵?
回答:因為你不設主鍵的情況下,innodb也會幫你生成一個隐藏列,作為自增主鍵。是以啦,反正都要生成一個主鍵,那你還不如自己指定一個主鍵,在有些情況下,就能顯式的用上主鍵索引,提高查詢效率!
問題2:主鍵是用自增還是UUID?
回答:肯定答自增啊。innodb 中的主鍵是聚簇索引。如果主鍵是自增的,那麼每次插入新的記錄,記錄就會順序添加到目前索引節點的後續位置,當一頁寫滿,就會自動開辟一個新的頁。如果不是自增主鍵,那麼可能會在中間插入,就會引發頁的分裂,産生很多表碎片!。
上面那句話看不懂沒事,大白話一句就是:用自增插入性能好!
另外,附一個測試表給你們,表名帶uuid的就是用uuid作為主鍵。大家看一下就知道性能差距了:
如上圖所示,當主鍵是UUID的時候,插入時間更長,而且占用空間更大!
額,大家千萬不要忘了,當你回答自增主鍵後,想一下《自增主鍵用完該怎麼辦?》
<code>ps</code>:這個問題,你要是能把<code>UUID</code>講出合理的理由也行。
問題3:主鍵為什麼不推薦有業務含義?
回答:有如下兩個原因
(1)因為任何有業務含義的列都有改變的可能性,主鍵一旦帶上了業務含義,那麼主鍵就有可能發生變更。主鍵一旦發生變更,該資料在磁盤上的存儲位置就會發生變更,有可能會引發頁分裂,産生空間碎片。
(2)帶有業務含義的主鍵,不一定是順序自增的。那麼就會導緻資料的插入順序,并不能保證後面插入資料的主鍵一定比前面的資料大。如果出現了,後面插入資料的主鍵比前面的小,就有可能引發頁分裂,産生空間碎片。
問題4:表示枚舉的字段為什麼不用enum類型?
回答:在工作中表示枚舉的字段,一般用<code>tinyint</code>類型。
那為什麼不用enum類型呢?下面兩個原因
(1)ENUM類型的ORDER BY操作效率低,需要額外操作
(2)如果枚舉值是數值,有陷阱
舉個例子,表結構如下
此時,你執行語句
查詢出的結果為
就産生了一個坑爹的結果。
插入語句應該像下面這麼寫,插入的才是1
問題5:貨币字段用什麼類型?
回答:如果貨币機關是分,可以用<code>Int</code>類型。如果堅持用元,用<code>Decimal</code>。
千萬不要答<code>float</code>和<code>double</code>,因為float和double是以二進制存儲的,是以有一定的誤差。
打個比方,你建一個列如下
然後insert給price列一個資料為<code>1234567.23</code>,你會發現顯示出來的資料變為<code>1234567.25</code>,精度失準!
問題6:時間字段用什麼類型?
回答:此題無固定答案,應結合自己項目背景來答!把理由講清楚就行!
(1)<code>varchar</code>,如果用varchar類型來存時間,優點在于顯示直覺。但是坑的地方也是挺多的。比如,插入的資料沒有校驗,你可能某天就發現一條資料為<code>2013111</code>的資料,請問這是代表2013年1月11日,還是2013年11月1日?
其次,做時間比較運算,你需要用<code>STR_TO_DATE</code>等函數将其轉化為時間類型,你會發現這麼寫是無法命中索引的。資料量一大,是個坑!
(2)<code>timestamp</code>,該類型是四個位元組的整數,它能表示的時間範圍為1970-01-01 08:00:01到2038-01-19 11:14:07。2038年以後的時間,是無法用<code>timestamp</code>類型存儲的。
但是它有一個優勢,<code>timestamp</code>類型是帶有時區資訊的。一旦你系統中的時區發生改變,例如你修改了時區
你會發現,項目中的該字段的值自己會發生變更。這個特性用來做一些國際化大項目,跨時區的應用時,特别注意!
(3)<code>datetime</code>,datetime儲存占用8個位元組,它存儲的時間範圍為1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。顯然,存儲時間範圍更大。但是它坑的地方在于,他存儲的是時間絕對值,不帶有時區資訊。如果你改變資料庫的時區,該項的值不會自己發生變更!
(4)<code>bigint</code>,也是8個位元組,自己維護一個時間戳,表示範圍比<code>timestamp</code>大多了,就是要自己維護,不大友善。
問題7:為什麼不直接存儲圖檔、音頻、視訊等大容量内容?
回答:我們在實際應用中,都是用<code>HDFS</code>來存儲檔案。然後mysql中,隻存檔案的存放路徑。mysql中有兩個字段類型被用來設計存放大容量檔案,也就是<code>text</code>和<code>blob</code>類型。但是,我們在生産中,基本不用這兩個類型!
主要原因有如下兩點
(1)Mysql記憶體臨時表不支援TEXT、BLOB這樣的大資料類型,如果查詢中包含這樣的資料,在排序等操作時,就不能使用記憶體臨時表,必須使用磁盤臨時表進行。導緻查詢效率緩慢
(2)<code>binlog</code>内容太多。因為你資料内容比較大,就會造成<code>binlog</code>内容比較多。大家也知道,主從同步是靠<code>binlog</code>進行同步,<code>binlog</code>太大了,就會導緻主從同步效率問題!
是以,不推薦使用<code>text</code>和<code>blob</code>類型!
問題8:字段為什麼要定義為NOT NULL?
回答:OK,這問題從兩個角度來答
(1)索引性能不好
Mysql難以優化引用可空列查詢,它會使索引、索引統計和值更加複雜。可空列需要更多的存儲空間,還需要mysql内部進行特殊處理。可空列被索引後,每條記錄都需要一個額外的位元組,還能導緻MYisam 中固定大小的索引變成可變大小的索引。 —— 出自《高性能mysql第二版》
(2)查詢會出現一些不可預料的結果
這裡舉一個例子,大家就懂了。假設,表結構如下
表資料是這樣的
id
name
1
孤獨煙
3
5
肥朝
7
你執行語句
你會發現結果為2,但是實際上是有四條資料的!類似的查詢問題,其實有很多,不一一列舉。
記住,因為null列的存在,會出現很多出人意料的結果,進而浪費開發時間去排查Bug.
希望大家有所收獲吧!
作者:孤獨煙
出處: http://rjzheng.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。