1. 程式人生 > 資訊 >企鵝電競大面積清退主播,訊息稱最晚 6 月結束運營

企鵝電競大面積清退主播,訊息稱最晚 6 月結束運營

轉自詳解 MySQL 資料型別,內容上稍作修改。

整型

資料型別 位元組數 帶符號最小值 帶符號最大值 不帶符號最小值 不帶符號最大值
TINYINT 1 -128 127 0 255
SMALLINT 2 -32768 32767 0 65535
MEDIUMINT 3 -8388608 8388607 0 16777215
INT 4 -2147483648 2147483647 0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807 0 18446744073709551616

從實際開發的角度,我們一定要為合適的列選取合適的資料型別

,即到底用不用得到這種資料型別?舉個例子:

  • 一個列舉欄位如果只有 0 和 1 兩個列舉值,那麼選用 TINYINT 就足夠了,但在開發場景下卻使用了 BIGINT,這就造成了資源浪費
  • 假使該資料表中有 100W 資料,那麼總共浪費了 700W 位元組也就是 6.7M 左右,如果更多的表這麼做了,那麼浪費得更多

執行下列 SQL 語句:

drop table if exists test_tinyint;
create table test_tinyint (
    num tinyint
);

insert into test_tinyint values(-100);
insert into test_tinyint values(255);

執行第 7 行的程式碼時候報錯 Out of range value for column 'num' at row 1,即插入的數字範圍越界了,這說明MySQL 中整型預設是帶符號的

把第 3 行的 num 欄位定義改為 num tinyint unsigned,第 7 的插入就不會報錯了,但是第 6 行的插入-100 會報錯,因為無符號整型是無法表示負數的。

整型(N)形式

有時,我們會碰到有些定義整型的寫法是 int(11)。int(N) 我們只需要記住兩點:

  • 無論 N 等於多少,int 永遠佔 4 個位元組
  • N 表示的是顯示寬度,預設補足空白(設定了 unsigned zerofill 之後用 0 補足),超過寬度時無視長度直接顯示整個數字
drop table if exists test_int_width;
create table test_int_width (
    a int(5),
    b int(5) unsigned,
    c int(5) unsigned zerofill,
    d int(8) unsigned zerofill
);

insert into test_int_width values(1, 1, 1, 1111111111);

select * from test_int_width;
+------+------+-------+------------+
| a    | b    | c     | d          |
+------+------+-------+------------+
|    1 |    1 | 00001 | 1111111111 |
+------+------+-------+------------+
1 row in set (0.00 sec)

浮點型

資料型別 位元組數 備註
float 4 單精度浮點型
double 8 雙精度浮點型

以 float 為例:

drop table if exists test_float;
create table test_float (
    num float(5, 2)
);

insert into test_float values(1.233);
insert into test_float values(1.237);
insert into test_float values(10.233);
insert into test_float values(100.233);
insert into test_float values(1000.233);
insert into test_float values(10000.233);
insert into test_float values(100000.233);

select * from test_float;
+--------+
| num    |
+--------+
|   1.23 |
|   1.24 |
|  10.23 |
| 100.23 |
+--------+
4 rows in set (0.00 sec)

