MySQL/InnoDB的併發插入Concurrent Insert

MySQL/InnoDB的併發插入Concurrent Insertjava

表people建表語句:ENGINE=InnoDBmysql

CREATE TABLE people  (
    person_id BIGINT NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(20),
    last_name VARCHAR(20),
    PRIMARY KEY (person_id)
);

 

兩個會話:會話一和會話二sql

會話一中,執行以下sql語句:數據庫

mysql> use local_database;
Database changed
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            0 |
+--------------+
1 row in set (0.00 sec)

mysql> select * from people;
+-----------+------------+-----------+
| person_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | 1111       | 1111      |
+-----------+------------+-----------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into people (first_name,last_name) values ('1111','1111');
Query OK, 1 row affected (0.12 sec)

mysql> select * from people;
+-----------+------------+-----------+
| person_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | 1111       | 1111      |
|         3 | 1111       | 1111      |
+-----------+------------+-----------+
2 rows in set (0.00 sec)

 

上面這些sql 語句最終的操做就是手動開啓了一個事務,而後提交了一個insert語句,注意沒有手動提交事務。。多線程

此時在另外一個會話二中進行以下操做併發

mysql> use local_database;
Database changed
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            0 |
+--------------+
1 row in set (0.00 sec)

mysql> select * from people;
+-----------+------------+-----------+
| person_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | 1111       | 1111      |
+-----------+------------+-----------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into people (first_name,last_name) values ('2222','2222');
Query OK, 1 row affected (0.00 sec)

mysql> select * from people;
+-----------+------------+-----------+
| person_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | 1111       | 1111      |
|         4 | 2222       | 2222      |
+-----------+------------+-----------+
2 rows in set (0.00 sec)

 

上面的sql語句最終的操做是手動開啓了一個事務,執行了insert語句,而沒有提交事務,而後select查看只會看到當前會話的操做結果,而沒有會話一的操做結果。這就是mysql默認事務隔離級別——可重複讀。函數

 

作到這裏,我驗證的不是可重複讀的事務隔離級別,我其實想驗證的是會話在事務內執行insert語句會不會給表加鎖(會給表加鎖,AUTO-INC lock),經過結果顯示,可知,不一樣的會話在事務內執行insert語句,而不會阻塞其餘會話事務內的insert語句測試

但最終我產生了疑問,MySQL/InnoDB是如何處理併發插入的。spa

 

MySQL/InnoDB的併發插入

提及auto_increment的併發插入,就要理解auto_increment的機制了。見文章:.net

http://my.oschina.net/xinxingegeya/blog/341991

http://my.oschina.net/xinxingegeya/blog/342075

下面我寫了兩個程序測試併發插入,一個是單線程的寫入10w條數據,一個是100條線程單個線程寫入1000條數據。能夠運行一下程序作一個對比。

建表語句

CREATE TABLE people_thread  (
    person_id BIGINT NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(20),
    last_name VARCHAR(20),
    thread_name VARCHAR(20),
    PRIMARY KEY (person_id)
);

總之仍是多線程的效率比較高:測試數據以下

單線程的運行時間爲:-- 16656ms

多線程的最長的運行時間爲:-- 12632ms

JDBCTest2.java

package com.lyx.other;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest2 {

	public static void main(String args[]) throws SQLException {

		long startTime = System.currentTimeMillis(); // 獲取開始時間
		Connection conn = null;
		PreparedStatement ps = null;
		String sql = "insert into people_thread (first_name ,last_name ,"
				+ "thread_name) values (?,?,?)";
		try {
			conn = getConnection();
			conn.setAutoCommit(false);
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < 100000; i++) {
				ps.setString(1, Integer.toString(i));
				ps.setString(2, Integer.toString(i));
				ps.setString(3, Integer.toString(i));
				ps.addBatch();
				// 一批提交一次
				if (i % 10 == 0) {
					ps.executeBatch();
					ps.clearBatch();
				}
			}
			ps.executeBatch();
			conn.commit();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		long endTime = System.currentTimeMillis(); // 獲取結束時間
		System.out.println("程序運行時間: " + (endTime - startTime) + "ms");
	}

	/* 獲取數據庫鏈接的函數 */
	public static Connection getConnection() {
		Connection con = null; // 建立用於鏈接數據庫的Connection對象
		try {
			Class.forName("com.mysql.jdbc.Driver");// 加載Mysql數據驅動
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/local_database", "root",
					"034039");// 建立數據鏈接
		} catch (Exception e) {
			System.out.println("數據庫鏈接失敗" + e.getMessage());
		}
		return con; // 返回所創建的數據庫鏈接
	}

}

JDBCTest3.java

package com.lyx.other;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest3 {
	public static void main(String[] args) {
		// 建立10個線程執行插入操做
		for (int i = 0; i < 100; i++) {
			final int n = i;
			Runnable task = new Runnable() {
				public void run() {
					Thread.currentThread().setName("thread_" + n);
					insertByBatch(Thread.currentThread().getName());
				}
			};
			Thread thread = new Thread(task);
			thread.start();
		}
	}

	public static void insertByBatch(String name) {
		long startTime = System.currentTimeMillis(); // 獲取開始時間
		Connection conn = null;
		PreparedStatement ps = null;
		String sql = "insert into people_thread (first_name ,last_name ,"
				+ "thread_name) values (?,?,?)";
		try {
			conn = getConnection();
			conn.setAutoCommit(false);
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < 1000; i++) {
				ps.setString(1, Integer.toString(i));
				ps.setString(2, Integer.toString(i));
				ps.setString(3, name);
				ps.addBatch();
				// 一批提交一次
				if (i % 10 == 0) {
					ps.executeBatch();
					ps.clearBatch();
				}
			}
			ps.executeBatch();
			conn.commit();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		long endTime = System.currentTimeMillis(); // 獲取結束時間
		System.out.println("程序運行時間: " + (endTime - startTime) + "ms");
	}

	/* 獲取數據庫鏈接的函數 */
	public static Connection getConnection() {
		Connection con = null; // 建立用於鏈接數據庫的Connection對象
		try {
			Class.forName("com.mysql.jdbc.Driver");// 加載Mysql數據驅動
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/local_database", "root",
					"034039");// 建立數據鏈接
		} catch (Exception e) {
			System.out.println("數據庫鏈接失敗" + e.getMessage());
		}
		return con; // 返回所創建的數據庫鏈接
	}

}

============END============

相關文章
相關標籤/搜索