POLARDB · 最佳實踐 · POLARDB不得不知道的祕密

## 前言html

POLARDB做爲阿里雲下一代關係型雲數據庫,自去年9月份公測以來,收到了很多客戶的重點關注,今年5月份商業化後,許多大客戶開始陸續遷移業務到POLARDB上,可是因爲POLARDB的不少默認行爲與RDS MySQL兼容版不同,致使不少用戶有諸多使用上的困惑,原本總結了幾點,給你們答疑解惑。另外,本文提到的參數,在新版本上,用戶均可以經過控制檯修改,若是沒有,能夠聯繫售後服務修改。本文適合讀者:阿里雲售後服務,POLARDB用戶,POLARDB內核開發者,須要有基本的數據庫知識,最好對MySQL源碼有部分了解。mysql

## 磁盤空間問題
RDS MySQL在購買的時候須要指定購買的磁盤大小,最大爲3TB。若是空間不夠,須要升級磁盤空間。具體來講,若是實例所在的物理機磁盤空間充足,這個升級磁盤的任務很快就能夠完成,可是若是空間不足,就須要在其餘物理機上重建實例,大實例須要幾天的時間。爲了解決這個問題,POLARDB底層使用存儲集羣的方式,作到磁盤動態擴容,且磁盤擴容過程對用戶無感知,具體來講,默認磁盤空間分配爲規格內存的10倍,當使用了70%,系統就會自動擴容一部分空間,並且擴容不須要中止實例。sql

有了這種機制,POLARDB的存儲能夠作到按照使用量來收費,真正作到使用多少就收多少錢,計費週期是一小時。同時,因爲存儲按量收費,致使許多用戶對存儲的使用量很是敏感,在咱們的控制檯上,有五種空間統計,分別是磁盤空間使用量,數據空間使用量,日誌空間使用量,臨時空間使用量和系統文件空間使用量。數據庫

磁盤空間使用量是後四者之和,數據空間使用量包括用戶建立的全部庫,mysql庫,test庫,performance_schema庫,日誌空間使用量包括redolog,undolog,ibdata1,ib_checkpoint(存儲checkpoint信息),innodb_repl.info(存儲切換信息,物理複製信息等),臨時空間使用量包括socket文件,pid文件,臨時表(大查詢排序用),審計日誌文件,系統文件空間使用量包括錯誤日誌,慢日誌,general日誌以及主庫信息(用於構建複製關係)。雖然有四部分空間使用量,但大多數主要被數據空間和日誌空間佔用,數據空間比較好理解,主要就是表空間彙集索引和二級索引的佔用量,可是這個日誌空間不少用戶不是很瞭解,經常提上來的問題是,爲何個人日誌空間佔了100多個G,而數據空間就幾個G,這裏簡單解釋一下。緩存

日誌空間使用量,如上所述,有不少組成部分。redolog,主要用來構建物理複製,同時也能夠被當作增量日誌來支持還原到時間點/克隆實例的任務,相似原生的binlog,文件名按順序遞增,主節點產生日誌,只讀節點/災備節點應用日誌,同時後臺管控任務會定時上傳redolog(只要發現新的就當即上傳)而且定時刪除(目前一小時觸發一次刪除任務),具體大小與DML總量有關。undolog,主要用來構建數據歷史版本以支持MVCC機制和回滾機制,不一樣於RDS MySQL的undolog都在ibdata1文件中,POLARDB的undolog大部分是以獨立表空間/文件存在,具體大小與用戶使用習慣有關。ibdata1,主要存儲系統元數據信息等系統信息,具體大小與用戶表數量有關,可是通常不會太大。ib_checkpoint,這個是POLARDB特有的,用於存儲checkpoint信息,大小固定。innodb_repl.info也是POLARDB獨有的,存儲物理複製相關的信息以及切換信息,通常不會太大。因而可知,日誌空間使用量雖然也有不少組成部分,但主要是被redolog日誌和undolog日誌佔用。多線程

