sysbench是一個開源的、基于LuaJIT(LuaJIT 是 Lua 的即時編譯器,可將代碼直接翻譯成機器碼,性能比原生 lua 要高) 的、可自定義腳本的多執行緒基準測驗工具,也是目前用得最多的 MySQL 性能壓測工具,
基于 sysbench,我們可以對比 MySQL 在不同版本、不同硬體配置、不同引數(作業系統和資料庫)下的性能差異,
下面會從 sysbench 的基本用法出發,逐漸延伸到 sysbench 的一些高級玩法,譬如如何閱讀自帶的測驗腳本、如何自定義測驗項等,除此之外,使用 sysbench 對 CPU 進行測驗,網上很多資料都語焉不詳,甚至是錯誤的,所以這次也會從原始碼的角度分析 CPU 測驗的實作邏輯及 --cpu-max-prime 選項的具體含義,
本文主要包括以下幾部分:
- 安裝sysbench
- sysbench用法講解
- 對MySQL進行基準測驗的基本步驟
- 如何分析MySQL基準測驗結果
- 如何使用sysbench對服務器進行測驗
- MySQL常見測驗場景及對應的 SQL 陳述句
- 如何自定義sysbench測驗腳本
安裝 sysbench
下面是 sysbench 原始碼包的安裝步驟,
# yum -y install make automake libtool pkgconfig libaio-devel openssl-devel mysql-devel
# cd /usr/src/
# wget https://github.com/akopytov/sysbench/archive/refs/tags/1.0.20.tar.gz
# tar xvf 1.0.20.tar.gz
# cd sysbench-1.0.20/
# ./autogen.sh
# ./configure
# make -j
# make install
安裝完成后,壓測腳本默認會安裝在 /usr/local/share/sysbench 目錄下,
我們看看該目錄的內容,
# ls /usr/local/share/sysbench/
bulk_insert.lua oltp_insert.lua oltp_read_write.lua oltp_write_only.lua tests
oltp_common.lua oltp_point_select.lua oltp_update_index.lua select_random_points.lua
oltp_delete.lua oltp_read_only.lua oltp_update_non_index.lua select_random_ranges.lua
除了oltp_common.lua是個公共模塊,其它每個 lua 腳本都對應一個測驗場景,
sysbench 用法講解
sysbench 命令語法如下:
sysbench [options]... [testname] [command]
命令中的testname是測驗項名稱,sysbench 支持的測驗項包括:
-
*.lua:資料庫性能基準測驗,
-
fileio:磁盤 IO 基準測驗,
-
cpu:CPU 性能基準測驗,
-
memory:記憶體訪問基準測驗,
-
threads:基于執行緒的調度程式基準測驗,
-
mutex:POSIX 互斥量基準測驗,
command是 sysbench 要執行的命令,支持的選項有:prepare,prewarm,run,cleanup,help,注意,不是所有的測驗項都支持這些選項,
options是配置項,sysbench 中的配置項主要包括以下兩部分:
1. 通用配置項,這部分配置項可通過 sysbench --help 查看,例如,
# sysbench --help
...
General options:
--threads=N number of threads to use [1]
--events=N limit for total number of events [0]
--time=N limit for total execution time in seconds [10]
...
2. 測驗項相關的配置項,各個測驗項支持的配置項可通過 sysbench testname help 查看,例如,
# sysbench memory help
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)
memory options:
--memory-block-size=SIZE size of memory block for test [1K]
--memory-total-size=SIZE total size of data to transfer [100G]
--memory-scope=STRING memory access scope {global,local} [global]
--memory-hugetlb[=on|off] allocate memory from HugeTLB pool [off]
--memory-oper=STRING type of memory operations {read, write, none} [write]
--memory-access-mode=STRING memory access mode {seq,rnd} [seq]
對 MySQL 進行基準測驗的基本步驟
下面以oltp_read_write為例,看看使用 sysbench 對 MySQL 進行基準測驗的四個標準步驟:
prepare
生成壓測資料,默認情況下,sysbench 是通過 INSERT INTO 命令來匯入測驗資料的,如果是使用 LOAD DATA LOCAL INFILE 命令來匯入,sysbench 導數速度能提升30%,具體可參考:使用 LOAD DATA LOCAL INFILE,sysbench 導數速度提升30%
# sysbench oltp_read_write --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=30 prepare
命令中各個選項的具體含義如下:
- oltp_read_write:測驗項,對應的是
/usr/local/share/sysbench/oltp_read_write.lua,這里也可指定腳本的絕對路徑名, - --mysql-host、--mysql-port、--mysql-user、--mysql-password:分別代表 MySQL 實體的主機名、埠、用戶名和密碼,
- --mysql-db:庫名,不指定則默認為
sbtest, - --tables :表的數量,默認為 1,
- --table-size :單表的大小,默認為 10000,
- --threads :并發執行緒數,默認為 1,注意,匯入時,單表只能使用一個執行緒,
- prepare:執行準備作業,
oltp_read_write 用來壓測 OLTP 場景,在 sysbench 1.0 之前, 該場景是通過 oltp.lua 這個腳本來測驗的,不過該腳本在 sysbench 1.0 之后就被廢棄了,但為了跟之前的版本兼容,該腳本放到了 /usr/local/share/sysbench/tests/include/oltp_legacy/ 目錄下,
鑒于 oltp_read_write.lua 和 oltp.lua 兩者的壓測內容完全一致,從 sysbench 1.0 開始,壓測 OLTP 場景建議直接使用 oltp_read_write,
prewarm
預熱,主要是將磁盤中的資料加載到記憶體中,
# sysbench oltp_read_write --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=30 prewarm
除了需要將命令設定為 prewarm,其它配置與 prepare 中一樣,
run
壓測,
# sysbench oltp_read_write --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
其中,
-
--time :壓測時間,不指定則默認為 10 秒,除了 --time,也可通過 --events 限制需要執行的 event 的數量,
-
--report-interval=10 :每 10 秒輸出一次測驗結果,默認為 0,不輸出,
cleanup
清理資料,
# sysbench oltp_read_write --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 cleanup
這里只需指定 --tables ,sysbench 會串行執行 DROP TABLE IF EXISTS sbtest 操作,
如何分析 MySQL 基準測驗結果
下面我們分析下 oltp_read_write 場景下的壓測結果,注:右滑可以看到每個指標的具體含義,
Threads started!
[ 10s ] thds: 64 tps: 5028.08 qps: 100641.26 (r/w/o: 70457.59/20121.51/10062.16) lat (ms,95%): 17.32 err/s: 0.00 reconn/s: 0.00
# thds 是并發執行緒數,tps 是每秒事務數,qps 是每秒運算元,等于 r(讀操作)加上 w(寫操作)加上 o(其他操作,主要包括 BEGIN 和 COMMIT),lat 是延遲,(ms,95%) 是 95% 的查詢時間小于或等于該值,單位毫秒,err/s 是每秒錯誤數,reconn/s 是每秒重試的次數,
[ 20s ] thds: 64 tps: 5108.93 qps: 102192.09 (r/w/o: 71533.28/20440.64/10218.17) lat (ms,95%): 17.32 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 64 tps: 5126.50 qps: 102505.50 (r/w/o: 71756.30/20496.60/10252.60) lat (ms,95%): 17.32 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 64 tps: 5144.50 qps: 102907.20 (r/w/o: 72034.07/20583.72/10289.41) lat (ms,95%): 17.01 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 64 tps: 5137.29 qps: 102739.80 (r/w/o: 71916.99/20548.64/10274.17) lat (ms,95%): 17.01 err/s: 0.00 reconn/s: 0.00
[ 60s ] thds: 64 tps: 4995.38 qps: 99896.35 (r/w/o: 69925.98/19979.61/9990.75) lat (ms,95%): 17.95 err/s: 0.00 reconn/s: 0.00
SQL statistics:
queries performed:
read: 4276622 # 讀操作的數量
write: 1221892 # 寫操作的數量
other: 610946 # 其它操作的數量
total: 6109460 # 總的運算元量,total = read + write + other
transactions: 305473 (5088.63 per sec.) # 總的事務數(每秒事務數)
queries: 6109460 (101772.64 per sec.) # 總的運算元(每秒運算元)
ignored errors: 0 (0.00 per sec.) # 忽略的錯誤數(每秒忽略的錯誤數)
reconnects: 0 (0.00 per sec.) # 重試次數(每秒重試的次數)
General statistics:
total time: 60.0301s # 總的執行時間
total number of events: 305473 # 執行的 event 的數量
# 在 oltp_read_write 中,默認引數下,一個 event 其實就是一個事務
Latency (ms):
min: 5.81 # 最小耗時
avg: 12.57 # 平均耗時
max: 228.87 # 最大耗時
95th percentile: 17.32 # 95% event 的執行耗時
sum: 3840044.28 # 總耗時
Threads fairness:
events (avg/stddev): 4773.0156/30.77 # 平均每個執行緒執行 event 的數量
# stddev 是標準差,值越小,代表結果越穩定,
execution time (avg/stddev): 60.0007/0.01 # 平均每個執行緒的執行時間
輸出中,重點關注三個指標:
- 每秒事務數,即我們常說的 TPS,
- 每秒運算元,即我們常說的 QPS,
- 95% event 的執行耗時,
TPS 和 QPS 反映了系統的吞吐量,越大越好,執行耗時代表了事務的執行時長,越小越好,在一定范圍內,并發執行緒數指定得越大,TPS 和 QPS 也會越高,
使用 sysbench 對服務器進行測驗
除了資料庫基準測驗,sysbench 還能對服務器的性能進行測驗,服務器資源一般包括四大類:CPU、記憶體、IO和網路,sysbench 可對CPU、記憶體和磁盤IO進行測驗,下面我們具體來看看,
cpu
CPU 性能測驗,支持的選項只有一個,即--cpu-max-prime,
CPU 測驗的命令如下:
# sysbench cpu --cpu-max-prime=20000 --threads=32 run
輸出中,重點關注events per second,值越大,代表 CPU 的計算性能越強,
CPU speed:
events per second: 25058.08
下面是 CPU 測驗相關的代碼,可以看到,sysbench 是通過計算--cpu-max-prime范圍內的質數來衡量 CPU 的計算能力的,
質數(prime number)又稱素數,指的是大于 1,且只能被 1 和自身整除的自然數,在代碼實作時,對于自然數 n,一般會用 2 到根號 n 之間的整數去除,如果都無法整除,則意味著 n 是個質數,
int cpu_execute_event(sb_event_t *r, int thread_id)
{
unsigned long long c;
unsigned long long l;
double t;
unsigned long long n=0;
(void)thread_id; /* unused */
(void)r; /* unused */
// max_prime 即命令列中指定的 --cpu-max-prime
for(c=3; c < max_prime; c++)
{
t = sqrt((double)c);
for(l = 2; l <= t; l++)
if (c % l == 0)
break;
if (l > t )
n++;
}
return 0;
}
memory
記憶體測驗,支持的選項有:
- --memory-block-size:記憶體塊的大小,默認為 1KB,測驗時建議設定為 1MB,
- --memory-total-size:要傳輸的資料的總大小,默認為 100GB,
- --memory-scope:記憶體訪問范圍,可指定 global、local,默認為 global,
- --memory-hugetlb:是否從 HugeTLB 池中分配記憶體,默認為 off,
- --memory-oper:記憶體操作型別,可指定 read、write、none,默認為 write,
- --memory-access-mode:記憶體訪問模式,可指定 seq(順序訪問)、rnd(隨機訪問),默認為 seq,
記憶體測驗的命令如下:
# sysbench --test=memory --memory-block-size=1M --memory-total-size=100G --num-threads=1 run
輸出中,重點關注以下部分:
102400.00 MiB transferred (23335.96 MiB/sec)
23335.96 MiB/sec 即資料在記憶體中的順序寫入速率,
fileio
磁盤 IO 測驗,支持的選項有:
- --file-num:需要創建的檔案數,默認為128,
- --file-block-size:資料塊的大小,默認為16384,即16KB,
- --file-total-size:需要創建的檔案總大小,默認為2GB,
- --file-test-mode:測驗模式,可指定 seqwr(順序寫)、seqrewr(順序重寫)、seqrd(順序讀)、rndrd(隨機讀)、rndwr(隨機寫)、rndrw(隨機讀寫),
- --file-io-mode:檔案的操作模式,可指定 sync(同步 IO)、async(異步 IO)、mmap,默認為 sync,
- --file-async-backlog:每個執行緒異步 IO 佇列的長度,默認為 128,
- --file-extra-flags:打開檔案時指定的標志,可指定 sync、dsync、direct,默認為空,沒指定,
- --file-fsync-freq:指定持久化操作的頻率,默認為 100,即每執行 100 個 IO 請求,則會進行一次持久化操作,
- --file-fsync-all:在每次寫入操作后執行持久化操作,默認為 off,
- --file-fsync-end:在測驗結束時執行持久化操作,默認為 on,
- --file-fsync-mode:持久化操作的模式,可指定 fsync、fdatasync,默認為 fsync,fdatasync 和 fsync類似,只不過 fdatasync 只會更新資料,而 fsync 還會同步更新檔案的屬性,
- --file-merged-requests:允許合并的最多 IO 請求數,默認為0,不合并,
- --file-rw-ratio:混合測驗中的讀寫比例,默認為1.5,
磁盤 IO 測驗主要分為以下三步:
# 準備測驗檔案
# sysbench fileio --file-num=1 --file-total-size=10G --file-test-mode=rndrw prepare
# 測驗
# sysbench fileio --file-num=1 --file-total-size=10G --file-test-mode=rndrw run
# 洗掉測驗檔案
# sysbench fileio --file-num=1 --file-total-size=10G --file-test-mode=rndrw cleanup
輸出中,重點關注以下兩部分:
File operations:
reads/s: 4978.26
writes/s: 3318.84
fsyncs/s: 83.07
Throughput:
read, MiB/s: 77.79
written, MiB/s: 51.86
其中,reads/s 加上 writes/s 即我們常說的 IOPS,read, MiB/s 加上 written, MiB/s 即我們常說的吞吐量,
MySQL 常見測驗場景及對應的 SQL 陳述句
接下來會列舉 MySQL 常見的測驗場景及各個場景對應的 SQL 陳述句,
為了讓大家清晰的知道 SQL 陳述句的含義,首先我們看看測驗表的表結構,
除了 bulk_insert 會創建單獨的測驗表,其它場景都會使用下面的表結構,
mysql> show create table sbtest.sbtest1\G
*************************** 1. row ***************************
Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (
`id` int NOT NULL AUTO_INCREMENT,
`k` int NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
bulk_insert
批量插入測驗,
# sysbench bulk_insert --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
下面是 bulk_insert 場景下創建的測驗表,
mysql> show create table sbtest.sbtest1\G
*************************** 1. row ***************************
Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (
`id` int NOT NULL,
`k` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
測驗對應的 SQL 陳述句如下:
INSERT INTO sbtest1 VALUES(?, ?),(?, ?),(?, ?),(?, ?)...
oltp_delete
洗掉測驗,
# sysbench oltp_delete --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
基于主鍵進行洗掉,測驗對應的 SQL 陳述句如下:
DELETE FROM sbtest1 WHERE id=?
oltp_insert
插入測驗,
# sysbench oltp_insert --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?)
oltp_point_select
基于主鍵進行查詢,
# sysbench oltp_point_select --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
SELECT c FROM sbtest1 WHERE id=?
oltp_read_only
只讀測驗,
# sysbench oltp_read_only --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
SELECT c FROM sbtest1 WHERE id=? # 默認會執行 10 次,由 --point_selects 選項控制,
SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?
SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?
SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
oltp_read_write
讀寫測驗,
測驗對應的 SQL 陳述句如下:
SELECT c FROM sbtest1 WHERE id=? # 默認會執行 10 次,由 --point_selects 選項控制,
SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?
SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?
SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
UPDATE sbtest1 SET k=k+1 WHERE id=?
UPDATE sbtest1 SET c=? WHERE id=?
DELETE FROM sbtest1 WHERE id=?
INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?)
oltp_update_index
基于主鍵進行更新,更新的是索引欄位,
# sysbench oltp_update_index --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
UPDATE sbtest1 SET k=k+1 WHERE id=?
oltp_update_non_index
基于主鍵進行更新,更新的是非索引欄位,
# sysbench oltp_update_non_index --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
UPDATE sbtest1 SET c=? WHERE id=?
oltp_write_only
只寫測驗,
# sysbench oltp_write_only --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 語句如下:
UPDATE sbtest1 SET k=k+1 WHERE id=?
UPDATE sbtest1 SET c=? WHERE id=?
DELETE FROM sbtest1 WHERE id=?
INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?)
select_random_points
基于索引進行隨機查詢,
# sysbench select_random_points --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
SELECT id, k, c, pad
FROM sbtest1
WHERE k IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
select_random_ranges
基于索引進行隨機范圍查詢,
# sysbench select_random_ranges --mysql-host=10.0.0.64 --mysql-port=3306 --mysql-user=admin --mysql-password=Py@123456 --mysql-db=sbtest --tables=30 --table-size=1000000 --threads=64 --time=60 --report-interval=10 run
測驗對應的 SQL 陳述句如下:
SELECT count(k)
FROM sbtest1
WHERE k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ?
如何自定義 sysbench 測驗腳本
下面通過 bulk_insert.lua 和 oltp_point_select.lua 這兩個腳本分析下 sysbench 測驗腳本的實作邏輯,
首先看看 bulk_insert.lua,
# cat bulk_insert.lua
#!/usr/bin/env sysbench
cursize=0
function thread_init()
drv = sysbench.sql.driver()
con = drv:connect()
end
function prepare()
local i
local drv = sysbench.sql.driver()
local con = drv:connect()
for i = 1, sysbench.opt.threads do
print("Creating table 'sbtest" .. i .. "'...")
con:query(string.format([[
CREATE TABLE IF NOT EXISTS sbtest%d (
id INTEGER NOT NULL,
k INTEGER DEFAULT '0' NOT NULL,
PRIMARY KEY (id))]], i))
end
end
function event()
if (cursize == 0) then
con:bulk_insert_init("INSERT INTO sbtest" .. thread_id+1 .. " VALUES")
end
cursize = cursize + 1
con:bulk_insert_next("(" .. cursize .. "," .. cursize .. ")")
end
function thread_done(thread_9d)
con:bulk_insert_done()
con:disconnect()
end
function cleanup()
local i
local drv = sysbench.sql.driver()
local con = drv:connect()
for i = 1, sysbench.opt.threads do
print("Dropping table 'sbtest" .. i .. "'...")
con:query("DROP TABLE IF EXISTS sbtest" .. i )
end
end
下面,我們看看這幾個函式的具體作用:
- thread_init():執行緒初始化時呼叫,這個函式常用來創建資料庫連接,
- prepare():指定 prepare 時呼叫,這個函式常用來創建測驗表,生成測驗資料,
- event():指定 run 時呼叫,這個函式會定義需要測驗的 SQL 陳述句,
- thread_done():執行緒退出時呼叫,這個函式常用來關閉 Prepared Statements 和資料庫連接,
- cleanup():指定 cleanup 時呼叫,這個函式常用來洗掉測驗表,
如果我們要自定義測驗腳本,只需實作這幾個函式即可,
如果我們要基于 sbtest 表自定義測驗項,就要分析 oltp*.lua 腳本的實作邏輯,
下面,以 oltp_point_select.lua 腳本為例,
#!/usr/bin/env sysbench
...
require("oltp_common")
function prepare_statements()
-- point_selects 是 oltp_point_select 中支持的選項,默認為 10,這里調整為了 1,
sysbench.opt.point_selects=1
prepare_point_selects()
end
function event()
execute_point_selects()
end
與 bulk_insert.lua 不一樣的是,oltp_point_select.lua 只簡單的定義了兩個函式:prepare_statements()和event(),實際上,不僅僅是 oltp_point_select.lua,其它 oltp*.lua 腳本也只定義了這兩個函式,
雖然只定義了這兩個函式,但腳本匯入了 oltp_common 模塊,所以實際上,腳本中的 prepare_point_selects(),execute_point_selects() 以及 bulk_insert.lua 中的 thread_init(),prepare(),thread_done(),cleanup() 都是在oltp_common.lua這個公共模塊中定義的,
接下來,我們看看 prepare_point_selects() 和 execute_point_selects() 這兩個函式的實作邏輯,
首先看看prepare_point_selects(),
它呼叫的是prepare_for_each_table(),prepare_for_each_table()是一個基礎函式,所有prepare 相關的函式都會呼叫prepare_for_each_table(), 只不過不同的 prepare 函式會傳入不同的引數名,
prepare_for_each_table()會填充兩張表(Lua 中的表既可用來表示陣列,也可用來表示集合):stmt 和 param,其中,stmt 用來存盤 Prepared Statements 陳述句,param 用來存盤 Prepared Statements 陳述句相關的引數型別,
填充完畢后,最后再通過 bind_param 函式將兩者系結在一起,
可以看到,無論是 Prepared Statements 陳述句還是相關的引數型別,都是在 stmt_defs 定義的,
function prepare_point_selects()
prepare_for_each_table("point_selects")
end
function prepare_for_each_table(key)
for t = 1, sysbench.opt.tables do
-- t 是表的序號,key 是測驗項的名字
stmt[t][key] = con:prepare(string.format(stmt_defs[key][1], t))
local nparam = #stmt_defs[key] - 1
if nparam > 0 then
param[t][key] = {}
end
for p = 1, nparam do
local btype = stmt_defs[key][p+1]
local len
if type(btype) == "table" then
len = btype[2]
btype = btype[1]
end
if btype == sysbench.sql.type.VARCHAR or
btype == sysbench.sql.type.CHAR then
param[t][key][p] = stmt[t][key]:bind_create(btype, len)
else
param[t][key][p] = stmt[t][key]:bind_create(btype)
end
end
if nparam > 0 then
stmt[t][key]:bind_param(unpack(param[t][key]))
end
end
end
接下來,我們看看 stmt_defs 的內容,
local stmt_defs = {
point_selects = {
"SELECT c FROM sbtest%u WHERE id=?",
t.INT},
simple_ranges = {
"SELECT c FROM sbtest%u WHERE id BETWEEN ? AND ?",
t.INT, t.INT},
sum_ranges = {
"SELECT SUM(k) FROM sbtest%u WHERE id BETWEEN ? AND ?",
t.INT, t.INT},
order_ranges = {
"SELECT c FROM sbtest%u WHERE id BETWEEN ? AND ? ORDER BY c",
t.INT, t.INT},
distinct_ranges = {
"SELECT DISTINCT c FROM sbtest%u WHERE id BETWEEN ? AND ? ORDER BY c",
t.INT, t.INT},
index_updates = {
"UPDATE sbtest%u SET k=k+1 WHERE id=?",
t.INT},
non_index_updates = {
"UPDATE sbtest%u SET c=? WHERE id=?",
{t.CHAR, 120}, t.INT},
deletes = {
"DELETE FROM sbtest%u WHERE id=?",
t.INT},
inserts = {
"INSERT INTO sbtest%u (id, k, c, pad) VALUES (?, ?, ?, ?)",
t.INT, t.INT, {t.CHAR, 120}, {t.CHAR, 60}},
}
可以看到,stmt_defs 是一張表,里面定義了不同測驗項對應的 Prepared Statements 陳述句和引數型別,
具體到 point_selects 這個測驗項,它對應的 Prepared Statements 陳述句是SELECT c FROM sbtest%u WHERE id=?,對應的引數型別是t.INT,
梳理完 prepare_point_selects() 函式的實作邏輯,最后我們看看execute_point_selects()函式的實作邏輯,
function execute_point_selects()
local tnum = get_table_num()
local i
-- point_selects 對應命令列中的 --point_selects 選項,默認為 10,
for i = 1, sysbench.opt.point_selects do
param[tnum].point_selects[1]:set(get_id())
stmt[tnum].point_selects:execute()
end
end
邏輯也非常簡單,先賦值,最后執行,
所以如果我們要基于 sbtest 表自定義測驗項,最關鍵的一步其實就是在 stmt_defs 中定義 Prepared Statements 陳述句和相關的引數型別,至于 prepare_xxx 和 execute_xxx 函式,實作起來都非常簡單,
總結
1. 基準測驗一般會關注三個指標:TPS/QPS、回應耗時和并發量,
2. 只有進行全鏈路壓測,我們才知道系統的瓶頸在哪里,不能想當然的以為,資料庫不容易橫向擴展,系統瓶頸就一定會出在資料庫層,事實上,很多系統在設計之初就引入了快取,而快取會分擔很大一部分讀流量,這種架構下的資料庫壓力其實并不大,
3. 不能簡單的將 sysbench 的測驗結果(TPS/QPS) 作為業務系統的吞吐量指標,因為兩者的業務模型并不一致,
4. 如果要自定義測驗腳本,實作的方式有兩種:
- 自己實作測驗相關的所有函式,具體實作細節可參考 bulk_insert.lua,
- 基于 sbtest 表自定義測驗項,實作程序中最關鍵的一步是在 stmt_defs 中定義 Prepared Statements 陳述句和相關的引數型別,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/539389.html
標籤:MySQL
上一篇:糟糕,資料庫例外不可用怎么辦?
下一篇:《MySQL必知必會》知識匯總四
