參考: https://zhuanlan.zhihu.com/p/67188414
問(wèn)題引入
在做壓力測(cè)試的時(shí)候,我使用了sysbench 1.1.0-faaff4f版本霸奕,測(cè)試命令如下:
sysbench oltp_read_only.lua --mysql-host=... --mysql-port=3306 --mysql-user=root --mysql-password=1234 --tables=10 --table-size=10000000 --time=600 --report-interval=10 --threads=1024 prepare
問(wèn)題主要出現(xiàn)在這個(gè)--threads身上溜宽,我的壓測(cè)線程數(shù)增長(zhǎng)是從 1,2质帅,4适揉,8,16临梗,32涡扼,64稼跳,128盟庞,256,512汤善,1024什猖,在1024前壓測(cè)都是沒(méi)有問(wèn)題了,但當(dāng)線程數(shù)增長(zhǎng)到了1024后红淡,sysbench 報(bào)錯(cuò)了不狮。
FATAL: MySQL error: 1461 "Can't create more than max_prepared_stmt_count statements (current value: 16382)"
這個(gè)問(wèn)題很好解決,上網(wǎng)一查就知道怎么解決在旱,登錄mysql摇零,調(diào)整這個(gè)變量值的大小即可。
mysql> show global status like 'com_stmt%';
查看如下3個(gè)參數(shù)值:
Com_stmt_close prepare語(yǔ)句關(guān)閉的次數(shù)
Com_stmt_execute prepare語(yǔ)句執(zhí)行的次數(shù)
Com_stmt_prepare prepare語(yǔ)句創(chuàng)建的次數(shù)
Com_stmt_prepare 減去 Com_stmt_close 大于 max_prepared_stmt_count 就會(huì)出現(xiàn)這種錯(cuò)誤桶蝎。那么我們手動(dòng)調(diào)高max_prepared_stmt_count(取值范圍:0 - 1048576驻仅,默認(rèn)16382)即可解決
mysql> set global max_prepared_stmt_count=500000;
那這里就容易再次引發(fā)一個(gè)問(wèn)題,我們?cè)O(shè)置max_prepared_stmt_count為50萬(wàn)登渣,但是我們其實(shí)現(xiàn)在是知其然不知其所以然噪服,max_prepared_stmt_count是干什么的,為什么會(huì)超過(guò)默認(rèn)值這個(gè)才是我們真正應(yīng)該關(guān)心的胜茧。
max_prepared_stmt_count基本介紹
這個(gè)參數(shù)是MySQL的一個(gè)基本參數(shù)粘优,其是用來(lái)限制一個(gè)session內(nèi)最多可以有多少條預(yù)編譯語(yǔ)句,什么是sql的預(yù)編譯呻顽,下面這篇文章講得很好雹顺,網(wǎng)上也有很多參考,大家可以直接查廊遍。
那為什么我們會(huì)超過(guò)max_prepared_stmt_count的默認(rèn)大小16382呢无拗?下面我們就先來(lái)介紹一下sysbench工具中關(guān)于壓測(cè)數(shù)據(jù)庫(kù)的lua腳本。
sysbench 壓測(cè)數(shù)據(jù)庫(kù)的lua腳本個(gè)人理解
sysbench官方默認(rèn)幫我們配置了默認(rèn)壓測(cè)數(shù)據(jù)庫(kù)的腳本:
這幾個(gè)腳本里面的功能我們簡(jiǎn)單說(shuō)明一下:
oltp_common.lua 腳本是提供給其他腳本如oltp_read_only.lua調(diào)用的昧碉,是基本的一系列函數(shù)英染。oltp_read_only.lua 主要的工作根據(jù)壓測(cè)需求來(lái)調(diào)用oltp_common.lua中的函數(shù)揽惹。
sysbench 的基本工作流程:
- prepare:
sysbench oltp_read_only.lua --mysql-host=x.x.x.x --mysql-port=3306 --mysql-user=root --mysql-password=password --tables=10 --table-size=10000000 --threads=1024 prepare
這個(gè)語(yǔ)句的基本作用就是調(diào)用建表語(yǔ)句,建立十張表四康,每張表大小1千萬(wàn)行搪搏。 - run:
sysbench oltp_read_only.lua --mysql-host=10.191.1.235 --mysql-port=6446 --mysql-user=root --mysql-password=vm000 --tables=10 --table-size=10000000 --threads=1024 --time=600 --report_interval=10 run
這個(gè)的過(guò)程實(shí)際是:建立數(shù)據(jù)庫(kù)連接-->預(yù)編譯SQL語(yǔ)句-->執(zhí)行預(yù)編譯語(yǔ)句 - cleanup:
sysbench oltp_read_only.lua --mysql-host=x.x.x.x --mysql-port=3306 --mysql-user=root --mysql-password=password --tables=10 --table-size=10000000 --threads=1024 cleanup
這里的實(shí)際過(guò)程是:直接將數(shù)據(jù)庫(kù)的表drop掉。
從第二步run 我們就可以知道我們?yōu)槭裁磿?huì)出現(xiàn)超過(guò)max_prepared_stmt_count的默認(rèn)大小16382的報(bào)錯(cuò)了闪金。我們先來(lái)看一下oltp_read_only.lua代碼內(nèi)容:
這里面函數(shù) function prepare_statements()中的兩個(gè)if判斷結(jié)果均為true疯溺,這個(gè)是因?yàn)樵谀J(rèn)情況下oltp_common.lua中已經(jīng)指定skip_trx=false, range_selects=true
所以這里一共需要調(diào)用7個(gè)預(yù)編譯函數(shù):
prepare_point_selects()
prepare_begin()
prepare_commit()
prepare_simple_ranges()
prepare_sum_ranges()
prepare_order_ranges()
prepare_distinct_ranges()
因?yàn)槲覀円还灿?0張表,相對(duì)于每一張表都需要執(zhí)行7個(gè)預(yù)編譯語(yǔ)句哎垦,所以我們可以計(jì)算:
在512個(gè)線程并發(fā)下囱嫩,每一個(gè)線程我們可以視之為一個(gè)用戶,每一個(gè)用戶需要在每一張表上預(yù)編譯7條SQL語(yǔ)句漏设,所以一共prepare的語(yǔ)句是:
Com_stmt_prepare = 512 * 10 * 7 = 35840 > 16382(max_prepared_stmt_count statements 默認(rèn)16382)
所以就出現(xiàn)上面的錯(cuò)誤:
FATAL: MySQL error: 1461 "Can't create more than max_prepared_stmt_count statements (current value: 16382)"
這個(gè)是一個(gè)大概的推測(cè)值墨闲,因?yàn)閷?shí)際執(zhí)行時(shí)還會(huì)有出入。這個(gè)數(shù)字可以方便我們?cè)诔霈F(xiàn)上面問(wèn)題后郑口,設(shè)置max_prepared_stmt_count_statements的值有一個(gè)參考的方向鸳碧,而不是盲目地設(shè)置成最大。