
最近在折騰指標監(jiān)控報警,對指標監(jiān)控來說,海量的數(shù)據(jù)存儲肯定是考驗,業(yè)內(nèi)比較好的方案應(yīng)該是存儲到時序數(shù)據(jù)庫。對我們來說剛開始起步,存儲到最熟悉的 MySql 肯定是最快最穩(wěn)妥的方案。做啥都是應(yīng)該先跑起來,一下子吃成胖子很容易撐壞。做大做強,再創(chuàng)輝煌,應(yīng)該是取得一定成績后的目標。
雖說初期小業(yè)務(wù)量,但是對最基本的存儲來說,還是應(yīng)該能省就省?,F(xiàn)在外部大環(huán)境不好,集團也特別關(guān)注成本的控制。放到程序上來說,每行更小的數(shù)據(jù) size,意味著數(shù)據(jù)讀取的更塊,系統(tǒng)跑的更快。
所以就整體復(fù)習了下 MySql 各種字段的存儲需求,MySql 版本是 5.7。
總體說明
表數(shù)據(jù)在磁盤上的存儲取決于幾個因素,不同的存儲引擎表示數(shù)據(jù)類型及對原始數(shù)據(jù)的存儲都是不同的。表數(shù)據(jù)可能會被壓縮,無論是針對一列還是整行,這會使表或列的存儲需求計算復(fù)雜化。我們就不去關(guān)注其他引擎了,都是基于 InnoDB 引擎。
在數(shù)據(jù)庫內(nèi)部,表中的行大小最大為 65,535 字節(jié),即使存儲引擎能夠支持更大的行。這個數(shù)字排除了 BLOB 或 TEXT 列,它們只占這個大小的 9 到 12 個字節(jié)。對于 BLOB 和 TEXT 數(shù)據(jù),信息存儲在與行緩沖區(qū)不同的內(nèi)存區(qū)域中。不同的存儲引擎根據(jù)它們處理相應(yīng)類型的方法,以不同的方式處理這些數(shù)據(jù)的分配和存儲。
數(shù)值類型
| 數(shù)據(jù)類型 | 存儲要求 | 數(shù)據(jù)范圍 |
|---|---|---|
| TINYINT | 1 byte | -128 到 127 |
| SMALLINT | 2 bytes | -32768 到 32767 |
| MEDIUMINT | 3 bytes | -8388608 到 8388607 |
| INT, INTEGER | 4 bytes | -2147483648 到 2147483647 |
| BIGINT | 8 bytes | -9223372036854775808 到 9223372036854775807 |
| FLOAT(p) | 當 0 <= p <= 24 , 4 bytes 當 25 <= p <= 53 , 8 bytes |
|
| FLOAT | 4 bytes | -3.402823466E+38 到 -1.175494351E-38 0 1.175494351E-38 到 3.402823466E+38. |
| DOUBLE [PRECISION], REAL | 8 bytes | -1.7976931348623157E+308 到 -2.2250738585072014E-308 0 2.2250738585072014E-308 到 1.7976931348623157E+308. |
| DECIMAL(M,D), NUMERIC(M,D) | 請看:DECIMAL 特殊說明 | M 長度最大為 65 |
| BIT(M) | 約為 (M+7)/8 bytes | M 長度 1 到 64 |
DECIMAL
在MySQL中,NUMERIC 是作為 DECIMAL 實現(xiàn)的,所以下面所有 DECIMAL 的說明同樣適用于 NUMERIC。
DECIMAL 類型的值使用二進制格式表示,將 9 個十進制數(shù)字裝入 4 個字節(jié)。每個值的整數(shù)和小數(shù)部分的存儲是單獨確定的。每 9 位數(shù)就需要 4 個字節(jié),而 "剩余 "的數(shù)字需要至少 4 個字節(jié)來存儲。多余的數(shù)字所需的存儲量由下表中給出。
| 剩余數(shù)字個數(shù) | 存儲需求 |
|---|---|
| 0 | 0 bytes |
| 1, 2 | 1 byte |
| 3, 4 | 2 bytes |
| 5, 6 | 3 bytes |
| 7, 8 | 4 bytes |
可以看出奇數(shù)個位數(shù)跟 +1 后的偶數(shù)位數(shù)所需的空間是一樣的,如果只考慮存儲成本,我們在使用的時候,可以盡量讓剩余數(shù)字個數(shù)為偶數(shù)值。
在標準 SQL 中,DECIMAL(M) 的語法等同于 DECIMAL(M,0)。類似地,DECIMAL 語法等同于 DECIMAL(M,0),其中允許執(zhí)行者決定 M 的值。M 的默認值是10。
DECIMAL 的最大位數(shù)是 65,但是一個給定的 DECIMAL 列的實際范圍會受到給定列的精度或比例的限制。當這樣的一列被分配到一個小數(shù)點后面的數(shù)字多于指定比例所允許的數(shù)值時,該數(shù)值將被轉(zhuǎn)換為該比例。(精確的行為是由操作系統(tǒng)決定的,但通常的效果是截斷到允許的位數(shù))。
日期和時間類型
| 數(shù)據(jù)類型 | 存儲需求 |
|---|---|
| YEAR | 1 byte |
| DATE | 3 bytes |
| TIME | 3 bytes + 小數(shù)秒存儲 |
| DATETIME | 5 bytes + 小數(shù)秒存儲 |
| TIMESTAMP | 4 bytes + 小數(shù)秒存儲 |
從 MySQL 5.6.4 開始,對 TIME、DATETIME 和 TIMESTAMP 改為固定大小 + 小數(shù)秒存儲方式,需要 0 到 3 個字節(jié),這取決于存儲的小數(shù)秒值精度。
| 小數(shù)秒精度 | 存儲需求 |
|---|---|
| 0 | 0 bytes |
| 1, 2 | 1 byte |
| 3, 4 | 2 bytes |
| 5, 6 | 3 bytes |
可以看到與 DECIMAL 類似,如果只考慮存儲成本,我們在使用的時候,可以盡量設(shè)置成所需的偶數(shù)值。
例如,DATETIME(0)、DATETIME(2)、DATETIME(4) 和 DATETIME(6) 分別使用 5、6、7 和 8 個字節(jié)。DATETIME 和 DATETIME(0) 是等價的,需要相同的存儲空間。
字符串類型
可變長度字符串類型使用長度前綴加數(shù)據(jù)存儲。根據(jù)數(shù)據(jù)類型,長度前綴需要 1 到 4 個字節(jié),字符串的字節(jié)長度為 L。例如,存儲一個 MEDIUMTEXT 值需要 L 個字節(jié)來存儲該值,再加上 3 個字節(jié)來存儲該值的長度。
在下表中,M 表示非二進制字符串類型的聲明列長度(以字符為單位)和二進制字符串類型的字節(jié)數(shù)。 L 表示給定字符串值的實際長度(以字節(jié)為單位),特別注意,這里不是字符長度,開始時候很容易弄混。即 LENGTH() 方法計算出來的長度,而不是 CHAR_LENGTH() 方法計算出來的長度。
| 數(shù)據(jù)類型 | 存儲需求 |
|---|---|
| CHAR(M) | InnoDB 緊湊的行格式,優(yōu)化了可變長度字符集的存儲,見下面對 CHAR 類型的說明。否則應(yīng)該是 M × w 字節(jié),0 <= M <= 255,其中 w 是字符集中最大長度字符所需的字節(jié)數(shù)。 |
| BINARY(M) | M bytes, 0 <= M <= 255 |
| VARCHAR(M), VARBINARY(M) | 當 0 <= L <= 255 bytes : L + 1 bytes 當 L > 255 bytes : L + 2 bytes |
| TINYBLOB, TINYTEXT | L + 1 bytes, L < |
| BLOB, TEXT | L + 2 bytes, L < |
| MEDIUMBLOB, MEDIUMTEXT | L + 3 bytes, L < |
| LONGBLOB, LONGTEXT | L + 4 bytes, L < |
| ENUM('value1','value2',...) | 1 或 2 bytes,取決于枚舉值的數(shù)量(最大為 65,535 個值) |
| SET('value1','value2',...) | 1、2、3、4 或 8 個 bytes,取決于 set 成員的數(shù)量(最大 64 個成員) |
要計算用于存儲特定 CHAR、VARCHAR 或 TEXT 列值的字節(jié)數(shù),必須考慮用于該列的字符集以及該值是否包含多字節(jié)字符。特別是,在使用 UTF-8 Unicode 字符集時,必須記住并非所有字符使用相同的字節(jié)數(shù)。utf8mb3 和 utf8mb4 字符集可以分別要求每個字符最多 3 個字節(jié)和 4 個字節(jié)。
CHAR
在 MySql 內(nèi)部,對于非可變長度字符集如 latin1,固定長度字符列,如 CHAR(10) 以固定長度格式存儲。
對于如 utf8mb3 和 utf8mb4 等可變長度字符集,InnoDB 至少為 CHAR(N) 保留 N 個字節(jié),并嘗試通過修剪尾隨空格將 CHAR(N) 存儲在 N 個字節(jié)中。如果 CHAR(N) 列值的字節(jié)長度超過 N 個字節(jié),則尾隨空格將被修剪為列值字節(jié)長度的最小值。 CHAR(N) 列的最大長度是最大字符字節(jié)長度 × N。
在許多情況下,保留最小空間 N 可以在不導(dǎo)致索引頁碎片的情況下就地完成列更新。相比之下,CHAR(N) 列在使用 REDUNDANT 行格式時占用最大字符字節(jié)長度 × N。
InnoDB 將長度大于等于 768 字節(jié)的定長字段編碼為可變長字段,可以在頁外存儲。例如,如果字符集的最大字節(jié)長度大于 3,例如 utf8mb4,CHAR(255) 列就可以超過 768 字節(jié)。
可變長類型
VARCHAR、VARBINARY 以及 BLOB 和 TEXT 類型都是可變長類型。對于每一個,存儲需求取決于以下因素:
- 列值的實際長度
- 列的最大可能長度
- 用于列的字符集,因為一些字符集包含多字節(jié)字符
例如,VARCHAR(255) 列可以容納最大長度為 255 個字符的字符串。假設(shè)列使用 latin1 字符集(每個字符一個字節(jié)),實際需要的存儲是字符串的長度 L,加上一個字節(jié)來記錄字符串的長度。對于字符串 'abcd', 那么 L 是 4,存儲要求是 5 個字節(jié)。如果這個列聲明為使用 ucs2 雙字節(jié)字符集,則存儲要求為 10 字節(jié):'abcd' 的長度是 8 個字節(jié),該列需要 2 個字節(jié)來存儲長度,因為最大長度大于 255 字節(jié)(最多 510 字節(jié))。
VARCHAR 或 VARBINARY 列中可以存儲的有效最大字節(jié)數(shù)受最大的行大?。?5,535字節(jié))的限制,該行大小在一行中的所有列之間共享。對于存儲多字節(jié)字符的 VARCHAR 列,有效最大字符數(shù)更少。例如,utf8mb4 字符每個字符最多需要 4 個字節(jié),因此可以將使用 utf8mb4 字符集的 VARCHAR 列聲明為最大 16383 個字符。
ENUM
ENUM 對象的大小由不同枚舉值的數(shù)量決定。1 個字節(jié)用于最多 255 個可能值的枚舉。2 個字節(jié)用于 256 到65,535 個可能值之間的枚舉。
SET
SET 對象的大小由不同 SET 成員的數(shù)量決定。如果設(shè)置的大小為 N,則對象占用 (N+7)/8 個字節(jié),四舍五入到1、2、3、4 或 8 個字節(jié)。一個集合最多可以有 64 個成員。
空間類型
空間類型在 MySQL 內(nèi)部以一種與 WKT 及 WKB 格式不完全相同的格式存儲空間類型數(shù)據(jù),格式是: 4 字節(jié) SRID + WKB。
WKB 格式
WKB 使用 1 字節(jié)的無符號 Integer 標識字節(jié)順序,4 字節(jié)的無符號 Integer 標識類型,及 8 字節(jié)的雙精度 Double來記錄坐標。
例如,對一個 POINT(1 -1) 的 WKB 數(shù)據(jù)就是由 21 個字節(jié)組成,每個字節(jié)由兩個十六進制數(shù)字表示。
0101000000000000000000F03F000000000000F0BF
| 組成部分 | 長度 | 值 |
|---|---|---|
| Byte order | 1 bytes | 01 |
| WKB type | 4 bytes | 01000000 |
| X coordinate | 8 bytes | 000000000000F03F |
| Y coordinate | 8 bytes | 000000000000F0BF |
空間類型存儲空間
mysql> SET @g = ST_GeomFromText('POINT(1 -1)');
mysql> SELECT LENGTH(@g);
+------------+
| LENGTH(@g) |
+------------+
| 25 |
+------------+
mysql> SELECT HEX(@g);
+----------------------------------------------------+
| HEX(@g) |
+----------------------------------------------------+
| 000000000101000000000000000000F03F000000000000F0BF |
+----------------------------------------------------+
知道了 WKB 數(shù)據(jù)的組成方式,自然也就知道了 MySql 中對空間類型的存儲大小了。
| 組成部分 | 長度 | 二進值 | 實際值 |
|---|---|---|---|
| SRID | 4byte | 00000000 | 0 |
| Byte order | 1 bytes | 01 | little-endian |
| WKB type | 4 bytes | 01000000 | Point |
| X coordinate | 8 bytes | 000000000000F03F | 1 |
| Y coordinate | 8 bytes | 000000000000F0BF | -1 |
JSON 類型
一般來說,JSON 類型的存儲需求與 LONGBLOB 或 LONGTEXT 類型的存儲大致相同;也就是說,JSON 文檔消耗的空間與存儲在這些類型的列中的字符串表示大致相同。但是,存儲在 JSON 文檔中的單個值的二進制編碼(包括查找所需的元數(shù)據(jù)和字典)會帶來額外的開銷。例如,存儲在 JSON 文檔中的字符串需要 4 到 10 個字節(jié)的額外存儲,這取決于字符串的長度和存儲它的對象或數(shù)組的大小。
另外,MySQL 對 JSON 列中存儲的任何 JSON 文檔的大小施加了限制,不能超過 max_allowed_packet 的值。