### redolog日誌佔用
redolog日誌,因爲對數據的修改都會記錄redolog,因此對數據修改的越快,redolog產生的速度就會越快,並且上傳OSS(保留下來作增量日誌)的速度有限,因此在實例導數據階段,會致使redolog堆積,當導入完成後,redolog會慢慢上傳完而後刪除,這樣空間就會降下來,可是不會徹底降爲0。具體緣由須要介紹一下:目前全部規格,redolog大小都爲1G,被刪除的redolog不會立刻被刪除,而是放入一個緩衝池(rename成一個臨時文件),當須要新的redolog時候,先看看緩衝池裏面還有沒有可用的文件,若是有直接rename成目標文件,若是沒有再建立,這個優化主要是爲了減小建立新文件時的io對系統的抖動,緩衝池的大小由參數`loose_innodb_polar_log_file_max_reuse`控制,默認是8,若是用戶想減小緩存池的文件個數,就能夠減小這個參數從而減小日誌空間佔用量,可是在壓力大的狀況下,性能可能會出現週期性的小幅波動。因此當寫入大量數據後,即便redolog都被上傳,默認也有8G的空間用做緩存。注意,調整這個參數後,緩衝池不會馬上被清空,隨着dml被執行,纔會慢慢減小,若是須要當即清空,建議聯繫售後服務架構

另外,POLARDB會提早建立好下一個須要寫的redolog日誌(每一個日誌都是固定的1G,即便沒有被寫過),主要目的是噹噹前的redolog被寫完後,能快速的切換到下一個,所以,也會佔用額外1G空間。此外,後臺定時刪除任務目前是一個小時清理一次(還有優化的空間),可是不會清理到最後一個日誌,會保留一個日誌,主要用來作按時間點還原任務。併發

接下來,舉個經典的例子,方便理解上述的策略:
```
mysql> show polar logs;
+-----------------+----------------+-------------+
| Log_name | Start_lsn | Log_version |
+-----------------+----------------+-------------+
| ib_logfile41008 | 19089701633024 | 100 |
| ib_logfile41009 | 19090775372800 | 100 |
+-----------------+----------------+-------------+
2 rows in set (0.00 sec)異步

mysql> show polar status\G
......
-----------------
Log File Info
-----------------
2 active ib_logfiles
The oldest log file number: 41008, start_lsn: 19089701633024
The newest log file number: 41009, start_lsn: 19090775372800
Log purge up to file number: 41008
8 free files for reallocation
Lastest(Doing) checkpoint at lsn 19091025469814(ib_logfile41009, offset 250099062)
......
```
`show polar logs`這條命令能夠查看系統中的redolog日誌,上個例子中,ib_logfile41008這文件已經被寫完,可是這個日誌須要被保留用來支持按照時間點還原和克隆實例任務,ib_logfile41009是最後一個redolog,表示目前正在寫的redolog。socket

`show polar status\G`能夠顯示POLARDB不少內部信息,這裏只截取了redolog相關的一部分,前四行就是字面的意思,不具體解釋了。第五行表示緩衝池目前有8個redolog。

另外,上文提到過,POLARDB會提早建立一個redolog用以快速的切換,名字通常是最後一個文件編號加一,因此是ib_logfile41010。

結合這些信息,就能夠推斷出,目前系統中redolog佔用量爲11G = 8G(緩衝池中的)+1G(保留的ib_logfile41008)+1G(正在被寫的ib_logfile41009)+1G(提早建立的ib_logfile41010)。

另外,透露一個好消息,咱們內部正在調研redolog日誌不收費的可行性,若是經過驗證,這部分佔用的空間將不會收取用戶費用。

### undolog日誌佔用
講完了redolog日誌,接下里講講undolog日誌。上文說過在POLARDB中undolog大部分是以獨立表空間存在的,也就是說是獨立的文件,而不是彙集在ibdata1文件中。目前分了8個文件,文件名爲undo001-undo008,每一個文件默認初始大小爲10M,會隨着使用增大,在某些不推薦的用法下,會致使undolog空間增加很快。這裏簡單舉個例子,可使undolog撐的很大:使用`START TRANSACTION WITH consistent snapshot`開啓一個事務,注意要在RR隔離級別下,而後開啓另一個鏈接,對庫中的表進行高頻率的更新,可使用sysbench輔助,很快,就會發現undolog膨脹。從數據庫內核的角度來說,就是因爲一個很老的readview,致使須要把不少的歷史版本都保留下來,從而致使undolog膨脹。在線上,每每是一個大查詢或者一個長時間不提交的事務致使undolog膨脹。undolog膨脹後,即便全部事務都結束後,也不會自動縮小,須要使用下文的方法進行在線truncate。

