數(shù)據(jù)庫(kù)全指南:JDBC配置、參數(shù)化與并發(fā)實(shí)戰(zhàn))
1. 先想清楚壓數(shù)據(jù)庫(kù)和壓接口到底差在哪很多人用 JMeter 做接口測(cè)試、壓 Web 服務(wù)駕輕就熟。但一說(shuō)到用 JMeter 直接壓數(shù)據(jù)庫(kù)不少人就懵了——數(shù)據(jù)庫(kù)也能用 JMeter 壓怎么連連上了能測(cè)啥先說(shuō)結(jié)論不僅能壓而且非常好使。我在實(shí)際項(xiàng)目里既用 JMeter 壓過(guò) MySQL、PostgreSQL也壓過(guò) Oracle甚至給第三方數(shù)據(jù)中臺(tái)做過(guò)壓測(cè)靠的都是 JMeter 自帶的 JDBC 請(qǐng)求組件不需要裝任何額外插件。但有一個(gè)認(rèn)知必須先糾正用 JMeter 測(cè)數(shù)據(jù)庫(kù)不等于在數(shù)據(jù)庫(kù)客戶(hù)端里手動(dòng)執(zhí)行幾條 SQL 看返回時(shí)間。它做的是“模擬真實(shí)業(yè)務(wù)場(chǎng)景下大量并發(fā)請(qǐng)求同時(shí)打向數(shù)據(jù)庫(kù)”這件事——比如 100 個(gè)用戶(hù)同時(shí)登錄每個(gè)登錄動(dòng)作背后對(duì)應(yīng)著幾條 SQL這些 SQL 同時(shí)打到數(shù)據(jù)庫(kù)上數(shù)據(jù)庫(kù)扛不扛得住平均響應(yīng)時(shí)間多少有沒(méi)有慢查詢(xún)連接池會(huì)不會(huì)被打滿(mǎn)這才是壓數(shù)據(jù)庫(kù)的核心目的。所以這篇文章我不會(huì)只講“怎么連上數(shù)據(jù)庫(kù)”而是把 JMeter 測(cè)試數(shù)據(jù)庫(kù)的完整方法拆開(kāi)揉碎環(huán)境準(zhǔn)備、JDBC 連接配置、JDBC Request 詳解、參數(shù)化傳值、斷言校驗(yàn)、并發(fā)場(chǎng)景搭建、常見(jiàn)問(wèn)題排查一條龍講清楚。你照著做基本可以覆蓋日常工作中 90% 的數(shù)據(jù)庫(kù)壓測(cè)需求。適合誰(shuí)看剛接觸 JMeter、想用它做數(shù)據(jù)庫(kù)壓測(cè)的測(cè)試新人被領(lǐng)導(dǎo)臨時(shí)派活、需要在兩天內(nèi)給出數(shù)據(jù)庫(kù)性能結(jié)論的測(cè)試工程師以及那些已經(jīng)會(huì)基本接口壓測(cè)、但沒(méi)系統(tǒng)搞過(guò) JDBC 請(qǐng)求的老手??赐昴阒辽倌塥?dú)立搭出一套帶參數(shù)化、帶斷言、帶并發(fā)模型的數(shù)據(jù)庫(kù)壓測(cè)腳本。2. 環(huán)境準(zhǔn)備驅(qū)動(dòng)、版本、連接方式一步都不能錯(cuò)2.1 先確認(rèn) JMeter 版本和 JDBC 驅(qū)動(dòng)開(kāi)始之前先確認(rèn)你的 JMeter 版本。我這邊用的是 JMeter 5.xJDBC 請(qǐng)求相關(guān)的組件從 3.x 就有界面和配置項(xiàng)基本一致所以你用 4.x 或 5.x 都行不用太糾結(jié)版本。關(guān)鍵在于JDBC 驅(qū)動(dòng) Jar 包。JMeter 本身不自帶數(shù)據(jù)庫(kù)驅(qū)動(dòng)你必須手動(dòng)把對(duì)應(yīng)數(shù)據(jù)庫(kù)的驅(qū)動(dòng) Jar 包放到 JMeter 的lib目錄下然后重啟 JMeter 才能生效。常見(jiàn)數(shù)據(jù)庫(kù)對(duì)應(yīng)的驅(qū)動(dòng) Jar 包MySQLmysql-connector-java-8.0.x.jar如果你連的是 MySQL 5.x建議用 5.1.4x 版本MySQL 8.x 一定要用 8.0 版本的驅(qū)動(dòng)不然連接會(huì)報(bào)錯(cuò)。PostgreSQLpostgresql-42.x.x.jarOracleojdbc8.jar或ojdbc11.jar看你數(shù)據(jù)庫(kù)版本SQL Servermssql-jdbc-9.x.x.jar驅(qū)動(dòng)包放好之后重啟 JMeter然后用一個(gè)最簡(jiǎn)單的 JDBC 請(qǐng)求驗(yàn)證一下能不能連上。別一上來(lái)就搞復(fù)雜場(chǎng)景連接都不通的話(huà)后面全是白搭。2.2 JDBC 驅(qū)動(dòng)類(lèi)名和連接 URL別記混了很多新手死在第一步就是驅(qū)動(dòng)類(lèi)名和 URL 寫(xiě)錯(cuò)。這里直接給你一份對(duì)照表抄作業(yè)就行數(shù)據(jù)庫(kù)JDBC Driver ClassJDBC URL 格式MySQLcom.mysql.jdbc.Driver5.x/com.mysql.cj.jdbc.Driver8.xjdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiPostgreSQLorg.postgresql.Driverjdbc:postgresql://127.0.0.1:5432/testdbOracleoracle.jdbc.OracleDriverjdbc:oracle:thin:127.0.0.1:1521/ORCLSQL Servercom.microsoft.sqlserver.jdbc.SQLServerDriverjdbc:sqlserver://127.0.0.1:1433;DatabaseNametestdb注意 MySQL 這里有兩個(gè)坑第一MySQL 5.x 和 8.x 的 Driver Class 不一樣。8.x 是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver寫(xiě)錯(cuò)直接報(bào)ClassNotFoundException。第二MySQL 8.x 連接 URL 里建議加上useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。不加 serverTimezone 會(huì)報(bào)時(shí)區(qū)錯(cuò)誤不加 allowPublicKeyRetrieval 會(huì)在某些認(rèn)證方式下報(bào)Public Key Retrieval is not allowed。這兩個(gè)參數(shù)我每次都會(huì)帶上省得排查半天。經(jīng)驗(yàn)我一般會(huì)在連接 URL 里額外加一個(gè)connectTimeout5000socketTimeout10000避免數(shù)據(jù)庫(kù)掛了之后線(xiàn)程一直卡在等待上壓測(cè)時(shí)能很快暴露連接超時(shí)問(wèn)題。比如jdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiconnectTimeout5000socketTimeout100002.3 環(huán)境驗(yàn)證用一個(gè)最簡(jiǎn)單的查詢(xún)打通鏈路驅(qū)動(dòng)放好、配置想清楚了先做個(gè)連通性測(cè)試——在 JMeter 里建一個(gè)測(cè)試計(jì)劃加一個(gè) JDBC Connection Configuration再選一個(gè)庫(kù)建一個(gè)線(xiàn)程池跑一條SELECT 1如果返回正常鏈路就通了。這一步別看簡(jiǎn)單它價(jià)值很大能一次性排除驅(qū)動(dòng)、URL、賬號(hào)密碼、網(wǎng)絡(luò)四個(gè)環(huán)節(jié)的問(wèn)題。如果SELECT 1都跑不通那后面再?gòu)?fù)雜的腳本也跑不通不如先在這里把問(wèn)題解決掉。3. JDBC Connection Configuration連接池配置才是壓測(cè)的關(guān)鍵3.1 配置項(xiàng)逐行拆解JDBC Connection Configuration 是 JDBC 請(qǐng)求的前置條件相當(dāng)于給所有 JDBC 請(qǐng)求提供數(shù)據(jù)庫(kù)連接池。它不是一個(gè)“填個(gè) URL 就行”的簡(jiǎn)單配置每個(gè)選項(xiàng)背后都有講究。關(guān)鍵的幾個(gè)配置項(xiàng)Variable Name for created pool連接池變量名隨便起比如db_pool但你要記死它因?yàn)?JDBC Request 里要通過(guò)這個(gè)名字來(lái)引用這個(gè)連接池。一個(gè)測(cè)試計(jì)劃里可以配置多個(gè)連接池給不同庫(kù)用靠名字區(qū)分。Max Number of Connections最大連接數(shù)。這個(gè)值很關(guān)鍵它決定了 JMeter 能同時(shí)從池里拿多少個(gè)連接。如果你壓 100 個(gè)并發(fā)但最大連接數(shù)只有 10那 JMeter 的請(qǐng)求就會(huì)排隊(duì)等連接測(cè)出來(lái)的結(jié)果不是數(shù)據(jù)庫(kù)的真實(shí)瓶頸而是連接池的瓶頸。Max Wait最大等待時(shí)間單位毫秒。連接池沒(méi)空閑連接時(shí)請(qǐng)求最多等多久超時(shí)拋異常。一般設(shè) 10000。Time Between Eviction Runs清理線(xiàn)程運(yùn)行間隔。JMeter 會(huì)定期清理空閑過(guò)久的連接一般設(shè) 60000。Auto Commit自動(dòng)提交。只做查詢(xún)壓測(cè)時(shí)設(shè) True 就好涉及事務(wù)、插入、更新時(shí)按需改成 False。Transaction Isolation事務(wù)隔離級(jí)別默認(rèn) DEFAULT 即可不用特別改。Pool Validation Query連接有效性檢查 SQL。MySQL 下寫(xiě)SELECT 1PostgreSQL 下寫(xiě)SELECT 1Oracle 下寫(xiě)SELECT 1 FROM DUAL。JMeter 每次從池里拿連接時(shí)可以用這個(gè) SQL 驗(yàn)證連接是否還活著避免拿到失效連接。3.2 連接池參數(shù)怎么定一個(gè)簡(jiǎn)單的估算方法連接池最大連接數(shù)怎么定很多教程會(huì)說(shuō)“設(shè)大一點(diǎn)就行”但實(shí)際不是這么回事。連接數(shù)設(shè)太大數(shù)據(jù)庫(kù)可能會(huì)因?yàn)椴l(fā)連接過(guò)多直接拒絕服務(wù)設(shè)太小JMeter 這邊又會(huì)排隊(duì)。我的做法是連接池最大連接數(shù) 目標(biāo)并發(fā)線(xiàn)程數(shù)。比如你要模擬 100 個(gè)用戶(hù)并發(fā)那么 Max Number of Connections 就設(shè) 100 或略大于 100比如 100。這樣每個(gè)線(xiàn)程拿到一個(gè)獨(dú)立連接不會(huì)出現(xiàn)兩個(gè)線(xiàn)程搶一個(gè)連接的情況。這里有個(gè)容易混淆的細(xì)節(jié)JMeter 的線(xiàn)程數(shù)和連接池連接數(shù)不是一一綁定的關(guān)系。線(xiàn)程可以復(fù)用連接但如果你想測(cè)的是“數(shù)據(jù)庫(kù)在 100 個(gè)并發(fā)連接下的表現(xiàn)”那連接數(shù)就必須給夠否則測(cè)出來(lái)的曲線(xiàn)是連接池排隊(duì)曲線(xiàn)不是數(shù)據(jù)庫(kù)性能曲線(xiàn)。3.3 為什么要用連接池而不是每個(gè)線(xiàn)程單獨(dú)連JMeter 里還有一種做法是每個(gè)線(xiàn)程自己創(chuàng)建數(shù)據(jù)庫(kù)連接不用連接池——比如用 BeanShell 或者 JSR223 寫(xiě)代碼去DriverManager.getConnection()。我不推薦這種方式做壓測(cè)原因有兩個(gè)第一性能差。每次請(qǐng)求都重新創(chuàng)建連接開(kāi)銷(xiāo)全花在 TCP 握手和認(rèn)證上測(cè)出來(lái)的響應(yīng)時(shí)間虛高。第二資源不可控。并發(fā) 100 個(gè)線(xiàn)程同時(shí)創(chuàng)建 100 個(gè)連接壓測(cè)機(jī)本身的文件描述符、內(nèi)存壓力都會(huì)變大干擾結(jié)果。所以標(biāo)準(zhǔn)做法就是一個(gè) JDBC Connection Configuration 管連接池N 個(gè) JDBC Request 從池里取連接執(zhí)行 SQL。連接池的創(chuàng)建、復(fù)用、回收J(rèn)Meter 都在底層幫你處理好了你要做的就是配置好那幾項(xiàng)參數(shù)。4. JDBC Request 詳解增刪改查都能壓4.1 SQL 查詢(xún)和變量名規(guī)則JDBC Request 是真正執(zhí)行 SQL 的地方。它長(zhǎng)得很像一個(gè) HTTP 請(qǐng)求 Sample但參數(shù)完全不同。核心參數(shù)有三個(gè)Variable Name of Pool declared in JDBC Connection Configuration填連接池變量名也就是你在 JDBC Connection Configuration 里填的那個(gè)名字。如果這里填錯(cuò)JMeter 會(huì)直接報(bào)錯(cuò)找不到連接池。Query TypeSQL 類(lèi)型有 Select Statement、Update Statement、Callable Statement、Prepared Select Statement、Prepared Update Statement 等。如果是普通查詢(xún)用 Select Statement如果 SQL 里有占位符?必須用 Prepared Select Statement 或 Prepared Update Statement。QuerySQL 語(yǔ)句本體。這里有個(gè)新手高頻疑問(wèn)Query Type 選 Select Statement 和 Prepared Select Statement 有什么區(qū)別區(qū)別在占位符。如果你要執(zhí)行SELECT * FROM user WHERE id ?就必須用 Prepared Select Statement然后通過(guò)參數(shù)列表傳入?的值。如果你執(zhí)行的是完整 SQL沒(méi)有?用 Select Statement 就行。相同點(diǎn)是結(jié)果都存放在變量里供后續(xù)使用。4.2 查詢(xún)結(jié)果的三種用法JDBC Request 執(zhí)行完之后結(jié)果存在哪答案是JMeter 會(huì)把結(jié)果集存到變量里變量名規(guī)則是你在 JDBC Request 里填的Variable Name。比如你在 JDBC Request 的 Variable Name 填了mysql_result那么執(zhí)行完查詢(xún)后mysql_result整個(gè)結(jié)果集mysql_result_#結(jié)果集行數(shù)比如返回 10 行這個(gè)變量值就是 10mysql_result_1第一行數(shù)據(jù)mysql_result_1_id第一行的 id 字段值mysql_result_2_id第二行的 id 字段值用Variable Name 下劃線(xiàn) 行號(hào) 下劃線(xiàn) 列名這個(gè)規(guī)則就能取到任意一行任意一列的值。這個(gè)特性相當(dāng)有用。最常見(jiàn)的場(chǎng)景是你從表 A 查到一批訂單 ID然后要把這些 ID 作為參數(shù)傳給下一個(gè) JDBC Request 或 HTTP 請(qǐng)求。具體做法就是先執(zhí)行一條查詢(xún) SQL然后在下一個(gè)請(qǐng)求里用${mysql_result_1_id}來(lái)引用第一行 ID。4.3 實(shí)際案例查詢(xún)用戶(hù)表數(shù)據(jù)我來(lái)演示一個(gè)最簡(jiǎn)單的例子。假設(shè)數(shù)據(jù)庫(kù)里有一張user表字段有id、username、age我想壓測(cè)一條查詢(xún) SQLSELECT id, username, age FROM user WHERE age 20配置如下JDBC Request Variable Nameuser_resultQuery TypeSelect StatementQuerySELECT id, username, age FROM user WHERE age 20跑完之后在察看結(jié)果樹(shù)里可以看到這個(gè) JDBC Request 的響應(yīng)數(shù)據(jù)里面是查到的所有行。如果你想把這批數(shù)據(jù)取出來(lái)給后續(xù)請(qǐng)求用比如遍歷前 10 個(gè)用戶(hù) ID就可以用${user_result_1_id}、${user_result_2_id}這種形式去引用。注意結(jié)果集會(huì)按位置存儲(chǔ)mysql_result_1是第 1 行mysql_result_2是第 2 行以此類(lèi)推。如果你查詢(xún)結(jié)果本身有排序需求在 SQL 里把ORDER BY寫(xiě)好否則取出來(lái)的行順序可能不穩(wěn)定。4.4 執(zhí)行更新操作怎么配置壓測(cè)不光是查詢(xún)有時(shí)也要模擬寫(xiě)入、更新的業(yè)務(wù)壓力。比如 100 個(gè)用戶(hù)同時(shí)更新自己的資料。如果是帶參數(shù)的更新比如UPDATE user SET age ? WHERE id ?那么 Query Type 要選Prepared Update StatementSQL 里的?是占位符需要通過(guò)“Parameter values”和“Parameter types”傳入實(shí)際值。這里有個(gè)重要細(xì)節(jié)執(zhí)行 Update/Insert/Delete 操作時(shí)如果數(shù)據(jù)庫(kù)沒(méi)有返回結(jié)果集JMeter 會(huì)返回受影響的行數(shù)。比如某個(gè) UPDATE 影響了 3 行JMeter 的響應(yīng)數(shù)據(jù)里會(huì)顯示3。如果你想驗(yàn)證更新是否成功一種做法是更新后再執(zhí)行一條 SELECT 去查這條數(shù)據(jù)配合斷言來(lái)校驗(yàn)。5. 參數(shù)化取值讓壓測(cè)數(shù)據(jù)動(dòng)起來(lái)5.1 CSVDataset 參數(shù)化每個(gè)線(xiàn)程分塊取值做數(shù)據(jù)庫(kù)壓測(cè)時(shí)你常常需要模擬“不同的用戶(hù)操作不同的數(shù)據(jù)”而不是 100 個(gè)用戶(hù)都查同一條記錄。前者才是真實(shí)業(yè)務(wù)場(chǎng)景也能更有效地壓出數(shù)據(jù)庫(kù)的查詢(xún)能力。JMeter 里最常用的參數(shù)化方式就是CSV 數(shù)據(jù)集配置CSV Data Set Config。它允許你從一個(gè) CSV 文件里按行讀取數(shù)據(jù)然后把每一列的值賦給變量。配置的時(shí)候有幾點(diǎn)要注意FilenameCSV 文件路徑Variable Names列名用英文逗號(hào)分隔比如id,username,ageDelimiter分隔符默認(rèn)是英文逗號(hào)Recycle on EOF讀完最后一行后是否回到開(kāi)頭繼續(xù)讀。如果你壓測(cè)時(shí)長(zhǎng)較長(zhǎng)或者并發(fā)數(shù)大于 CSV 行數(shù)建議設(shè) True否則讀到末尾線(xiàn)程會(huì)直接結(jié)束或報(bào)錯(cuò)。Stop thread on EOF讀完最后一行是否停止線(xiàn)程。如果 Recycle on EOF 是 True這個(gè)就設(shè) False。Sharing mode共享模式。默認(rèn) All threads 是所有線(xiàn)程共享一個(gè)文件指針按順序往下讀如果你想每個(gè)線(xiàn)程各讀各的可以選 Current thread。我在實(shí)際項(xiàng)目里壓“100 并發(fā)用戶(hù)登錄”時(shí)會(huì)準(zhǔn)備一個(gè) 100 行的 CSV每行一個(gè)用戶(hù) ID 和密碼然后把線(xiàn)程組并發(fā)數(shù)設(shè)為 100CSV 共享模式用默認(rèn)的 All threads這樣每個(gè)線(xiàn)程拿到一個(gè)不同的用戶(hù)數(shù)據(jù)誰(shuí)也不會(huì)搶。5.2 從上一個(gè)查詢(xún)結(jié)果取參JDBC 請(qǐng)求結(jié)果復(fù)用CSV 適合數(shù)據(jù)預(yù)先準(zhǔn)備好的場(chǎng)景但有些場(chǎng)景數(shù)據(jù)是動(dòng)態(tài)生成的——你先要查一下有哪些訂單然后針對(duì)每個(gè)訂單做后續(xù)操作。這種時(shí)候就要用 JDBC Request 的查詢(xún)結(jié)果來(lái)做參數(shù)化。思路是先跑一條 SELECT把結(jié)果存到一個(gè)變量里然后在后一個(gè)請(qǐng)求里用${var_行號(hào)_列名}去引用。舉個(gè)例子第一步JDBC Request 查訂單 IDSELECT order_id, amount FROM orders WHERE status pending這個(gè) JDBC Request 的 Variable Name 設(shè)為order_result。第二步在 HTTP 請(qǐng)求或下一個(gè) JDBC Request 里引用訂單 ID${order_result_1_order_id} 訂單金額${order_result_1_amount}這樣就實(shí)現(xiàn)了“把數(shù)據(jù)庫(kù)查出來(lái)的數(shù)據(jù)作為下一個(gè)接口的參數(shù)”這個(gè)高頻需求。但如果查詢(xún)返回了多行而你希望循環(huán)處理每一行數(shù)據(jù)那還是建議配合ForEach 控制器來(lái)做。ForEach 控制器可以遍歷一個(gè)變量集合比如order_result下的所有行然后逐一執(zhí)行后續(xù)請(qǐng)求。配置時(shí)輸入變量前綴order_result循環(huán)變量名隨便取一個(gè)比如current_order然后在后續(xù)請(qǐng)求里用${current_order_order_id}就能依次取到每一行的數(shù)據(jù)。5.3 JDBC 請(qǐng)求結(jié)果里的特殊變量行數(shù)和 NULL使用查詢(xún)結(jié)果時(shí)有兩個(gè)場(chǎng)景很容易踩坑。第一空結(jié)果集。如果 SQL 查不到數(shù)據(jù)order_result_#會(huì)是 0此時(shí)不能引用order_result_1_order_id否則 JMeter 會(huì)輸出一個(gè)空值。為了不報(bào)錯(cuò)可以搭配If 控制器判斷order_result_#是否大于 0再執(zhí)行后續(xù)邏輯。第二字段值本身是 NULL。比如某個(gè)字段在某些行里是 NULL引用之后得到的字符串是null而不是空字符串。如果你的下游接口對(duì)參數(shù)格式有嚴(yán)格要求需要在 BeanShell 或 JSR223 里做一個(gè) null 值替換處理否則接口調(diào)用可能會(huì)因?yàn)槎鄠髁薾ull字符串而報(bào)錯(cuò)。5.4 直接用 JSR223 腳本搞定更復(fù)雜的數(shù)據(jù)加工如果上面的方式都滿(mǎn)足不了需求比如你要對(duì)查出來(lái)的數(shù)據(jù)進(jìn)行 MD5 加密后作為參數(shù)傳給接口那就得上 JSR223 腳本了。JSR223 Sampler 里可以用 Groovy 直接查數(shù)據(jù)庫(kù)處理結(jié)果集生成任意格式的字符串。Groovy 的語(yǔ)法比 BeanShell 簡(jiǎn)潔太多性能也更好是 JMeter 官方推薦的方式。雖然這篇文章主要講 JDBC Request 這種圖形化配置但在自定義邏輯非常復(fù)雜的時(shí)候JSR223 是終極兜底方案。簡(jiǎn)單示例import java.sql.* // 獲取連接池里的連接 def conn vars.get(db_pool) // 這只是示意 // 真實(shí)的連接獲取可以通過(guò) DataSource 或者直接用 JDBC 驅(qū)動(dòng)獲取這里省略不過(guò)說(shuō)實(shí)話(huà)只有你需要寫(xiě)復(fù)雜邏輯時(shí)才用 JSR223一般情況下 JDBC Request CSV 已經(jīng)能覆蓋絕大多數(shù)場(chǎng)景。過(guò)度使用 JSR223 反而會(huì)讓腳本難以維護(hù)。6. 斷言校驗(yàn)怎么證明數(shù)據(jù)庫(kù)返回是對(duì)的6.1 響應(yīng)斷言最快最直接的校驗(yàn)方式壓數(shù)據(jù)庫(kù)不是把 SQL 跑完就完事了你得確認(rèn)返回內(nèi)容是符合預(yù)期的。比如你查詢(xún)age 20的用戶(hù)結(jié)果返回里至少有數(shù)據(jù)行而不是空結(jié)果集或者一堆錯(cuò)誤信息。JMeter 的響應(yīng)斷言可以加到 JDBC Request 上用來(lái)校驗(yàn)響應(yīng)內(nèi)容。配置方式很簡(jiǎn)單在 JDBC Request 上右鍵 → Add → Assertions → Response AssertionField to Test選 Text ResponsePattern Matching Rules選 ContainsPatterns to Test填你要校驗(yàn)的關(guān)鍵字比如查詢(xún)user表你想確認(rèn)結(jié)果里有username這個(gè)字段名就可以在響應(yīng)斷言里填username。如果響應(yīng)里沒(méi)有這個(gè)關(guān)鍵字?jǐn)嘌跃蜁?huì)失敗在聚合報(bào)告里會(huì)看到錯(cuò)誤率上升。這里有個(gè)技巧有時(shí)候 SQL 查出來(lái)的數(shù)據(jù)量很大響應(yīng)內(nèi)容很長(zhǎng)你沒(méi)法直觀地看到是否有數(shù)據(jù)這時(shí)可以直接用響應(yīng)斷言校驗(yàn) “返回行數(shù)不為 0”。具體做法是把 SQL 改成SELECT COUNT(*) AS cnt FROM user WHERE age 20然后斷言響應(yīng)里包含cnt10或某個(gè)具體數(shù)字。注意JMeter 響應(yīng)里 COUNT 查詢(xún)的顯示格式是類(lèi)似cnt10這樣的配合 Contains 斷言就能判斷數(shù)據(jù)量是否符合預(yù)期。6.2 BeanShell 斷言處理復(fù)雜校驗(yàn)邏輯響應(yīng)斷言只能做簡(jiǎn)單匹配如果你要做復(fù)雜判斷比如“查詢(xún)結(jié)果里最大金額大于 1000”或“返回行數(shù)在某個(gè)區(qū)間內(nèi)”就得用 BeanShell 斷言或者 JSR223 斷言。JSR223 斷言可以用 Groovy 寫(xiě)邏輯清晰多了。比如判斷結(jié)果集行數(shù)def rowCount vars.get(user_result_#).toInteger() if (rowCount 10) { // 失敗手動(dòng)設(shè)置斷言結(jié)果 AssertionResult.setFailure(true) AssertionResult.setFailureMessage(查詢(xún)結(jié)果集行數(shù)小于10實(shí)際為: rowCount) }這段腳本的邏輯是從 JMeter 變量里取user_result_#的值轉(zhuǎn)成整數(shù)判斷是否小于 10如果小于就手動(dòng)標(biāo)記斷言失敗并輸出失敗原因。這樣在聚合報(bào)告或察看結(jié)果樹(shù)里你能很直觀地看到哪些請(qǐng)求沒(méi)通過(guò)校驗(yàn)。經(jīng)驗(yàn)斷言別加太多加的每一個(gè)斷言都會(huì)消耗額外的性能。壓測(cè)腳本里的斷言越精簡(jiǎn)越好能用一個(gè)響應(yīng)斷言解決的就不要加腳本斷言。高并發(fā)壓測(cè)時(shí)過(guò)重的斷言會(huì)拉高 JMeter 本身的 CPU 開(kāi)銷(xiāo)影響測(cè)試數(shù)據(jù)準(zhǔn)確性。6.3 結(jié)合“用結(jié)果樹(shù)看數(shù)據(jù)”排查問(wèn)題壓測(cè)跑完后第一件事永遠(yuǎn)是看察看結(jié)果樹(shù)View Results Tree。在結(jié)果樹(shù)里每個(gè) JDBC Request 都能展開(kāi)看它的請(qǐng)求數(shù)據(jù)、響應(yīng)數(shù)據(jù)、斷言結(jié)果。響應(yīng)數(shù)據(jù)里會(huì)顯示 SQL 執(zhí)行后返回的內(nèi)容格式。我遇到過(guò)很多測(cè)試小白寫(xiě)完了腳本直接開(kāi)壓壓完發(fā)現(xiàn)錯(cuò)誤率 100%然后開(kāi)始懷疑數(shù)據(jù)庫(kù)出問(wèn)題了。其實(shí)根本不是數(shù)據(jù)庫(kù)的問(wèn)題而是 SQL 寫(xiě)錯(cuò)了或者參數(shù)沒(méi)傳進(jìn)去。這種時(shí)候不用慌打開(kāi)察看結(jié)果樹(shù)找一個(gè)失敗的請(qǐng)求看它的響應(yīng)數(shù)據(jù)里的報(bào)錯(cuò)信息十有八九一次就能定位到問(wèn)題。7. 并發(fā)壓測(cè)實(shí)戰(zhàn)模擬 100 個(gè)用戶(hù)同時(shí)查庫(kù)7.1 線(xiàn)程組參數(shù)設(shè)計(jì)前面把 JDBC Request 和參數(shù)化都講完了接下來(lái)進(jìn)入實(shí)戰(zhàn)模式——模擬 100 個(gè)用戶(hù)同時(shí)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)看數(shù)據(jù)庫(kù)的表現(xiàn)。線(xiàn)程組配置建議Number of Threads (users)100Ramp-Up Period (seconds)10Loop Count50 或者填 ∞根據(jù)你的壓測(cè)時(shí)長(zhǎng)來(lái)定這里解釋一下 Ramp-Up Period 的作用它表示 100 個(gè)線(xiàn)程在 10 秒內(nèi)全部啟動(dòng)完畢也就是說(shuō)每秒啟動(dòng) 10 個(gè)線(xiàn)程。這樣做是為了模擬用戶(hù)逐步進(jìn)入系統(tǒng)的真實(shí)場(chǎng)景而不是 100 個(gè)請(qǐng)求在同一毫秒內(nèi)全部打到數(shù)據(jù)庫(kù)。如果你希望一開(kāi)始就是滿(mǎn)負(fù)載可以把 Ramp-Up 設(shè)成 1 秒如果你要做階梯壓測(cè)可以用 Stepping Thread Group 插件但這個(gè)插件需要額外安裝基礎(chǔ)場(chǎng)景下普通線(xiàn)程組就夠用了。Loop Count 表示每個(gè)線(xiàn)程執(zhí)行多少次查詢(xún)。假如一個(gè) JDBC Request 執(zhí)行一條 SQL100 個(gè)線(xiàn)程 × 循環(huán) 50 次 總共 5000 次 SQL 查詢(xún)。這個(gè)總數(shù)可以用來(lái)估算壓測(cè)時(shí)長(zhǎng)和數(shù)據(jù)庫(kù)負(fù)載。7.2 實(shí)戰(zhàn)腳本結(jié)構(gòu)一個(gè)標(biāo)準(zhǔn)的數(shù)據(jù)庫(kù)壓測(cè)腳本結(jié)構(gòu)如下測(cè)試計(jì)劃線(xiàn)程組100 線(xiàn)程10 秒內(nèi)啟動(dòng)JDBC Connection Configuration連接池配置最大連接數(shù) 100CSV Data Set Config用戶(hù)數(shù)據(jù)參數(shù)化JDBC Request執(zhí)行 SQL響應(yīng)斷言校驗(yàn)返回結(jié)果監(jiān)聽(tīng)器聚合報(bào)告Aggregate Report察看結(jié)果樹(shù)View Results Tree需要注意JDBC Connection Configuration 和 CSV Data Set Config 都是配置元件Config Element它們的作用范圍是“所在層級(jí)之下的所有請(qǐng)求”。放在線(xiàn)程組下就對(duì)該線(xiàn)程組里的所有 JDBC Request 生效。7.3 壓測(cè)過(guò)程中的實(shí)時(shí)監(jiān)控壓測(cè)跑起來(lái)之后除了看 JMeter 的聚合報(bào)告我強(qiáng)烈建議你同時(shí)監(jiān)控?cái)?shù)據(jù)庫(kù)端的指標(biāo)。光看 JMeter 的響應(yīng)時(shí)間是不夠的你還需要知道數(shù)據(jù)庫(kù)的 CPU、內(nèi)存、連接數(shù)、慢查詢(xún)數(shù)等指標(biāo)才能完整評(píng)估數(shù)據(jù)庫(kù)性能。具體做法如果是 MySQL可以在壓測(cè)時(shí)執(zhí)行SHOW PROCESSLIST;查看當(dāng)前所有連接和正在執(zhí)行的 SQL。查看數(shù)據(jù)庫(kù)的 CPU 和內(nèi)存占用比如用top或數(shù)據(jù)庫(kù)自帶的性能監(jiān)控工具。開(kāi)啟慢查詢(xún)?nèi)罩緣簻y(cè)完后分析哪些 SQL 的執(zhí)行時(shí)間超過(guò)了閾值。這一步非常重要因?yàn)?JMeter 的聚合報(bào)告只能告訴你“響應(yīng)時(shí)間變慢了”但到底慢在數(shù)據(jù)庫(kù)的哪個(gè)環(huán)節(jié)——是 CPU 打滿(mǎn)了、還是鎖等待、還是磁盤(pán) IO——必須結(jié)合數(shù)據(jù)庫(kù)端的指標(biāo)來(lái)看。7.4 聚合報(bào)告怎么解讀壓測(cè)結(jié)束看聚合報(bào)告的幾個(gè)核心指標(biāo)Samples總請(qǐng)求數(shù)Average平均響應(yīng)時(shí)間單位毫秒Min / Max最小/最大響應(yīng)時(shí)間Std. Dev.響應(yīng)時(shí)間標(biāo)準(zhǔn)差越大說(shuō)明響應(yīng)時(shí)間波動(dòng)越明顯性能越不穩(wěn)定Error %錯(cuò)誤率正常壓測(cè)場(chǎng)景下應(yīng)為 0%有少量錯(cuò)誤要具體分析Throughput吞吐量單位通常是req/sec意思是每秒能處理多少請(qǐng)求舉個(gè)例子100 個(gè)線(xiàn)程、循環(huán) 50 次的腳本跑完Samples 是 5000Throughput 如果是 200/sec說(shuō)明數(shù)據(jù)庫(kù)每秒能扛住 200 次這個(gè) SQL 查詢(xún)。如果你把循環(huán)次數(shù)加大Throughput 會(huì)逐步逼近數(shù)據(jù)庫(kù)的極限值這個(gè)極限值就是數(shù)據(jù)庫(kù)的處理能力上限。經(jīng)驗(yàn)壓測(cè)數(shù)據(jù)庫(kù)時(shí)平均響應(yīng)時(shí)間和吞吐量要一起看。如果平均響應(yīng)時(shí)間很低但吞吐量也低說(shuō)明 JMeter 本身或網(wǎng)絡(luò)成了瓶頸不是數(shù)據(jù)庫(kù)不行如果吞吐量上去了但平均響應(yīng)時(shí)間也跟著飆升這才說(shuō)明數(shù)據(jù)庫(kù)開(kāi)始進(jìn)入高負(fù)載狀態(tài)。8. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄8.1 連接不通ClassNotFoundException 和 URL 報(bào)錯(cuò)問(wèn)題現(xiàn)象JDBC Request 報(bào)ClassNotFoundException: com.mysql.jdbc.Driver或Cannot create PoolableConnectionFactory。排查思路先看驅(qū)動(dòng) Jar 包有沒(méi)有放到 JMeter 的 lib 目錄下然后看是否重啟了 JMeter。如果確定 Jar 包在再看 Driver Class 寫(xiě)對(duì)沒(méi)有——MySQL 8.x 驅(qū)動(dòng)對(duì)應(yīng)的是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver。如果報(bào)錯(cuò)信息是Unknown database xxx檢查連接 URL 里的數(shù)據(jù)庫(kù)名是否正確。如果是Access denied for user檢查賬號(hào)密碼和數(shù)據(jù)庫(kù)權(quán)限。8.2 連接池不夠用Too many connections問(wèn)題現(xiàn)象壓測(cè)過(guò)程中JMeter 報(bào)Too many connections或者數(shù)據(jù)庫(kù)端出現(xiàn)Connection refused。原因分析這個(gè)報(bào)錯(cuò)有兩個(gè)方向一個(gè)是 JMeter 連接池最大連接數(shù)設(shè)得太大打爆了數(shù)據(jù)庫(kù)的最大連接數(shù)另一個(gè)是數(shù)據(jù)庫(kù)自身max_connections配置太小。解決方案先查看數(shù)據(jù)庫(kù)的max_connections比如 MySQL 可以用SHOW VARIABLES LIKE max_connections;查看。然后調(diào)整 JMeter 連接池的最大連接數(shù)讓它的上限略低于數(shù)據(jù)庫(kù)的max_connections留一些余量給其他應(yīng)用連接。順便說(shuō)一句做壓測(cè)之前一定要和 DBA 或運(yùn)維確認(rèn)好數(shù)據(jù)庫(kù)的連接上限否則壓到一半數(shù)據(jù)庫(kù)直接把連接全踢掉測(cè)試結(jié)果就廢了。8.3 響應(yīng)時(shí)間異常長(zhǎng)慢 SQL 還是連接池排隊(duì)問(wèn)題現(xiàn)象聚合報(bào)告顯示平均響應(yīng)時(shí)間高達(dá)幾秒遠(yuǎn)大于你手動(dòng)執(zhí)行 SQL 的時(shí)間。原因分析首先要區(qū)分是數(shù)據(jù)庫(kù)真慢還是連接池在排隊(duì)。手動(dòng)執(zhí)行 SQL 快說(shuō)明 SQL 本身沒(méi)問(wèn)題。這時(shí)候要去看 JMeter 連接池的最大連接數(shù)是不是小于線(xiàn)程數(shù)——如果 100 個(gè)線(xiàn)程去搶 20 個(gè)連接剩下 80 個(gè)線(xiàn)程全在等連接響應(yīng)時(shí)間自然飆升。解決方案把 Max Number of Connections 調(diào)大使其等于或大于線(xiàn)程數(shù)再跑一次對(duì)比。如果響應(yīng)時(shí)間正常了說(shuō)明之前的瓶頸是連接池如果響應(yīng)時(shí)間依然很大那就要去數(shù)據(jù)庫(kù)端看慢查詢(xún)?nèi)罩径ㄎ皇遣皇撬饕?、鎖等待等問(wèn)題。還有一個(gè)細(xì)節(jié)如果連接 URL 設(shè)置了socketTimeout當(dāng)數(shù)據(jù)庫(kù)處理查詢(xún)超過(guò)這個(gè)值時(shí)JMeter 會(huì)直接報(bào) socket 超時(shí)。這種情況下不是數(shù)據(jù)庫(kù)變慢而是策略性地?cái)嚅_(kāi)了連接。壓測(cè)時(shí)可以把 socketTimeout 調(diào)大一點(diǎn)避免誤判。8.4 變量引用出來(lái)是 null結(jié)果集行號(hào)與字段大小寫(xiě)問(wèn)題現(xiàn)象在后續(xù)請(qǐng)求里用${var_1_id}引用 JDBC 查詢(xún)結(jié)果發(fā)現(xiàn)值是 null 或者空字符串。原因排查第一先看結(jié)果集是不是空的。如果 SQL 查不出來(lái)數(shù)據(jù)任何引用都是空。用察看結(jié)果樹(shù)看響應(yīng)數(shù)據(jù)確認(rèn)是否有數(shù)據(jù)返回。第二看字段名大小寫(xiě)。MySQL 的字段名默認(rèn)全小寫(xiě)如果 SQL 里寫(xiě)了別名比如SELECT id AS userId FROM user那么引用時(shí)要寫(xiě)成${var_1_userId}。建議 SQL 里起別名時(shí)統(tǒng)一用帶下劃線(xiàn)的命名如user_id然后引用時(shí)保持大小寫(xiě)一致。第三行號(hào)從 1 開(kāi)始不是從 0 開(kāi)始。如果變量名是order_result那么第一行是order_result_1沒(méi)有order_result_0這個(gè)變量。8.5 中文亂碼與編碼問(wèn)題問(wèn)題現(xiàn)象JDBC 查詢(xún)返回的中文變成問(wèn)號(hào)或亂碼。原因排查大多是連接 URL 里沒(méi)有指定字符集。在 MySQL 連接 URL 上加上characterEncodingutf8jdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaicharacterEncodingutf8另外檢查數(shù)據(jù)庫(kù)表本身的字符集是不是 utf8mb4。如果表是 latin1 而應(yīng)用按 utf8 讀取也會(huì)亂碼。8.6 一個(gè)容易被忽略的問(wèn)題壓測(cè)機(jī)性能不夠最后提一個(gè)很多人踩過(guò)的坑——壓測(cè)機(jī)本身的性能不夠。JMeter 是 Java 應(yīng)用100 個(gè)并發(fā)線(xiàn)程跑起來(lái)時(shí)壓測(cè)機(jī) CPU 和內(nèi)存消耗不小。如果你在本地筆記本上壓遠(yuǎn)程數(shù)據(jù)庫(kù)壓測(cè)結(jié)果可能不是你 SQL 的真實(shí)性能而是筆記本 CPU 打滿(mǎn)后的結(jié)果。怎么判斷壓測(cè)時(shí)打開(kāi)任務(wù)管理器或top看看 Java 進(jìn)程的 CPU 占用率。如果已經(jīng)超過(guò)了機(jī)器 CPU 的 70% 以上建議換一臺(tái)性能更強(qiáng)的機(jī)器或者把 JMeter 部署到另一臺(tái)服務(wù)器上盡量讓壓測(cè)機(jī)資源充足確保瓶頸在數(shù)據(jù)庫(kù)端而不是客戶(hù)端。8.7 問(wèn)題排查速查表問(wèn)題現(xiàn)象可能原因解決方案ClassNotFoundException驅(qū)動(dòng) Jar 未放到 lib 目錄放入 Jar 包并重啟 JMeterCannot create PoolableConnectionFactoryURL 或賬號(hào)密碼錯(cuò)誤檢查 URL 格式、賬號(hào)權(quán)限Too many connections連接數(shù)超過(guò)數(shù)據(jù)庫(kù)上限調(diào)大數(shù)據(jù)庫(kù) max_connections 或調(diào)小 JMeter 連接池響應(yīng)時(shí)間超長(zhǎng)連接池排隊(duì)或慢 SQL調(diào)大連接池查看慢查詢(xún)?nèi)罩咀兞恐禐?null結(jié)果集為空或字段名大小寫(xiě)不一致確認(rèn)查詢(xún)結(jié)果檢查 SQL 別名中文亂碼連接 URL 未指定字符集添加 characterEncodingutf8壓測(cè)結(jié)果不穩(wěn)定壓測(cè)機(jī)資源不足更換壓測(cè)機(jī)或降低線(xiàn)程數(shù)9. 一個(gè)完整的數(shù)據(jù)庫(kù)壓測(cè)腳本示例講了這么多最后給你一個(gè)完整的 MySQL 壓測(cè)腳本搭建過(guò)程從零到一可以直接照抄。9.1 測(cè)試場(chǎng)景模擬 100 個(gè)用戶(hù)并發(fā)查詢(xún)訂單表每個(gè)查詢(xún)根據(jù)不同的訂單 ID 查詢(xún)訂單信息持續(xù)壓測(cè) 10 分鐘觀察數(shù)據(jù)庫(kù)的吞吐量和響應(yīng)時(shí)間變化。9.2 準(zhǔn)備工作MySQL 數(shù)據(jù)庫(kù)提前創(chuàng)建好訂單表orders字段包括order_id、user_id、amount、status準(zhǔn)備一個(gè)order_ids.csv文件里面放 100 個(gè)訂單 ID用于參數(shù)化JMeter 5.xmysql-connector-java-8.0.x.jar已放入 lib 目錄并重啟9.3 腳本搭建步驟第一步創(chuàng)建測(cè)試計(jì)劃并添加線(xiàn)程組線(xiàn)程數(shù) 100Ramp-Up 10 秒循環(huán)次數(shù)勾選無(wú)限在調(diào)度器配置里設(shè)置持續(xù)時(shí)間為 600 秒。第二步添加 JDBC Connection Configuration配置項(xiàng)如下Variable Name for created poolmysql_poolMax Number of Connections100Max Wait10000Auto CommitTruePool Validation QuerySELECT 1Database Connection ConfigurationDatabase URLjdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiJDBC Driver Classcom.mysql.cj.jdbc.DriverUsernametest_userPasswordyour_password第三步添加 CSV Data Set ConfigFilename/data/order_ids.csvVariable Namesorder_idDelimiter,Recycle on EOFTrueStop thread on EOFFalseSharing modeAll threads第四步添加 JDBC RequestVariable Name of Poolmysql_poolQuery TypePrepared Select StatementQuerySELECT * FROM orders WHERE order_id ?Parameter values${order_id}Parameter typesVARCHAR這里說(shuō)明一下因?yàn)?SQL 里有?占位符所以 Query Type 必須選 Prepared Select StatementParameter values 里填 CSV 讀取出來(lái)的訂單 IDParameter types 填數(shù)據(jù)庫(kù)字段類(lèi)型。第五步添加響應(yīng)斷言Field to TestText ResponsePattern Matching RulesContainsPatterns to Testorder_id這樣就確保了查詢(xún)請(qǐng)求返回的結(jié)果中包含 order_id 字段證明查詢(xún)是成功的。第六步添加監(jiān)聽(tīng)器加一個(gè)聚合報(bào)告和一個(gè)察看結(jié)果樹(shù)。壓測(cè)結(jié)束后聚合報(bào)告里可以看到平均響應(yīng)時(shí)間、吞吐量、錯(cuò)誤率等核心數(shù)據(jù)。9.4 跑完怎么分析壓測(cè)結(jié)束后我一般這么看數(shù)據(jù)先看錯(cuò)誤率。如果錯(cuò)誤率為 0%說(shuō)明 100 并發(fā)下這條 SQL 沒(méi)有出錯(cuò)數(shù)據(jù)庫(kù)連接和查詢(xún)都正常。如果錯(cuò)誤率大于 0查詢(xún)具體的錯(cuò)誤信息常見(jiàn)的是連接池不夠用或超時(shí)。然后看吞吐量變化。如果 JMeter 的 Throughput 在持續(xù)穩(wěn)定上升后趨于平緩說(shuō)明已經(jīng)摸到了數(shù)據(jù)庫(kù)的處理上限。此時(shí)如果平均響應(yīng)時(shí)間還能接受說(shuō)明數(shù)據(jù)庫(kù)還有冗余如果響應(yīng)時(shí)間已經(jīng)超標(biāo)比如超過(guò) 500ms說(shuō)明數(shù)據(jù)庫(kù)快要撐不住了。最后結(jié)合數(shù)據(jù)庫(kù)端監(jiān)控比如 CPU、慢查詢(xún)?nèi)罩究雌款i到底在哪個(gè)環(huán)節(jié)然后給出測(cè)試結(jié)論和優(yōu)化建議。這里有一個(gè)小建議壓測(cè)時(shí)先把線(xiàn)程數(shù)保守一點(diǎn)比如 50跑一輪看結(jié)果再逐步升到 100、200。這樣做的好處是你能看到數(shù)據(jù)庫(kù)在不同并發(fā)下的表現(xiàn)曲線(xiàn)而不是一上來(lái)就壓到天花板拿到一堆根本沒(méi)法分析的數(shù)據(jù)。階梯式加壓是我在實(shí)際項(xiàng)目中比較喜歡用的方式。最后說(shuō)幾句實(shí)在話(huà)用 JMeter 做數(shù)據(jù)庫(kù)壓測(cè)門(mén)檻真的不高但想做好需要你對(duì)數(shù)據(jù)庫(kù)本身的運(yùn)行機(jī)制有基本認(rèn)知。我在實(shí)際測(cè)試項(xiàng)目里見(jiàn)過(guò)不少人把壓測(cè)腳本配好了、跑出了漂亮的報(bào)告但一問(wèn)數(shù)據(jù)庫(kù)端的指標(biāo)完全答不上來(lái)——CPU 多少、內(nèi)存多少、慢查詢(xún)有沒(méi)有、連接數(shù)有沒(méi)有到上限全是空白。這種壓測(cè)報(bào)告說(shuō)實(shí)話(huà)價(jià)值有限。我的個(gè)人建議是壓測(cè)數(shù)據(jù)庫(kù)永遠(yuǎn)要把 JMeter 端的數(shù)據(jù)和數(shù)據(jù)庫(kù)端的數(shù)據(jù)結(jié)合起來(lái)看。JMeter 告訴你發(fā)生了什么數(shù)據(jù)庫(kù)的監(jiān)控告訴你為什么會(huì)發(fā)生。兩邊對(duì)照才能真正判斷數(shù)據(jù)庫(kù)的性能瓶頸在哪里也才能給出可信的測(cè)試結(jié)論。如果你只是剛接觸這塊先從最簡(jiǎn)單的單條 SQL 查詢(xún)壓測(cè)入手把 JDBC Connection Configuration 和 JDBC Request 玩熟再慢慢加參數(shù)化、加斷言、加并發(fā)場(chǎng)景。不要一上來(lái)就想測(cè)多表關(guān)聯(lián)或存儲(chǔ)過(guò)程底子打好后面的內(nèi)容都是水到渠成的事。