MySQL:建立表時如何選擇合適的字段類型

最近須要對錶加一個字段,同時以爲前期創建表的時候有點粗暴,沒有加很對限制,好比有些字符串長度是有限制的,在建立表時字段也沒有對其進行限制。因此想借着此次加字段對錶字段也進行一個優化,在優化以前先看了點理論知識,理論指導實踐mysql

寫在前面

選擇合適的字段類型既能夠節省空間,又能夠在查詢上提升效率,所以字段類型選擇是很重要的。本篇文章將介紹經常使用字段類型:sql

  • 整數類型
  • 實數類型
  • 字符串類型
  • 日期和時間
  • 枚舉類型

整數類型

整數類型有TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT,存儲空間及數值範圍以下表數據庫

類型 存儲空間(單位爲位) 數值範圍
TINYINT 8 -128 ~ 127
SMALLINT 16 -32768 ~ 32767
MEDIUMINT 24 -8388608 ~ 8388607
INT 32 -2147483648 ~ 2147483647
BIGINT 64 太大了

數值範圍爲-2^(N-1) ~ 2^N, 其中N爲存儲空間大小緩存

整數類型有可選的UNSIGNED屬性,不容許出現負值。設置UNSIGNED屬性可使正數的上限提升一倍,數值範圍大小爲 0 ~ 2^(N-1) + 2^Nbash

通常選擇最小的可以知足存儲的類型就行,更小的數據類型一般更快,佔用更少的磁盤、內存和CPU緩存。處理時須要的CPU週期也更少post

實數類型

FLOAT

單精度浮點型,使用8位性能

DOUBLE

雙精度浮點型,使用16位存儲優化

DECIMAL

float和double進行計算時會發生精度損失,損精度損失緣由可參考這篇文章:老闆,用float存儲金額爲何要扣我工資 須要精度計算的時候可使用DECIMAL,使用DECIMAL須要額外的空間和計算開銷,所以當且僅當須要精度計算時才使用spa

字符串類型

1. VARCHAR和CHAR

varchar和char是很是很是經常使用的字符串類型3d

VARCHAR

VARCHAR用於存儲變長字符串,使用該類型存儲字符串時須要額外使用1或2個額外字節記錄字符串的長度:

  • 列的最大長度小於或等於255 => 使用1字節
  • 列的長度大於255 => 使用2字節

適用VARCHAR做爲存儲類型的場景:

  • 列更新不多 => 列常常更新容易產生頁分裂
  • 列長度非固定 => VARCHAR存儲時只使用必要空間,所以會省空間
CHAR

CHAR用於存儲定長字符串,在存儲CHAR類型時,會刪除全部的末尾空格

使用CHAR最爲存儲類型的場景

  • 列幾乎定長
  • 列長度很短 => VARCHAR須要額外字節存儲長度
  • 列常常更新

2. BLOB和TEXT類型

BLOB和TEXT類型都是用來存儲很大的數據,好比文章內容這些

BLOB

採用二進制方式存儲, BLOB細分又能夠分爲TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB, LONGBLOB

TEXT

採用字符方式存儲,TEXT細分又能夠分爲TINYTEXT,SMALLTEXT,TEXT,MEDIUMTEXT, LONGTEXT

當BLOB和TEXT值太大時,InnoDB存儲會使用外部存儲區域來存儲值,而後保存一個1~4字節的指針指向外部存儲

日期和時間類型

經常使用的日期類型有DATETIME和TIMESTAMP

DATETIME

使用8字節存儲,能夠保存大範圍的值,從1001~9999年

TIMESTAMP

使用4字節存儲,保存範圍比DATETIME小,從1970~2038年

對於須要存儲更小粒度的日期和時間可使用DOUBLE或BIGINT,固然不是存儲小粒度也可使用BIGINT

DATETIME和TIMESTAMP如何選擇

以前曾由於時間類型搞出過線上慢查詢,這篇文章記錄了慢查詢緣由:很高興!終於踩到了慢查詢的坑, 對於須要對時間進行範圍查找、排序、分組等操做之類的建議使用BIGINT,若是對時間類型字段沒有任何操做,建議使用TIMESTAMP,能夠參考這篇文章:mysql數據庫時間類型datetime、bigint、timestamp的查詢效率比較
在stackoverflow下找到以下:

枚舉類型

可使用枚舉列代替經常使用的字符串類型,經過枚舉能夠限制值的取值範圍

枚舉使用

建立表語句:

CREATE TABLE `dataset_enum` (
  `name` varchar(48) DEFAULT NULL,
  `status` enum('NEW','UPLOADING','USING','DELETING') DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
複製代碼

插入語句:

insert into dataset_enum(name, status) values("t4", "DELETING")
複製代碼

對於status字段底層存儲的是整數而不是字符串,在底層會維護一個 數字 - 字符串的映射關係

查詢語句並根據status字段進行排序:

select * from dataset_enum order by status;
複製代碼

查詢結果:

+------+-----------+
| name | status    |
+------+-----------+
| t1   | NEW       |
| t2   | UPLOADING |
| t4   | DELETING  |
+------+-----------+
複製代碼

說明:

  • 排序的結果是根據內部存儲的整數來的而不是定義的字符串進行排序的
  • 底層存儲的是整數,根據映射關係轉化爲字符串,所以會有必定的開銷

爲何使用TINYINT而不用ENUM

以前建立表的時候對於經常使用字符串的代替選擇的都是TINYINT類型,應用層在作轉換。當看到ENUM類型時有點困惑,爲何沒選擇使用ENUM而是TINY,網上查找了一下緣由,以下圖:

總結緣由以下:

  • 不方便遷移,可擴展性弱,如比較熟悉的PostgreSQL數據庫就不支持ENUM類型
  • ENUM字段添加或刪除字符串時會進行表重構,這個操做很是耗時和耗性能
  • 有坑 以以前的表dataset_enum爲例插入數據:
    mysql> insert into dataset_enum values("t1", "NEW"), ("t2", 2);
    Query OK, 2 rows affected (0.01 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    複製代碼
    成功插入了數據 查詢數據:
    mysql> select * from dataset_enum;
    +------+-----------+
    | name | status    |
    +------+-----------+
    | t1   | NEW       |
    | t2   | UPLOADING |
    +------+-----------+
    複製代碼
    數值類型作轉化之後也能夠插入
  • 沒法與其餘表作關聯



參考文章:
Should I use the datetime or timestamp data type in MySQL?
8 Reasons Why MySQL's ENUM Data Type Is Evil 爲何辣麼多人喜歡用 tinyint而不用 enum? 《高性能MySQL》

相關文章
相關標籤/搜索