目前,用戶還不能直接查看undolog的佔用量,後續咱們會在information_schema加上,方便用戶查看,可是能夠經過間接的方法:若是控制檯上顯示日誌佔用量很大,可是redolog佔用量很小,那麼通常就是undolog了,由於其餘幾個都佔用很小的空間,幾乎能夠忽略不計。

若是發現undolog佔用量比較大,POLARDB也有辦法清理。原理是,等undolog所對應的事務都結束後,把清理開關打開,若是發現大小超過執行大小的undo tablespace,就會在purge線程中進行undo的truncate。儘可能在業務低峯期進行,而且保證沒有大事務長事務。具體操做方法就兩步,首先調整`innodb_max_undo_log_size`大小,這個參數表示當每一個undo tablespace大於這個值時候,後續會把它縮小,從新調整爲10M。接着,打開truncate開關`innodb_undo_log_truncate`,這樣,後臺線程就會把全部大於`innodb_max_undo_log_size`設置的undo tablespace調整爲10M。注意,這裏要保證沒有大事務長事務,由於後臺線程會等待undo tablespace中全部事務都提交後,纔會下發命令,同時也要保證`innodb_undo_logs`大於等於2。另外,不建議這個功能長期開着,若是在控制檯發現日誌佔用量減小了,建議關閉truncate功能,由於其有可能在您業務高峯期運行,致使數據庫延遲

## DDL與大事務問題
若是有一個大事務或者長事務長時間未提交,因爲其長期持有MDL讀鎖,這個會帶來不少問題。在RDS MySQL上,若是後續對這張表又有DDL操做,那麼這個操做會被這個大事務給堵住。在POLARDB上,這個問題更加嚴重,簡單的說,若是隻讀實例上有一個大事務或者長期未提交的事務,會影響主實例上的DDL,致使其超時失敗。糾其本質的緣由,是由於POLARDB基於共享存儲的架構,所以在對錶結構變動前,必須保證全部的讀操做(包括主實例上的讀和只讀實例上的讀)結束。

具體解釋一下POLARDB上DDL的過程。在DDL的不一樣階段,當須要對錶進行結構變動前,主實例本身獲取MDL鎖後,會寫一條redolog日誌,只讀實例解析到這個日誌後,會嘗試獲取同一個表上的MDL鎖,若是失敗,會反饋給主實例。主實例會等待全部只讀實例同步到最新的複製位點,即全部實例都解析到這條加鎖日誌,主實例同時判斷是否有實例加鎖失敗,若是沒有,DDL就成功,不然失敗回滾。

這裏涉及到兩個時間,一個是主實例等待全部只讀實例同步的超時時間,這個由參數`loose_innodb_primary_abort_ddl_wait_replica_timeout`控制,默認是一個小時。另一個是隻讀實例嘗試加MDL鎖的超時時間,由參數`loose_replica_lock_wait_timeout`控制,默認是50秒。能夠調整這兩個參數來提早結束回滾DDL,經過返回的錯誤信息,來判斷是否有事務沒結束。
`loose_innodb_primary_abort_ddl_wait_replica_timeout`建議比`loose_replica_lock_wait_timeout `大。

舉個實際例子方便理解:
用戶能夠經過命令`show processlist`中的State列觀察,若是發現`Wait for syncing with replicas`字樣,那麼表示這條DDL目前處在等待只讀節點同步的階段。若是超過`loose_innodb_primary_abort_ddl_wait_replica_timeout`設置的時間,那麼主節點會返回錯誤:
```
ERROR HY000: Rollback the statement as connected replica(s) delay too far away. You can kick out the slowest replica or increase variable 'innodb_abort_ddl_wait_replica_timeout'
```
若是沒有超時,那麼主節點會檢查是否全部只讀節點都成功獲取MDL鎖了,若是失敗,那麼主節點依然會返回錯誤:
```
ERROR HY000: Fail to get MDL on replica during DDL synchronize
```

若是主實例返回第二個錯誤,那麼建議用戶檢查一下主實例以及全部只讀實例上是否有未結束的大查詢或者長時間未提交的事務。

這裏順便介紹一下大事務長事務的防範手段。參數`loose_max_statement_time`能夠控制大查詢的最大執行時間,超過這個時間後,會把查詢kill掉。參數`loose_rds_strict_trx_idle_timeout`能夠控制空閒事務的最長存活時間,當一個事務空閒狀態超過這個值時候,會主動把這個鏈接斷掉,從而結束事務,注意,這個參數應該比`wait_timeout/interactive_timeout`小,不然無效。

