1. 程式人生 > 實用技巧 >schema與資料型別優化-高效能mysql

schema與資料型別優化-高效能mysql

總結作為開發人員重點注意的內容!這是一篇有關高效能MYSQL第四章schema相關的筆記。

0.前言

在專案中,資料庫表列有兩個text欄位,用來儲存大文字,在資料規模達到40萬後,如果查詢沒命中索引,發現耗時會達到5s以上,在表中刪除這兩個大欄位後,就算全表掃描,耗時也不過0.6s左右。

解決方案:

  1. 將這類不常參加查詢的大欄位放入詳情表中,但對現有程式影響過大(懶)。
  2. 查詢欄位統統加上索引。

1.選擇合適的資料型別

  • 選擇合適的資料範圍,能用 tinyint 不用 int。
  • 欄位越簡單越好。儲存時間使用datetime而不是字串,儲存ip用整數而不用字串。
  • 儘量避免null。null 會使索引更加複雜,null改為not null效能提升較小,但設計時,邏輯上不可能為null的值,最好加上not null限制。

datetime 和 timestamp 比較

相同點: 都能儲存日期、時間,精確到秒。

不同點: timestamp 只佔 datetime 一半儲存空間,具備時區功能,同時允許的時間範圍小得多。

1.1.1 整數型別

型別及儲存空間

  • tinyint(1位元組)
  • smallint(2位元組)
  • mediumint(3位元組)
  • int(4位元組)
  • bigint(8節字)

int(11) 其中11只是指定顯示寬頻,對儲存空間無任何影響。

可以指定型別為unsinged,但java不支援unsinged, 使用相同型別可能會有麻煩。

1.1.2 實數型別

精確型別:decimal, 不精確型別: double, float。

decimal 支援儲存和計算的精確。計算效能比浮點型別差一點。

decimal(11,4)表示支援4位精確小數,整數部分最多隻能有7位數字。

decimal型別4個位元組儲存9個數字,decimal(18,9)佔用9個位元組,前面9位整數佔4個位元組,小數點1個位元組,後面小數佔4個位元組。

mysql5.0版本及以上,decimal 最多儲存65個數字。

浮點型別在表示同樣範圍,通常比decimal使用更少的空間。

在資料量比較大的情況下,可以用bigint代替decimal, 將小數的位數乘以相應的倍數轉為bigint,可避免不精確的問題。

1.1.3字串型別

varchar 和char

varchar儲存可變長的字串,使用額外的1個或兩個位元組額外儲存空間,表示字串的長度。

varchar在table指定row_format=fixed情況下,也是定長的,這會導致空間的浪費。

varchar(256)使用一個位元組儲存字元字元數。varchar(1000)使用兩個位元組儲存字元數。

update varchar欄位可能會導致列變得更長,innodb儲存引擎會導致出現分裂頁的情況出現,myisam將行拆分成不同的段儲存。

varchar適用場景:最長字串比平均字串長度長很多。

char是定長的,不容易產生碎片,適用於儲存密碼或md5值, 或表示性別,F/M, varchar(1)需要兩個位元組,char(1)需要一個位元組。

char 會截斷末尾的空格。

varchar(5)和varchar(100)都能儲存5個字元,但varchar(100)效能會比varchar(5)糟糕,所以最好選擇合適的長度。

text與blob型別

text儲存大文字,blob儲存二進位制資料。

大文字有關的型別: tinytext, smalltext, text, mediumtext, longtext。

二進位制有關的型別: tinyblob, smallbolob, blob, mediumblob, longblob。

當text,blob太大時,會作為一個物件單獨儲存,行中使用指標指向資料位置。

個人建議少用或不用這種型別,或拆除到詳情表,減少對查詢帶來影響

enum型別

欄位定義:animal enum("dog", "fish", "cat")

enum 實際儲存為整數,並在.frm檔案中儲存字串-整數對應關係。

看個列子更容易明白:

create table enum_test(
	id int auto_increment,
    animal enum("dog", "fish", "cat"),
    primary key(id)
);
insert into enum_test(animal)
values
("dog"),
("cat"),
("dog"),
("fish")
;

對該表進行查詢:

select animal+0, animal
from enum_test
order by animal
;

可以看到排序是按照整數排序的,即定義的順序進行排序,而不是字串序。

解決方案:定義時按照字串序定義。

自定義順序排序使用field函式,但這樣將無法使用索引:

select animal+0, animal
from enum_test
order by field(animal, "cat", "fish", "dog");

欄位join效率比較: enum join enum > varchar join varchar > enum join varchar = varchar join enum。

使用alter talbe 在列舉型別中新增字串時,會重建整個表,除非總是在末尾新增值。

壞處:當列舉值為數字時,很容易發生困惑,儘量避免這種情況。

1.1.4 時間型別

datetime型別:

  • 儲存範圍大: (1001年-9999年)精度為秒,將值儲存為 YYYYMMDDHHMMSS的大整數中,佔用八位元組。

timestamp:

  • 儲存範圍小:(1970年-2038年),精度為秒,儲存從1970-1-1年以來的秒數,佔用4位元組。
  • unix_timestamp()支援日期->秒數, from_unixtime()支援秒數->日期。
  • 具備時區概念,在mysql伺服器,作業系統,連線串都可以指定時區,推薦在連線串指定時區:(jdbc)serverTimezone=Asia/Shanghai
  • 插入時沒有指定會使用當前時間作為預設值

不推薦自己使用整數值來儲存時間,這和內部儲存沒什麼差別,沒帶來什麼好處卻多了額外的處理邏輯。

書中推薦使用timestamp, 個人推薦使用datetime,timestamp花活太多。

1.1.5 位資料型別

bit型別

bit列可以儲存多個true/false值,bit(17)可以儲存17個true/false值,myisam 使用一個位元來儲存一個單獨的true/false值,innodb使用最小的整數值來儲存bit列的值。

myisam: bit(17)使用17個位元儲存。

innodb: tinyint 即可儲存。

高效能mysql認為應該慎用這個型別,所以就不深究了。

set 型別

好處: 儲存使用打包的位儲存,佔用空間少。

壞處: 無法通過索引查詢,修改列定義代價較高:需要alter table。

許可權控制使用例子:

create table acl_test(
id int not null auto_increment,
perms set('can_read', 'can_write', 'can_delete'),
primary key(id)
);

insert into acl_test(perms)
values
('can_read,can_write'),
('can_read,can_delete')
;

select *
from acl_test
where find_in_set('can_write', perms)
;

查詢結果:

1.1.6 主鍵及其他

主鍵最好選擇自增id為主鍵,對資料效能比較好,同時相對應orm也支援,可能存在某些orm層框架不支援聯合主鍵的情況。

ip與int轉換函式: inet_aton(), inet_ntoa()。

1.2 schema錯誤設計

  • 列太多(幾千個列)
  • join 太多,mysql 限制最多可以join 61張表,實際為了效能考慮,最好一次查詢join不超過12張表
  • 錯誤使用列舉, 如一個列舉欄位儲存國家,列舉值有幾十上百個, 更好的解決辦法是使用關聯表去儲存國家資訊,用整數去對映。