Mysql壓縮解決方案<一>

提到mysql壓縮相關的內容,我們能想到的可能是如下幾種和壓縮相關的場景:

1、客戶端和服務器之間傳輸的數據量太大,需要進行壓縮,節約帶寬

2、mysql某個列的數據量大,只針對某個列的數據壓縮

3、mysql某個或者某幾個表數據太多,需要將表數據壓縮存放,減少磁盤空間的占用

這幾個問題在mysql側都有很好的解決方案 ,針對第1個問題,可以使用mysql的壓縮協議解決;針對第2個問題,可以采用mysql的壓縮和解壓函數完美解決;而針對最復雜的第3個問題,則可以在引擎層面進行解決,目前myisam、innodb、tokudb、MyRocks等引擎都支持表的壓縮。本篇文章要詳細討論的就是此類關于mysql壓縮機制相關 的問題,下面是主要的內容:

一、mysql壓縮協議介紹

1、適用場景

mysql壓縮協議適合的場景是mysql的服務器端和客戶端之間傳輸的數據量很大,或者可用帶寬不高的情況,典型的場景有如下兩個:

a、查詢大量的數據,帶寬不夠(比如導出數據的時候)

b、復制的時候binlog量太大,啟用slave_compressed_protocol參數進行日志壓縮復制

2、壓縮協議簡介

壓縮協議是mysql通信協議的一部分,要啟用壓縮協議進行數據傳輸,需要mysql服務器端和客戶端都支持zlib算法。啟動壓縮協議會導致CPU負載略微上升。使用啟用壓縮協議使用-C參數或者 --compress=true參數啟動客戶端的壓縮功能。如果啟用了-C或者compress=true選項,那么在連接到服務器段的時候,會發送0x0020(CLIENT_COMPRESS)的服務器權能標志位,和服務器端協商通過后(3次握手以后),就支持壓縮協議了。由于采用壓縮,數據包的格式會發生變化,具體的變化如下:

未壓縮的數據包格式:

壓縮后的數據包格式:

大家可能留意到壓縮后的數據報格式有壓縮和未壓縮之分,這個是mysql為了較少CPU開銷而做的一個優化。如果內容小于50個字節的時候,就不對內容進行壓縮,而大于50字節的時候,才會啟用壓縮功能。具體的規則如下:

當第三個字段的值等于0x00的時候,表示當前包沒有壓縮,因此n*byte的內容為1*byte,n*byte,即請求類型和請求內容。

當第三個字段的值大于0x00的時候,表示當前包已采用zlib壓縮,因此使用的時候需要對n*byte進行解壓,解壓后內容為1*byte,n*byte,即請求類型和請求內容。

3、方案實踐

在客戶端連接的時候加上-C或者--compress=true參數。如果是對同步添加壓縮協議支持的時候,則需要配置slave_compressed_protocol=1。下面是采用壓縮協議連接mysql服務端的范例:

mysql -h hostip -uroot -p password --compress

mysqldump -h hostip -uroot -p password -default-character-set=utf8 ?--compress?--single-transaction dbname tablename > tablename.sql

如果需要在主從復制中啟用壓縮傳輸,則在從機開啟slave_compressed_protocol=1參數就OK。

4、壓縮效果

可以通過在mysqldump中使用--compress選項來觀察壓縮傳輸的效果,也可以通過主從復制中已用slave_compressed_protocol參數來觀察壓縮傳輸的效果,很容易看出效果,這里不再截圖說明。

二、mysql列壓縮解決方案

mysql針對列的壓縮目前直接的方案并不支持,映象中騰訊的Tmysql可以直接針對列的壓縮。這里主要介紹一個曲線救國的辦法,那就是在業務層面使用mysql提供的壓縮和解壓函數來針對列進行壓縮和解壓操作。也就是要對某一列做壓縮,就需要在寫入的時候調用COMPRESS函數對那個列的內容進行壓縮,然后存放到對應的列。讀取的時候,使用UNCOMPRESSED函數對壓縮的內容進行解壓縮。

1、適用場景

針對mysql中某個列或者某幾個列數據量特別大,一般都是varchar、text、char等數據類型。

2、壓縮函數簡介