float(M,D)、double(M、D) 中:

  • D 表示浮點型資料小數點之後的精度,假如超過 D 位則四捨五入,所以 1.233 四捨五入為 1.23,1.237 四捨五入為 1.24
  • M 表示浮點型資料總共的位數,M 為 5 表示總共支援五位,即小數點前只支援三位數,所以我們並沒有看到 1000.23、10000.233、100000.233 這三條資料的插入,因為插入都報錯了(Out of range value for column 'num' at row 1

當我們不指定 M、D 的時候,會按照實際的精度來處理。

定點型

定點型即 decimal 型別,有了浮點型為什麼我們還需要定點型?寫一段 SQL 看一下就明白了:

drop table if exists test_decimal;
create table test_decimal (
    float_num float(10, 2),
    double_num double(20, 2),
    decimal_num decimal(20, 2)
);

insert into test_decimal values(1234567.66, 1234567899000000.66, 1234567899000000.66);
insert into test_decimal values(1234567.66, 12345678990000000.66, 12345678990000000.66);

select * from test_decimal;
+------------+----------------------+----------------------+
| float_num  | double_num           | decimal_num          |
+------------+----------------------+----------------------+
| 1234567.62 |  1234567899000000.80 |  1234567899000000.66 |
| 1234567.62 | 12345678990000000.00 | 12345678990000000.66 |
+------------+----------------------+----------------------+
2 rows in set (0.00 sec)

看到 float、double 型別存在精度丟失問題,即寫入資料庫的資料未必是插入資料庫的資料,而 decimal 無論寫入資料中的資料是多少,都不會存在精度丟失問題,這就是我們要引入 decimal 型別的原因,decimal 型別常見於銀行系統、網際網路金融系統等對小數點後的數字比較敏感的系統中。

最後講一下 decimal 和 float/double 的區別,個人總結主要體現在兩點上:

  • float/double 在 db 中儲存的是近似值,而 decimal 則是以字串形式進行儲存的
  • decimal(M,D) 的規則和 float/double 相同,但區別在 float/double 在不指定 M、D 時預設按照實際精度來處理而 decimal 在不指定 M、D 時預設為 decimal(10, 0)

日期型別

MySQL 支援五種形式的日期型別:date、time、year、datetime、timestamp:

資料型別 位元組數 格式 備註
date 3 yyyy-MM-dd 儲存日期值
time 3 HH:mm:ss 儲存時分秒
year 1 yyyy 儲存年
datetime 8 yyyy-MM-dd HH:mm:ss 儲存日期 + 時間
timestamp 4 yyyy-MM-dd HH:mm:ss 儲存日期 + 時間,可作時間戳
drop table if exists test_time;
create table test_time (
    date_value date,
    time_value time,
    year_value year,
    datetime_value datetime,
    timestamp_value timestamp
);

insert into test_time values(now(), now(), now(), now(), now());

select * from test_time;
+------------+------------+------------+---------------------+---------------------+
| date_value | time_value | year_value | datetime_value      | timestamp_value     |
+------------+------------+------------+---------------------+---------------------+
| 2022-03-05 | 16:05:37   |       2022 | 2022-03-05 16:05:37 | 2022-03-05 16:05:37 |
+------------+------------+------------+---------------------+---------------------+
1 row in set (0.00 sec)

這裡重點關注一下 datetime 與 timestamp 兩種型別的區別:

  • 上面列了,datetime 佔 8 個位元組,timestamp 佔 4 個位元組
  • 由於位元組數的區別,datetime 與 timestamp 能儲存的時間範圍也不同,datetime 的儲存範圍為 1000-01-01 00:00:00——9999-12-31 23:59:59,timestamp 儲存的時間範圍為 19700101080001——20380119111407
  • datetime 預設值為空,當插入的值為 null 時,該列的值就是 null;timestamp 預設值不為空,當插入的值為 null 的時候,MySQL 會取當前時間
  • datetime 儲存的時間與時區無關,timestamp 儲存的時間及顯示的時間都依賴於當前時區

在實際工作中,一張表往往我們會有兩個預設欄位,一個記錄建立時間而另一個記錄最新一次的更新時間,這種時候可以使用 timestamp 型別來實現。

char 和 varchar 型別

說到 MySQL 字元型,我們最熟悉的應該就是 char 和 varchar 了,關於 char 和 varchar 的對比,我總結一下:

  1. char 是固定長度字串,其長度範圍為 0~255 且與編碼方式無關,無論字元實際長度是多少,都會按照指定長度儲存,不夠的用空格補足;varchar 為可變長度字串,在 utf8 編碼的資料庫中其長度範圍為 0~21844
  2. char 實際佔用的位元組數即儲存的字元所佔用的位元組數,varchar 實際佔用的位元組數為儲存的字元 +1 或 +2 或 +3(見 varchar 型資料佔用空間大小及可容納最大字串限制探究
  3. MySQL 處理 char 型別資料時會將結尾的所有空格處理掉而 varchar 型別資料則不會

關於第一點、第二點,稍後專門解釋,關於第三點,寫一下 SQL 驗證一下:

drop table if exists test_string;
create table test_string (
    char_value char(5),
    varchar_value varchar(5)
);

insert into test_string values('a', 'a');
insert into test_string values(' a', ' a');
insert into test_string values('a ', 'a ');
insert into test_string values(' a ', ' a ');

select length(char_value), length(varchar_value) from test_string;
+--------------------+-----------------------+
| length(char_value) | length(varchar_value) |
+--------------------+-----------------------+
|                  1 |                     1 |
|                  2 |                     2 |
|                  1 |                     2 |
|                  2 |                     3 |
+--------------------+-----------------------+
4 rows in set (0.00 sec)

可以看到,char 型別資料並不會保留字串結尾的空格。

varchar 型資料佔用空間大小及可容納最大字串限制探究

接上一部分,我們這部分來探究一下 varchar 型資料實際佔用空間大小是如何計算的以及最大可容納的字串為多少,首先要給出一個結論:這部分和具體編碼方式有關

先寫一段 SQL 建立表,utf8 的編碼格式:

drop table if exists test_varchar;
create table test_varchar (
    varchar_value varchar(100000)
) charset=utf8;

執行報錯:

Column length too big for column 'varchar_value' (max = 21845); use BLOB or TEXT instead

按照提示,我們把大小改為 21845,執行依然報錯:

Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

改為 21844 就不會有問題,因此在 utf8 編碼下我們可以知道 varchar(M),M 最大=21844。那麼 gbk 呢:

drop table if exists test_varchar;
create table test_varchar (
    varchar_value varchar(100000)
) charset=gbk;

同樣報錯:

Column length too big for column 'varchar_value' (max = 32767); use BLOB or TEXT instead

把大小改為 32767,也是和 utf8 編碼格式一樣的報錯:

Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

改為 M=32765 不會有問題,那麼為什麼會這樣?分點詳細解釋一下:

  • MySQL 要求一個行的資料量長度不能超過 65535 即 64K
  • 對於未指定 varchar 欄位 not null 的表,會有 1 個位元組專門表示該欄位是否為 null
  • varchar(M),當 M 範圍為 0<=M<=255 時會專門有一個位元組記錄 varchar 型字串長度,當 M>255 時會專門有兩個位元組記錄 varchar 型字串的長度,把這一點和上一點結合,那麼 65535 個位元組實際可用的為 65535-3=65532 個位元組
  • 所有英文無論其編碼方式,都佔用 1 個位元組,但對於 gbk 編碼,一個漢字佔兩個位元組,因此最大 M=65532/2=32766;對於 utf8 編碼,一個漢字佔 3 個位元組,因此最大 M=65532/3=21844,解釋了實驗中的現象
  • 舉一反三,對於 utf8mb4 編碼方式,1 個字元最大可能佔 4 個位元組,那麼 varchar(M),M 最大為 65532/4=16383,可以自己驗證一下

同樣的,上面是表中只有 varchar 型資料的情況,如果表中同時存在 int、double、char 這些資料,需要把這些資料所佔據的空間減去,才能計算 varchar(M) 型資料 M 最大等於多少

varchar、text 和 blob

最後講一講 text 和 blob 兩種資料型別,它們的設計初衷是為了儲存大資料使用的,因為之前說了,MySQL 單行最大資料量為 64K。

先說一下 text,text 和 varchar 是一組既有區別又有聯絡的資料型別,其聯絡在於當 varchar(M) 的 M 大於某些數值時,varchar 會自動轉為 text

  • M>255 時轉為 tinytext
  • M>500 時轉為 text
  • M>20000 時轉為 mediumtext

所以過大的內容 varchar 和 text 沒有區別,同時 varchar(M) 和 text 的區別在於:

  • 單行 64K 即 65535 位元組的空間,varchar 只能用 65532/65533 個位元組,但是 text 可以 65535 個位元組全部用起來
  • text 可以指定 text(M),但是 M 無論等於多少都沒有影響
  • text 不允許有預設值,varchar 允許有預設值

varchar 和 text 兩種資料型別,使用建議是能用 varchar 就用 varchar 而不用 text(儲存效率高),varchar(M) 的 M 有長度限制,之前說過,如果大於限制,可以使用 mediumtext(16M)或者 longtext(4G)。

至於 text 和 blob,簡單過一下就是text 儲存的是字串而 blob 儲存的是二進位制字串,簡單說 blob 是用於儲存例如圖片、音視訊這種檔案的二進位制資料的。