H2介紹 – Java嵌入式數據庫

H2是一個用Java開發的嵌入式數據庫,這裏指的嵌入式不是手持設備之類的,而是H2數據庫做爲一個類庫,直接嵌入到上層的應用程序中,與應用運行在同一個進程中。 數據庫

最大的優點在於能夠同應用程序打包在一塊兒發佈,對於客戶端應用來講,很是方便。好比說騰訊QQ或者Mozilla Firefox,用戶不可能爲了用個軟件還得在本身機器上裝個MySQL?SQL Server?上述軟件就使用嵌入式數據庫SQLite來進行客戶端本地存儲。H2的定位和SQLite同樣,屬於嵌入式數據庫。(H2也能夠用在Android上哦) 後端

另外一個優點是,寫代碼時須要寫單元測試,與數據庫操做相關的功能單元測試都比較很差作,由於有一個環境的問題,而採用H2來進行就要比MySQL要方便的多,首先是啓動速度快,並且能夠關閉持久化功能,表只存在內存中,每個用例執行完自動還原到純淨環境。 緩存

如今不少開源產品的發佈版中所附的測試用例,都是用的H2,目前你們都是把它用做測試。我感受它的另外一個用處是做爲內存緩存,NoSQL的一個補充,當某些場景下數據模型必須爲關係型,能夠拿它當Memcached使,做爲後端MySQL/Oracle的一個緩衝層,緩存一些不常常變化但須要頻繁訪問的數據,好比字典表、權限表。 服務器

H2使用很是簡單,使用URL: jdbc:h2:~/test 來創建JDBC鏈接,就會自動建立一個test.h2.db文件和一個test.lock.db文件,前者就是用來存儲數據的。只要這個Connection不斷開,H2就始終處於運行狀態。 併發

H2支持3種運行模式: 單元測試

1.嵌入式模式。H2運行在應用程序的進程中,執行效率會比較高,但因爲不容許其餘進程訪問,管理起來麻煩點。 測試

 

2.服務器模式。相似於MySQL那種C/S模型,H2運行在一個獨立的進程中,應用程序經過TCP協議與其遠程通訊。優點就是管理方便,並且能夠部署在不一樣的機器上,使用包括集羣等特性。 進程

 

3.混合模式。綜合以上兩種狀況,由應用程序首先啓動H2,這時對於應用來講H2工做在嵌入式模式,同時H2監聽TCP某個端口,等待遠程鏈接,這就是服務器模式,便於管理維護。 事務

 

一般來講,混合模式比較實用。使用jdbc:h2:~/test;AUTO_SERVER=TRUE來創建JDBC鏈接,就能夠開啓混合模式。 內存

還能夠關閉持久化功能,全部數據都保存在內存中,效率很高。URL:jdbc:h2:mem:test ,一樣能夠開啓混合模式容許遠程訪問。

對於多版本併發,默認的實現是基於表鎖的,讀操做加共享鎖,寫操做加互斥鎖,(也就是寫會阻塞讀)等待鎖超時會拋出異常。能夠開啓MVCC (Multi-Version Concurrent Control) 多版本併發控制模式,URL爲jdbc:h2:~/test;MVCC=TRUE,開啓MVCC模式後,全部操做都基於行鎖,只能看到已提交的數據,寫不阻塞讀。

舉個例子來講明下這個問題:


// 事務1
Connection conn1 = DriverManager.getConnection(jdbcUrl, user, passwd);
conn1.setAutoCommit(false); // 開啓事務

ResultSet rs1 = conn1.createStatement().executeQuery(
"select name from students where id = 1 limit 1"); // 事務中讀
if (rs1.next())
System.out.println("1. " + rs1.getString("name")); // 修改前

conn1.createStatement().execute(
"update students set name = 'lisi' where id = 1");
// 寫操做 注意!完成後故意不提交

ResultSet rs2 = conn1.createStatement().executeQuery(
"select name from students where id = 1 limit 1"); // 事務中讀
if (rs2.next())
System.out.println("2. " + rs2.getString("name")); // 修改後

// 事務2
Connection conn2 = DriverManager.getConnection(jdbcUrl, user, passwd);
conn2.setAutoCommit(false); // 開啓事務

ResultSet rs3 = conn2.createStatement().executeQuery(
"select name from students where id = 1"); // 事務外讀
if (rs3.next())
System.out.println("3. " + rs3.getString("name")); // ?
這個實驗的流程是這樣的:首先開啓一個事務1,讀取初始值,修改它,再從新讀取這個值。保持事務1沒有提交的狀況下,在事務2中讀取這個值。

若是使用默認模式,則是基於表鎖,寫阻塞讀,事務1的3個操做順利完成,但事務1沒有提交,寫鎖沒有釋放。這個時候事務2中會讀取這個值,發現數據被鎖了,等待一小會後發現仍然沒法獲取鎖,因而第24行拋出異常,控制檯打印信息以下:

1. zhangsan
2. lisi
Exception in thread "main" org.h2.jdbc.JdbcSQLException:
Timeout trying to lock table "STUDENTS"; SQL statement:
select name from students where id = 1 [50200-157]
若是開啓了MVCC模式,寫操做就不會阻塞讀操做了,任什麼時候間點只能讀到已提交數據。第24行代碼正常執行,由於事務1沒有提交,因此只能讀取到事務1修改前的值,控制檯打印信息以下:

1. zhangsan 2. lisi 3. zhangsan 若是應用有併發請求的話,建議開啓MVCC模式。

相關文章
相關標籤/搜索