## 查詢緩存問題
在MySQL低版本,查詢緩存(Query Cache)能提升查詢的性能,尤爲是更新少的狀況下,可是因爲其自己也容易成爲性能瓶頸,因此在最新的MySQL中此特性已經被移除。POLARDB目前的版本兼容MySQL 5.6,因此用戶依然可使用查詢緩存,可是咱們仍是建議不使用,由於咱們在引擎存儲層作了不少優化,即便不用查詢緩存依然有很好的性能。

因爲POLARDB使用了物理複製,不一樣於binlog的邏輯複製,查詢緩存在只讀實例上的失效,依然須要經過redolog來保證,即當某條查詢緩存失效的時候,須要經過redolog來通知全部只讀節點,讓他們把對應的查詢記錄也失效掉,不然經過只讀節點會讀到歷史版本的數據。

當查詢緩存失效時,會寫redolog通知全部只讀節點,這個機制默認是關閉的,經過參數`loose_innodb_primary_qcache_invalid_log`來控制。

綜上所示,若是在只讀節點上開啓了查詢緩存(只要有一個開啓),那麼必須在主節點上開啓`loose_innodb_primary_qcache_invalid_log`,不然只讀節點會讀到歷史版本的數據。考慮到HA切換會切換到任意一個只讀節點,所以建議若是開啓了查詢緩存,在全部只讀節點上也把`loose_innodb_primary_qcache_invalid_log`開啓。

## 讀寫分離問題
POLARDB自帶一個只讀實例,增減只讀實例很是快速,因此用戶很是適合使用讀寫分離的功能,可是從目前用戶的反饋來看,若是在插入數據後馬上查詢,很容易查詢到以前舊版的數據,爲了解決這個問題,咱們給出兩種解法。一種是經過POLARDB數據庫內核的強同步保證主實例和只讀節點數據一致,另一種是經過數據庫前面的PROXY層來解決。下面簡單介紹一下。

POLARDB集羣基於物理複製構建,目前複製除了支持常規的異步複製(默認),半同步複製以外,還有強同步複製,即當事務提交時,只有當指定的只讀實例應用完redolog日誌後,主實例纔給用戶返回成功。這樣即便後續的讀請求發送到了只讀節點,也能保證讀到最新的數據。可是這個配置會致使性能大幅度降低,只有默認異步複製的三分之一左右,在使用以前請作詳細的測試。簡單說一下配置過程:

首先須要在主實例上設置:設置`loose_innodb_primary_sync_slave`爲3,目的是告訴主實例,它鏈接的只讀實例會有強同步的需求。接着在須要強同步的只讀實例上把參數`loose_slave_trans_sync_level `設置爲2,注意這個參數須要重啓實例。另外,先設置主實例,再設置只讀實例的順序不能亂。設置成功後,在主實例上執行`show polar replicas;`(這個命令能夠查看全部的只讀實例),在`sync_level`這一列,能夠發現由默認的0變成了2,這就表示強同步開啓成功了。若是須要關閉強同步,在主實例上設置`loose_innodb_primary_sync_slave`爲0,只讀節點上設置`loose_slave_trans_sync_level `設置爲0便可,注意設置的順序依然不能亂。此外,若是強同步的只讀實例在`loose_innodb_primary_sync_slave_timeout`後還沒返回,強同步複製退化爲異步複製,還能夠經過`loose_innodb_primary_sync_slave`參數控制當只讀節點掉線時是否馬上退化爲異步複製。

另一種解決辦法是經過PROXY來解決。主實例每次作完更新就會把當前的日誌位點發給PROXY,同時PROXY也會按期去輪詢最大的日誌位點,當PROXY須要把後續的查詢發到只讀實例上時,首先會判斷只讀實例是否應用到了最新的位點,若是不是,就把請求轉發到主實例。這個策略操做的單位是鏈接,即經過這種方法能保證同一個鏈接中讀到的必定是最新的數據。這種方法雖然會致使主庫的壓力變大,可是其對性能影響較小,是一種推薦的方法。若是用戶須要使用,聯繫售後作一次小版本升級,便可開放這個功能。

