schema與數據類型優化-高性能mysql

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

0.前言

在項目中,數據庫表列有兩個text字段,用來存儲大文本,在數據規模達到40萬後,若是查詢沒命中索引,發現耗時會達到5s以上,在表中刪除這兩個大字段後,就算全表掃描,耗時也不過0.6s左右。mysql

解決方案:sql

  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張表
  • 錯誤使用枚舉, 如一個枚舉字段存儲國家,枚舉值有幾十上百個, 更好的解決辦法是使用關聯表去存儲國家信息,用整數去映射。
相關文章
相關標籤/搜索