mysql的壓縮函數COMPRESS壓縮一個字符串,然后返回一個二進制串。使用該函數需要mysql服務端支持壓縮,否則會返回NULL,壓縮字段最好采用varbinary或者blob字段類型保存。使用UNCOMPRESSED函數對壓縮過的數據進行解壓。注意,采用這種方式需要在業務側做少量改造。壓縮后的內容存儲方式如下:

a、空字符串就以空字符串存儲

b、非空字符串存儲方式為前4個bype保存未壓縮的字符串,緊接著保存壓縮的字符串

3、方案實踐

字段壓縮方案涉及到的幾個相關的函數如下:

壓縮函數

COMPRESS()

解壓縮函數

UNCOMPRESS()

字符串長度函數

LENGTH()

未解壓字符串長度函數

UNCOMPRESSED_LENGTH()

實踐步驟:

a、創建一張測試表

CREATE TABLE? IF NOT EXISTS `test`.`test_compress` (

`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',

`content` blob NOT NULL COMMENT '內容列',

PRIMARY KEY (`id`)

) ENGINE=InnoDB? DEFAULT CHARSET=latin1 COMMENT='壓縮測試表';

b、網表中插入壓縮的數據

insert into `test`.`test_compress`(content) values(COMPRESS(REPEAT('a',1000)));

c、讀取壓縮的數據

select UNCOMPRESS(content) from `test`.`test_compress`;

d、查詢對應的長度和內容

SELECT UNCOMPRESSED_LENGTH(content) AS length, LENGTH(content) AS compress_length, UNCOMPRESS(content), content FROM `test`.`test_compress`

4、壓縮效果

從上面截圖可以看出壓縮效果比較好,針對text、char、varchr、blob等,如果里面重復的數據越多壓縮效果就越好。

三、InnoDB表壓縮方案解決方案

1、適用場景

采用壓縮表一般都用在由于數據量太大,磁盤空間不足,負載主要體現在IO上,而服務器的CPU又有比較多的余量的場景。

2、表壓縮簡介

a、為什么需要壓縮

目前很多表都支持壓縮,比如Myisam、InnoDB、TokuDB、MyRocks 。由于使用InnoDB主要是不需要做什么改動,對線上完全透明,壓縮方案也非常成熟,因此這里只對InnoDB做詳細說明。對于TokuDB和MyRocks的壓縮方案講在mysql的壓縮方案<二>中撰文說明。

在SSD沒有大量橫行的時候,數據庫幾乎都是IO負載型的,在CPU有大量余量的時候,磁盤IO的瓶頸就已經凸顯出來。而數據的大量存儲,尤其是日志型數據和監控類型的數據,會導致磁盤空間快速增長。硬盤不夠用也會在很多業務中凸顯出來。一種比較好的方式就誕生了,那就是通過犧牲少量CPU資源,采用壓縮來減少磁盤空間占用,以及優化IO和帶寬。尤其針對讀多些少的業務。

SSD出來后,數據庫的IO負載有所降低,但是對于磁盤空間的問題還是沒有很好的解決。因此壓縮表使用還是非常的廣泛。這也就是為什么那么多的引擎都支持壓縮的原因。而innodb在mysql 5.5的時候就支持了壓縮功能,只是壓縮比比較低,通常在50%左右。而tokuDB能達到80%左右,MyRocks的壓縮比能達到70%左右。

注意:壓縮比和你存儲的數據組成有很大的關系,并不是所有的數據都能達到上面所說的壓縮比。如果大部分都是字符串,并且重復的數據比較多,壓縮比會很好。

b、innodb的壓縮介紹

使用innodb壓縮的前提條件是,innodb_file_per_table這個參數要啟用,innodb_file_format這個參數設置成Barracuda。

你可以使用ROW_FORMAT=COMPRESSED來create或者alter表來開啟innodb的壓縮功能,如果沒有指定KEY_BLOCK_SIZE的大小,默認KEY_BLOCK_SIZE為innodb_page_size大小的一半,也可以通過指定KEY_BLOCK_SIZE=n參數來開啟innodb的壓縮功能,n可以為1、2、4、8、16,單位是K。n的值越小,壓縮比越高,消耗的CPU資源也越多。注意32K或者64K的頁不支持壓縮。啟用壓縮后,索引數據也同樣會被壓縮。

你也可以通過調整innodb_compression_level來設置壓縮的級別,級別從1~9,默認是6。級別越低,意味著壓縮比越高,同時也意味著需要更多的CPU資源。

c、壓縮算法

innodb壓縮借助的是著名的zlib庫,采用L777壓縮算法,這種算法在減少數據大小和CPU利用方面很成熟高效。同時這種算法是無損的,因此原生的未壓縮的數據總是能夠從壓縮文件中重構,LZ777實現原理是查找重復數據的序列號然后進行壓縮,所以數據模式決定了壓縮效率,一般而言,用戶的數據能夠被壓縮50%以上。

d、壓縮表在buffer_pool中如何處理

在buffer_pool緩沖池中,壓縮的數據通過KEY_BLOCK_SIZE的大小的頁來保存,如果要提取壓縮的數據或者要更新壓縮數據對應的列,則會創建一個未壓縮頁來解壓縮數據,然后在數據更新完成后,會將為壓縮頁的數據重新寫入到壓縮頁中。內存不足的時候,mysql會講對應的未壓縮頁踢出去。因此如果你啟用了壓縮功能,你的buffer_pool緩沖池中可能會存在壓縮頁和未壓縮頁,也可能只存在壓縮頁。不過可能仍然需要將你的buffer_pool緩沖池調大,以便能同時能保存壓縮頁和未壓縮頁。

mysql采用最少使用(LRU)算法來確定將哪些頁保留在內存中,哪些頁剔除出去,因此熱數據會更多地保留在內存中。當壓縮表被訪問的時候,mysql使用自適應的LRU算法來維持內存中壓縮頁和非壓縮頁的平衡。當系統IO負載比較高的時候,這種算法傾向于講未壓縮的頁剔除,一面騰出更多的空間來存放更多的壓縮頁。當系統CPU負載比較高的時候,mysql傾向于將壓縮頁和未壓縮頁都剔除出去,這個時候更多的內存用來保留熱的數據,從而減少解壓的操作。

e、如何評估KEY_BLOCK_SIZE是否合適

為了更深入地了解壓縮表對性能的影響,在Information Schema庫中有對應的表可以用來評估內存的使用和壓縮率等指標。INNODB_CMP是收集的是某一類的KEY_BLOCK_SIZE壓縮表的整體狀況的信息,匯總的是所有KEY_BLOCK_SIZE壓縮表的統計。而INNODB_CMP_PER_INDEX表則是收集各個表和索引的壓縮情況信息,這些信息對于在某個時間評估某個表的壓縮效率或者診斷性能問題很有幫助。INNODB_CMP_PER_INDEX表的收集會導致系統性能受到影響,必須innodb_cmp_per_index_enabled選項才會記錄,生產環境最好不要開啟。

我們可以通過觀察INNODB_CMP表的壓縮失敗情況,如果失敗比較多,則需要調大KEY_BLOCK_SIZE。一般建議KEY_BLOCK_SIZE設置為8。

3、方案實踐

a、設置好innodb_file_per_table和innodb_file_format參數

SET GLOBAL innodb_file_per_table=1;SET GLOBAL innodb_file_format=Barracuda;

b、創建對應的壓縮表

CREATE TABLE compress_test (c1 INT PRIMARY KEY,content varchar(255)) ROW_FORMAT=COMPRESSEDKEY_BLOCK_SIZE=8;

如果是已經存在的表,則通過alter來修改,SQL如下:

ALTER TABLEcompress_testROW_FORMAT=COMPRESSEDKEY_BLOCK_SIZE=8;

4、壓縮效果

壓縮效果通過線上的一個監控的表修改為壓縮后的文件大小來說明,壓縮前后對比如下:

四、參考文獻

https://dev.mysql.com/doc/refman/5.7/en/innodb-compression-background.html

https://dev.mysql.com/doc/refman/5.7/en/general-tablespaces.html#general-tablespaces-creating

http://www.tocker.ca/2013/10/31/benchmarking-innodb-page-compression-performance.html

http://www.cnblogs.com/mysql-dba/p/5125220.html

http://hutaow.com/blog/2013/11/06/mysql-protocol-analysis/#11

https://my.oschina.net/tangcoffee/blog/362382

http://www.cnblogs.com/lispking/p/3604063.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-table-compression.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-compression-internals.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容