## BINLOG問題
POLARDB使用基於redolog的物理複製來構建複製關係,不依賴BINLOG,所以BINLOG默認是關閉的,可是許多用戶須要使用BINLOG將數據同步到第三方數據倉庫以方便作複雜的數據分析,因此有不少開啓BINLOG的需求。用戶能夠開啓BINLOG,可是與RDS不一樣,後臺不但不會有任務定時上傳備份BINLOG,並且也不會有按期刪除BINGLOG的任務,徹底須要用戶本身控制什麼時候刪除,不然會致使BINLOG堆積,從而形成更多的存儲成本。由於POLARDB不依賴BINLOG複製,咱們不清楚用戶已經消費了多少日誌(有可能用戶的SLAVE端使用了相似複製延遲的技術),所以,須要用戶本身決定什麼時候清除日誌

用戶能夠經過主實例(考慮到HA切換,最好把全部只讀實例也打開)參數`loose_polar_log_bin`打開BINLOG(須要重啓),BINLOG就會自動存儲在日誌目錄下,空間統計在日誌空間使用量裏面。能夠經過常規的`show master logs`查看BINLOG。每一個BINLOG的大小,BINLOG cache的大小,BINLOG格式等參數均可以經過控制檯調整。這裏注意下,因爲POLARDB使用的是自研的存儲系統,`sync_binlog`參數無效,所以沒有開放。

若是用戶須要刪除無用的BINLOG,目前有一種方法:經過調節參數`loose_expire_logs_hours`來控制BINLOG自動刪除的時間,這個參數表示自BINLOG建立後多久系統自動將它刪除,單位是小時。注意,刪除前請務必確保BINLOG已經無效,誤刪除後將沒法恢復(BINLOG是否須要後臺上傳OSS來做爲備份,這個需求咱們會參考用戶的反饋來決定是否支持)。

目前,開啓BINLOG有一個限制:當底層存儲系統升級的時,開啓BINLOG的實例不可服務時間目前是分鐘級別的,不開啓BINLOG的實例是秒級別的。因此,若是用戶對實例可用性要求比較高,能夠等咱們優化後再開啓BINLOG

## 限制問題
POLARDB因爲使用了自研的文件系統和自研的塊設備存儲系統,所以在一些限制上與RDS MySQL有所不一樣。

因爲文件系統是集成在數據庫裏面的,即數據庫與文件系統共用一個進程,因此文件系統會佔用一部分的規格內存。另外不一樣規格的文件個數也有上限。目前存儲最大支持到10000GB。

此外,文件名,即數據庫中的表名和庫名都不能超過63個字符,實際使用的時候最好控制在55個字符如下,由於還有.frm,.ibd後綴,中間過程臨時表等。詳細的說明見[這裏](https://help.aliyun.com/document_detail/72671.html?spm=5176.10695662.1996646101.searchclickresult.38b34a84FpSWtb)

## 併發鏈接問題
數據庫最佳性能的線程數通常是CPU核數的2-3倍,線程數太少,不容易發揮出多線程的優點,線程數太多,又容易致使上下文切換過多以及鎖爭搶嚴重的問題。不幸的是,不少用戶每每會建立不少併發鏈接,致使數據庫CPU打滿,性能低下。

爲了解決頻繁建立釋放鏈接的問題,即高頻短鏈接問題,能夠調大`thread_cache_size`,從而減小頻繁建立鏈接的開銷。另外,也建議用戶使用客戶端鏈接池來代替高頻短鏈接的方案。

爲了解決高併發鏈接的問題,可使用Thread Pool功能。在Thread Pool模式下,用戶鏈接和處理線程再也不是一一對應關係。處理線程的數量是一個可控的數量,不會隨着用戶鏈接數的增多而大幅增長,這樣能夠減小高併發場景下線程上下文切換的消耗。

用戶能夠經過調整參數`loose_thread_handling`爲`pool-of-threads`來打開Thread Pool功能。同時,建議調整參數`thread_pool_size`爲實例CPU核數,其餘參數保持默認便可。

Thread Pool比較適合短小的查詢和更新,大事務大查詢會下降其效果。用戶須要依據業務模型來斟酌。另外須要注意一點,Thread Pool不會提升性能,可是其能穩定高併發場景下的性能。

## 總結本文簡單介紹了POLARDB常見的幾種問題,大多數來源於用戶真實的反饋。咱們也在不斷的探索更多的功能以及更好的交互。若是在使用POLARDB中遇到疑惑,不要猶豫請馬上聯繫咱們,咱們會給您最滿意的答覆,謝謝對POLARDB的支持。

相關文章
相關標籤/搜索