innodb buffer pool size 設(shè)置過小導(dǎo)致的問題

在上一篇文章《The total number of locks exceeds the lock table size 異常處理》中已經(jīng)提到了在innodb buffer pool size設(shè)置較小遇到大事務(wù)時(shí)導(dǎo)致大事務(wù)執(zhí)行失敗。除了文章到提到的異常,還會(huì)伴隨另外一個(gè)waring或者異常。

在同一個(gè)實(shí)例的錯(cuò)誤日志中發(fā)現(xiàn)如下信息:

180306 ?7:48:41 ?InnoDB: WARNING: over 67 percent of the buffer pool is occupied by

InnoDB: lock heaps or the adaptive hash index! Check that your

InnoDB: transactions do not set too many row locks.

InnoDB: Your buffer pool size is 1191 MB. Maybe you should make

InnoDB: the buffer pool bigger?

InnoDB: Starting the InnoDB Monitor to print diagnostics, including

InnoDB: lock heap and hash index sizes.

超過67%的buffer pool 空間被lock heap或者AHI占用,檢查事務(wù)是否設(shè)置多太多的行鎖,buffer pool只有1191M,需要設(shè)置buffer pool更大一些。

提示信息說的很明顯,lock heap 或者AHI占用了過多的buffer pool空間(也就是說留給各個(gè)block list的空間不足),這里提示的是67%,上一篇文章中,free list + lru list < buffer pool / 4 則會(huì)使得事務(wù)終止。

那說明在這之前錯(cuò)誤日志已經(jīng)已有提示該問題?,F(xiàn)在來分析看看這個(gè)warning產(chǎn)生的原因:

還提示信息是下面函數(shù)產(chǎn)生(mysql 5.5.9,在mysql 5.7中則是buf_LRU_check_size_of_non_data_objects函數(shù)):

/******************************************************************//**

Returns a free block from the buf_pool. The block is taken off the

free list. If it is empty, blocks are moved from the end of the

LRU list to the free list. 該函數(shù)從free list返回一個(gè)free block,如果free list 為空,則從LRU list的尾部移除一個(gè)到free list,再返回。

@return the free control block, in state BUF_BLOCK_READY_FOR_USE */

UNIV_INTERN

buf_block_t*

buf_LRU_get_free_block(

/*===================*/

? ? ? ? buf_pool_t* ? ? buf_pool, ? ? ? /*!< in: buffer pool instance */

? ? ? ? ulint ? ? ? ? ? zip_size) ? ? ? /*!< in: compressed page size in bytes,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? or 0 if uncompressed tablespace */

{

? ? ? ? buf_block_t* ? ?block ? ? ? ? ? = NULL;

? ? ? ? ibool ? ? ? ? ? freed;

? ? ? ? ulint ? ? ? ? ? n_iterations ? ?= 1;

? ? ? ? ibool ? ? ? ? ? mon_value_was ? = FALSE;

? ? ? ? ibool ? ? ? ? ? started_monitor = FALSE;

loop:

? ? ? ? buf_pool_mutex_enter(buf_pool);

? ? ? ? if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free)

? ? ? ? ? ? + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 20) { ? ?當(dāng)前free list + lru list 小于buffer pool size的5%

? ? ? ? ? ? ? ? ut_print_timestamp(stderr);

? ? ? ? ? ? ? ? fprintf(stderr,

? ? ? ? ? ? ? ? ? ? ? ? " ?InnoDB: ERROR: over 95 percent of the buffer pool"

? ? ? ? ? ? ? ? ? ? ? ? " is occupied by\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: lock heaps or the adaptive hash index!"

? ? ? ? ? ? ? ? ? ? ? ? " Check that your\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: transactions do not set too many row locks.\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: Your buffer pool size is %lu MB."

? ? ? ? ? ? ? ? ? ? ? ? " Maybe you should make\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: the buffer pool bigger?\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: We intentionally generate a seg fault"

? ? ? ? ? ? ? ? ? ? ? ? " to print a stack trace\n"

? ? ? ? ? ? ? ? ? ? ? ? "InnoDB: on Linux!\n",

? ? ? ? ? ? ? ? ? ? ? ? (ulong) (buf_pool->curr_size

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/ (1024 * 1024 / UNIV_PAGE_SIZE)));

? ? ? ? ? ? ? ? ut_error;

? ? ? ? } else if (!recv_recovery_on

? ? ? ? ? ? ? ? ? ?&& (UT_LIST_GET_LEN(buf_pool->free)

? ? ? ? ? ? ? ? ? ? ? ?+ UT_LIST_GET_LEN(buf_pool->LRU))

< buf_pool->curr_size / 3) {?? ?當(dāng)前free list + lru list 小于buffer pool size的33%

? ? ? ? ? ? ? ? if (!buf_lru_switched_on_innodb_mon) {

? ? ? ? ? ? ? ? ? ? ? ? /* Over 67 % of the buffer pool is occupied by lock

? ? ? ? ? ? ? ? ? ? ? ? heaps or the adaptive hash index. This may be a memory

? ? ? ? ? ? ? ? ? ? ? ? leak! */

? ? ? ? ? ? ? ? ? ? ? ? ib::warn() << "Over 67 percent of the buffer pool is"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " occupied by lock heaps or the adaptive hash"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " index! Check that your transactions do not"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " set too many row locks. Your buffer pool"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " size is "

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? << (buf_pool->curr_size

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/ (1024 * 1024 / UNIV_PAGE_SIZE))

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? << " MB. Maybe you should make the buffer pool"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " bigger?. Starting the InnoDB Monitor to print"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " diagnostics, including lock heap and hash"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " index sizes.";

? ? ? ? ? ? ? ? ? ? ? ? buf_lru_switched_on_innodb_mon = true;

? ? ? ? ? ? ? ? ? ? ? ? srv_print_innodb_monitor = TRUE;

? ? ? ? ? ? ? ? ? ? ? ? os_event_set(lock_sys->timeout_event);

? ? ? ? ? ? ? ? }

。。。

而數(shù)據(jù)讀取,更新,插入都需要free block,則需要調(diào)用該函數(shù)獲取一個(gè)free block。為了保持buffer pool中可用的block (free list + lru list)足夠多,每次調(diào)用都會(huì)判斷一次當(dāng)前的buffer pool的使用情況。

從當(dāng)時(shí)show engine innodb status 的輸出:

BUFFER POOL AND MEMORY

----------------------

Total memory allocated 1277050880; in additional pool allocated 0

Dictionary memory allocated 482195

Buffer pool size ? 76159

Free buffers ? ? ? 71

Database pages ? ? 25314

Old database pages 9324

Modified db pages ?17359

Pending reads 0

Pending writes: LRU 0, flush list 0, single page 0

得到free list 為71,lru list 為25314 ,而buffer size 大小為76159。(71 + 25314)/76159 = 33%因此觸發(fā)可上述warning。

而當(dāng)時(shí)執(zhí)行的語(yǔ)句正是上一篇文章中的insert xxx select xxx 語(yǔ)句:

---TRANSACTION 27A7D9, ACTIVE 7986 sec, process no 10782, OS thread id 140449731196672 inserting

mysql tables in use 2, locked 2

9338873 lock struct(s), heap size 831568312, 49139875 row lock(s), undo log entries 18795462

MySQL thread id 16, query id 8912946 Sending data

INSERT ?INTO

? ? ? ? T_XXX_TMP(

----------------------

可用看出lock heap size 為831568312 = 800M,?831568312? /?1277050880 = 65.12%。 說明lock heap size就已經(jīng)占用了總buffer pool大小的65%。還有其他的一些內(nèi)存使用,超過了67%。

當(dāng)然這里只是warning,如果事務(wù)足夠大的話,則有可能觸發(fā)到95%的的error異常。

從這個(gè)調(diào)用流程來看,事務(wù)執(zhí)行加鎖,消耗了大量的buffer pool ,申請(qǐng)free block讀取數(shù)據(jù),插入數(shù)據(jù)也需要申請(qǐng)block,然后檢測(cè)buffer pool的可用空間,觸發(fā)warning,緊接著執(zhí)行插入邏輯,

調(diào)用buf_LRU_buf_pool_running_out判斷buffer pool的可用空間,觸發(fā)異常,導(dǎo)致事務(wù)中斷。因此在申請(qǐng)free block和執(zhí)行插入操作過程中,都會(huì)判斷buffer pool可用空間,判斷的邏輯都是一樣,

兩者是先后出現(xiàn)的,解決的方式也是一樣。拆小事務(wù)或者調(diào)大innodb buffer poo size。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容