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模式。

相关文章
相关标签/搜索