MYSQL讀寫(xiě)性能測(cè)試的簡(jiǎn)單記錄
進(jìn)行測(cè)試之前首先保證你已經(jīng)可以對(duì)數(shù)據(jù)庫(kù)進(jìn)行讀寫(xiě):參見(jiàn)
要求:對(duì)MYSQL數(shù)據(jù)庫(kù)的讀寫(xiě)讀寫(xiě)性能進(jìn)行測(cè)試。支持多并發(fā)、支持調(diào)整事物提交記錄數(shù)。
注意事項(xiàng):
要運(yùn)行測(cè)試需要
1. 需要修改數(shù)據(jù)庫(kù)的配置信息DB_DRIVER、DB_URL、DB_USERNAME、DB_PASSWORD;
2.DB_URL中還要指定哪個(gè)數(shù)據(jù)庫(kù)。“dbc:mysql://localhost:3306/test”其中的test就是我鎖用的那個(gè)數(shù)據(jù)庫(kù);
3. 修改TABLE_NAME指定數(shù)據(jù)庫(kù)測(cè)試的表名(此處是student表),測(cè)試程序會(huì)查詢這個(gè)表的定義來(lái)生成寫(xiě)入SQL語(yǔ)句;
4.還有此語(yǔ)句 if (column.equalsIgnoreCase("name")) 中的那么為你創(chuàng)建的表的key,這個(gè)也要對(duì)用調(diào)整過(guò)來(lái);
5. 修改concurrentList指定需要測(cè)試并發(fā)數(shù)列表,默認(rèn)測(cè)試1,5,10,20四種并發(fā)數(shù);
6. 修改batchSizeList指定每次測(cè)試的事務(wù)提交記錄數(shù)據(jù),默認(rèn)是100,200,500,1000
最后運(yùn)行測(cè)試,會(huì)生成類似下面的結(jié)果:
測(cè)試完成后檢查該student表,輸入select * from student; 可以看到源源不斷的數(shù)據(jù)的輸出,插入的表項(xiàng)太多了。
具體查了多少數(shù)據(jù)呢?使用select count(*) from student;查看。
可能遇到的問(wèn)題:
(1)UUID.randomUUID().toString()生成的主鍵會(huì)很長(zhǎng),因此主鍵name的長(zhǎng)度應(yīng)設(shè)的長(zhǎng)一些否則會(huì)出現(xiàn)如下報(bào)錯(cuò):
由上上截圖可以看到生成的主鍵還是蠻長(zhǎng)的。
(2)每次測(cè)試的時(shí)候最好將上一次測(cè)試的table刪除重新建一個(gè)。否則測(cè)試的數(shù)據(jù)之間相差很大。
建立student表的SQL語(yǔ)句如下:
--刪除student表的SQL語(yǔ)句 drop table student; --查詢表格項(xiàng)數(shù)大小的語(yǔ)句 select count(*) from student; --建立student表,注意key的預(yù)留空間較大 create table student (name varchar(120) not null, goal varchar(20) not null, primary key(name));
程序代碼如下:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Formatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.logging.Level; import java.util.logging.Logger; public class InsertTest { private static Logger logger = Logger.getLogger(InsertTest.class.getName()); //驅(qū)動(dòng)程序名 private static final String DB_DRIVER = "com.mysql.cj.jdbc.Driver"; //URL指向要訪問(wèn)的數(shù)據(jù)庫(kù)名mysql private static final String DB_URL = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useSSL=false&serverTimezone=UTC"; //MySQL配置時(shí)的用戶名 private static final String DB_USERNAME = "root"; //MySQL配置時(shí)的密碼 private static final String DB_PASSWORD = "…………"; private static Random random = new Random(10000); //我們要測(cè)試的表的名稱 private static final String TABLE_NAME = "student"; private int batchSize;//一批提交的事務(wù)數(shù) private int concurrent;// private int sampling;// public static void main(String[] args) throws Exception { printHeader(); int[] concurrentList = new int[]{1, 5, 10, 20};//默認(rèn)測(cè)試1,5,10,20個(gè)并發(fā) int[] batchSizeList = new int[] {100, 200, 500, 1000};//一批提交的事務(wù)數(shù) for (int concurrent : concurrentList) { for (int batchSize : batchSizeList) { //對(duì)以上每種組合都run一次 new InsertTest(batchSize, concurrent).run(true); } Thread.sleep(10000); } } /*-----------InsertTest類的構(gòu)造函數(shù)一-------------*/ public InsertTest(final int batchSize, final int concurrent) throws Exception { this.batchSize = batchSize; this.concurrent = concurrent; this.sampling = 100; } /*-----------InsertTest類的構(gòu)造函數(shù)二-------------*/ public InsertTest(final int batchSize, final int concurrent, final int sampling) throws Exception { this.batchSize = batchSize; this.concurrent = concurrent; this.sampling = sampling; } /*-----------開(kāi)始運(yùn)行run方法-------------*/ public void run(boolean printResult) throws Exception { final List<Long> results = Collections.synchronizedList(new ArrayList<Long>()); final CountDownLatch startGate = new CountDownLatch(concurrent); final CountDownLatch endGate = new CountDownLatch(concurrent); for (int idxConcurrent = 0; idxConcurrent < concurrent; idxConcurrent++) { new Thread(new Runnable() { public void run() { startGate.countDown(); try { long time = execute(); long avg = batchSize * sampling * 1000 / time;; results.add(Long.valueOf(avg)); } catch(Exception ex) { ex.printStackTrace(); } finally { endGate.countDown(); } } }).start(); } endGate.await(); Collections.sort(results); //每種組合跑完之后都打印出一行數(shù)據(jù) if (printResult) { printResult(batchSize, concurrent, results); } } public long execute() throws Exception { Connection conn = getConnection(); Map<String, Integer> columns = queryTableColumns(conn); String insertSQL = generateInsertSQL(columns); PreparedStatement ps = conn.prepareStatement(insertSQL); try { long start = System.currentTimeMillis(); for (int i = 0; i < sampling; i++) { execute(conn, ps, columns); } long stop = System.currentTimeMillis(); return stop - start; } catch(Exception ex) { logger.log(Level.SEVERE, null, ex); conn.rollback(); conn.close(); throw ex; } finally { conn.close(); } } //執(zhí)行插入語(yǔ)句出錯(cuò)。 ps:INSERT INTO student1(name,goal)VALUES(** NOT SPECIFIED **,** NOT SPECIFIED **) public void execute(Connection conn, PreparedStatement ps, Map<String, Integer> columns) throws Exception { try { for (int idx = 0; idx < batchSize; idx++) { int idxColumn = 1; //這個(gè)地方實(shí)際上是對(duì)每一列進(jìn)行循環(huán)。(1)如果該列是key對(duì)應(yīng)的列………… (2)該列為普通列 for (String column : columns.keySet()) { //如果該列為name列即key對(duì)應(yīng)的列,就單獨(dú)為他生成一個(gè)主鍵。 //為了執(zhí)行忽略大小寫(xiě)的比較,使用equalsIgnoreCase if (column.equalsIgnoreCase("name")) { //給JDBC的SQL語(yǔ)句的占位符賦值的,即是下面的“? connection.prepareStatement("insert into t_user values (?,?)"); //UUID.randomUUID().toString()是java JDK提供的一個(gè)自動(dòng)生成主鍵的方法。 ps.setObject(idxColumn, UUID.randomUUID().toString()); } //否則就是普通列,隨便填充點(diǎn)東西進(jìn)去就好了。 else { ps.setObject(idxColumn, generateColumnValue(columns.get(column))); } idxColumn ++; } ps.addBatch(); } //批量執(zhí)行SQL語(yǔ)句 ps.executeBatch(); conn.commit(); ps.clearBatch(); } //如果上面出錯(cuò)了就捕獲其異常 catch (SQLException ex) { logger.log(Level.SEVERE, null, ex); if (null != ex.getNextException()) { logger.log(Level.SEVERE, null, ex.getNextException()); } conn.rollback(); throw ex; } } //根據(jù)獲取的列信息,生成插入的sql語(yǔ)句。 private String generateInsertSQL(Map<String, Integer> columns) throws SQLException { StringBuilder sb = new StringBuilder(); StringBuffer sbColumns = new StringBuffer(); StringBuffer sbValues = new StringBuffer(); sb.append("INSERT INTO ").append(TABLE_NAME); for (String column : columns.keySet()) { if (sbColumns.length() > 0) { sbColumns.append(","); sbValues.append(","); } sbColumns.append(column); sbValues.append("?"); } sb.append("(").append(sbColumns).append(")"); sb.append("VALUES"); sb.append("(").append(sbValues).append(")"); return sb.toString(); } private Map<String, Integer> queryTableColumns(Connection conn) throws Exception { Map<String, Integer> columns = new LinkedHashMap<String, Integer>(); String sql = "SELECT * FROM " + TABLE_NAME + " WHERE 1=0"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1; i <= rsmd.getColumnCount(); i++) { columns.put(rsmd.getColumnName(i), rsmd.getColumnType(i)); } return columns; } //生成列值 private Object generateColumnValue(int type) { Object obj = null; switch (type) { case Types.DECIMAL: case Types.NUMERIC: case Types.DOUBLE: case Types.FLOAT: case Types.REAL: case Types.BIGINT: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: obj = random.nextInt(10000); break; case Types.DATE: obj = Calendar.getInstance().getTime(); break; case Types.TIMESTAMP: obj = new Timestamp(System.currentTimeMillis()); break; default: obj = String.valueOf(random.nextInt(10000)); break; } return obj; } //連接MYSQL數(shù)據(jù)庫(kù)。 private Connection getConnection() throws Exception { Class.forName(DB_DRIVER); Connection conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD); conn.setAutoCommit(false); return conn; } //打印出表頭(即列的信息) private static void printHeader() { StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append(new Formatter().format("%15s|%15s|%15s|%15s|%15s", "BATCH_SIZE", "CONCURRENT", "AVG (r/s)", "MIN (r/s)", "MAX (r/s)")); System.out.println(sb.toString()); } //打印每次跑完后的統(tǒng)計(jì)信息 private static void printResult(int batch, int concurrent, List<Long> results) { Long total = Long.valueOf(0); for (Long result : results) { total += result; } StringBuilder sb = new StringBuilder(); sb.append(new Formatter().format("%15s|%15s|%15s|%15s|%15s", batch, concurrent, (total/results.size()), results.get(0), results.get(results.size() - 1))); System.out.println(sb.toString()); } }
到此這篇關(guān)于MYSQL讀寫(xiě)性能測(cè)試的簡(jiǎn)單記錄的文章就介紹到這了,更多相關(guān)MYSQL讀寫(xiě)性能測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql中影響數(shù)據(jù)庫(kù)性能的因素講解
在本篇文章中我們給大家講述了mysql中影響性能的因素以及相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友參考下。2018-09-09Mysql數(shù)據(jù)庫(kù)報(bào)錯(cuò)2003?Can't?connect?to?MySQL?server?on?
最近在用mysql,打開(kāi)mysql的圖形化界面要連接時(shí)出現(xiàn)2003錯(cuò)誤,所以下面這篇文章主要給大家介紹了關(guān)于Mysql數(shù)據(jù)庫(kù)報(bào)錯(cuò)2003?Can't?connect?to?MySQL?server?on?'localhost'?(10061)的解決方式,需要的朋友可以參考下2022-09-09MySql按時(shí),天,周,月進(jìn)行數(shù)據(jù)統(tǒng)計(jì)
這篇文章主要介紹了MySql按時(shí),天,周,月進(jìn)行數(shù)據(jù)統(tǒng)計(jì),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08MySQL 數(shù)據(jù)庫(kù)函數(shù)庫(kù)
MySQL 數(shù)據(jù)庫(kù)函數(shù)庫(kù)...2006-12-12Mysql數(shù)據(jù)庫(kù)時(shí)間查詢舉例詳解
在項(xiàng)目開(kāi)發(fā)中,一些業(yè)務(wù)表字段經(jīng)常使用日期和時(shí)間類型,而且后續(xù)還會(huì)牽涉到這類字段的查詢,下面這篇文章主要給大家介紹了關(guān)于Mysql數(shù)據(jù)庫(kù)時(shí)間查詢的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05MLSQL Stack如何讓流調(diào)試更加簡(jiǎn)單詳解
這篇文章主要給大家介紹了關(guān)于MLSQL Stack如何讓流調(diào)試更加簡(jiǎn)單的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MLSQL具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Mysql數(shù)據(jù)庫(kù)監(jiān)聽(tīng)binlog的開(kāi)啟步驟
這篇文章主要給大家介紹了關(guān)于Mysql數(shù)據(jù)庫(kù)監(jiān)聽(tīng)binlog的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Mysql具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08