來自于高性能mysql的一部分總結(jié)。
1.schema和數(shù)據(jù)類型優(yōu)化
選擇數(shù)據(jù)類型的原則:
1. 小。
小,占磁盤小,占內(nèi)存小,占cpu少。
比如varchar字段,varchar(10)和varchar(255),存儲(chǔ)“a”的時(shí)候,雖然都占兩個(gè)字節(jié),但是在從磁盤讀取到內(nèi)存的時(shí)候,內(nèi)存卻要準(zhǔn)備255字符對(duì)應(yīng)的大小的內(nèi)存塊,比如排序操作,或者臨時(shí)表的時(shí)候。因?yàn)閮?nèi)存并不知道varchar字段到底真正存了一個(gè)多大的數(shù)據(jù),所以只能最壞打算。
2. 簡(jiǎn)單
整型比字符串簡(jiǎn)單,字符串有字符集、校對(duì)規(guī)則。
使用日期類型而不是字符串。
使用整形存儲(chǔ)ip,而不是字符串。
3. 避免null
null需要更多存儲(chǔ)空間,需要多一個(gè)字節(jié)標(biāo)記是否為null。
mysql對(duì)null需要特殊的處理。
1.1 數(shù)據(jù)類型
1.1.1 整型類型
tinyint、smallint、mediumint、int、bigint,分別占8、16、24、36、64位存儲(chǔ)空間。
對(duì)于int(11)這樣規(guī)定大小是無意義的,因?yàn)閕nt不管規(guī)定多大的大小,都不會(huì)影響其占的字節(jié)數(shù)以及大小范圍,只會(huì)對(duì)一些mysql交互工具顯示產(chǎn)生影響。
1.1.2 浮點(diǎn)型
Decimal除了存儲(chǔ)小數(shù)之外,可以存儲(chǔ)比bigint還大的數(shù)。
float、double和decimal都可以表示小數(shù),但是float和double使用的是標(biāo)準(zhǔn)的浮點(diǎn)運(yùn)算,cpu直接使用原聲的浮點(diǎn)運(yùn)算,所以可能會(huì)導(dǎo)致結(jié)果的不精確。(比如java,double a = 1.0f - 0.1f = 0.8999999這種現(xiàn)象)。但是decimal是精確計(jì)算,mysql服務(wù)器自身實(shí)現(xiàn)了對(duì)decimal的精確計(jì)算處理。decimal占用空間大,decimal(18,9),整數(shù)位占4個(gè)字節(jié),小數(shù)位占4個(gè)字節(jié),小數(shù)點(diǎn)占1個(gè)字節(jié)。
1.1.3 字符串類型
varchar 比char更節(jié)省空間,除非row_format=fix定長(zhǎng)控制(幾乎不會(huì))。
varchar需要1到2個(gè)字節(jié),記錄字符串長(zhǎng)度,小于255長(zhǎng)度,用1個(gè)字節(jié)記錄,大于,用2個(gè)字節(jié)記錄。
varchar是變長(zhǎng)的,所以u(píng)pdate操作的時(shí)候,可能導(dǎo)致頁分裂
varchar(255)和varchar(10)存儲(chǔ)“a”這個(gè)字符,磁盤上占用的空間是一樣的,但是當(dāng)讀取到內(nèi)存,進(jìn)行排序或者生成臨時(shí)表的時(shí)候,占用的空間是不一樣的,內(nèi)存對(duì)于varchar(255)不知道其實(shí)際存儲(chǔ)了多大的字符,所以會(huì)按最壞的情況,分配255個(gè)空間,所以為了節(jié)省cpu和內(nèi)存,varchar需要盡可能小。
char存儲(chǔ),會(huì)把末尾空格刪掉,比較的時(shí)候會(huì)填充空格進(jìn)行比較(這不分存儲(chǔ)引擎,因?yàn)檫@個(gè)操作是mysql服務(wù)層做的),同樣大小的字符,char比varchar節(jié)省空間,因?yàn)関archar需要1或2個(gè)字節(jié)記錄字符串長(zhǎng)度。
還有一個(gè)和char和varchar性質(zhì)類似的是,binary和varbinary,binary存儲(chǔ)的是二進(jìn)制字符串。
對(duì)于隨機(jī)字符串,比如md5()或uuid(),因?yàn)榇笮‰S機(jī),所以,插入位置是隨機(jī)的,會(huì)造成隨機(jī)訪問,頁分裂、聚簇索引碎片等,所以insert會(huì)慢,select 也會(huì)慢,因?yàn)檫壿嬑恢孟噜彽臄?shù)據(jù)物理位置不同,局部性原理失效(局部性原理是說,磁盤預(yù)讀幾頁數(shù)據(jù)緩存起來,局部性原理認(rèn)為,訪問這個(gè)位置的數(shù)據(jù),很可能會(huì)訪問這個(gè)位置附近的數(shù)據(jù))。
對(duì)于uuid這種隨機(jī)字符串,可以使用unhex()函數(shù)轉(zhuǎn)成16字節(jié)的數(shù)字存儲(chǔ),檢索的時(shí)候再hex()轉(zhuǎn)換回來。
blob和text類型
因?yàn)閮?nèi)存不知道其實(shí)際存儲(chǔ)了多少個(gè)字符,所以極其好性能,會(huì)使用磁盤臨時(shí)表,可以使用substring()截取成字符串,來使用內(nèi)存臨時(shí)表。
當(dāng)然,這種字段,盡可能不用。
1.1.4 枚舉
枚舉排序,使用的枚舉對(duì)應(yīng)的整形,而不是字符串,所以如果對(duì)此有要求的時(shí)候,定義枚舉的時(shí)候,按照字符串的順序定義。
枚舉的一個(gè)不好的地方是:修改枚舉的時(shí)候,使用的是alter table操作。所以我覺得對(duì)于枚舉,還是直接使用int類型,在服務(wù)層做轉(zhuǎn)換比較好。
1.1.5 日期類型
不管什么日期類型,都只能精確到秒,如果需要存儲(chǔ)微妙,則可以使用bigint存儲(chǔ)微妙級(jí)別時(shí)間戳,或者double存儲(chǔ)秒之后的部分。
timestamp時(shí)間范圍是1970到2038,而且依賴時(shí)區(qū),服務(wù)器、客戶端連接都有時(shí)區(qū)設(shè)置。
1.1.6 位數(shù)據(jù)類型
bit,存儲(chǔ)的是二進(jìn)制,檢索的時(shí)候查出來的是這個(gè)二進(jìn)制表示的ascii值。
避免使用這種類型。
如果要存儲(chǔ)boolean類型,true or false,可以使用可為null的char(0),char(0)占用一個(gè)位,可以表示兩種情況,null或空字符串。(我覺得沒必要)
1.2 schema設(shè)計(jì)陷阱
1.太多的列
mysql服務(wù)層通過存儲(chǔ)引擎api,以行緩沖格式拷貝數(shù)據(jù),然后在服務(wù)層解碼成各個(gè)列,所以如果一行有太多的列的話,行緩沖數(shù)據(jù)解碼成各個(gè)列的消耗是很大的。
2. 太多的關(guān)聯(lián)
mysql限制了每個(gè)關(guān)聯(lián)最多只能是61張表。太多關(guān)聯(lián)的弊端不必說。
1.3.反范式
以更新代碼換取查詢性能,以及排序性能。
子表的統(tǒng)計(jì)數(shù)據(jù)記錄在父表,省的每次查詢都要count()統(tǒng)計(jì)子表。
1.4. 緩存表和匯總表
匯總表,把一些定時(shí)匯總,得到一個(gè)不精確的數(shù)據(jù),加快查詢。精確查詢則,統(tǒng)計(jì)匯總表數(shù)據(jù)的和,加上匯總表數(shù)據(jù)到真實(shí)時(shí)間這個(gè)區(qū)間的數(shù)據(jù)和。
緩存表:把一個(gè)表的某些字段,緩存到另一個(gè)表中。
1.5 加快alter table的速度
線上alter table會(huì)鎖住整個(gè)表,可以使用新結(jié)構(gòu)創(chuàng)建一個(gè)空表,然后數(shù)據(jù)遷移完之后,重命名表替換舊表。有工具可以幫助做這一系列操作。比如:online-schema-change