JAVA程序对MYSQL数据库加锁实验

什么是脏读,不可重复读,幻读

1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2. 不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

补充 : 基于元数据的 Spring 声明性事务 :

Isolation 属性一共支持五种事务设置,具体介绍如下:

l          DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .

l          READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )

l          READ_COMMITTED  会出现不可重复读、幻读问题(锁定正在读取的行)

l          REPEATABLE_READ 会出幻读(锁定所读取的所有行)

l          SERIALIZABLE 保证所有的情况不会发生(锁表)

不可重复读的重点是修改 :
同样的条件 ,   你读取过的数据 ,   再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 ,   第 1 次和第 2 次读出来的记录数不一样

再想了想,其实你后面说的都没错,唯一的问题是“数据库不会处理并发操作”这个理解所引发的,数据库不是不会处理并发操作,如果它不会处理就没有加锁机制和事务隔离机制了,数据库的问题是在于它并不知道你的并发依赖关系,因此需要你告知它。数据库的并发处理控制并不依赖于Hibernate或其它数据访问层的实现机制,唯一需要的只是你要“告知它”哪里需要重点关照。

数据库默认情况下的事务管理是“已提交读取”,也就是说已经可以保证Select不会拿到脏数据(不会发生脏读----你理解错的地方);当然也可能你理解错的脏读,脏读的意思是,事务B拿到了事务A中间状态的数据,而A后来把这个整个事务回滚了,那么事务B就相当于拿到了一个实际上根本不存在的脏数据。

而不可重复读(幻读)和更新丢失的问题需要另外解决,这个你是说对了的;目前除了加锁(排他锁)和串行化隔离外,没有更好招数。

虽然你只错了一个地方,但我感觉可能你还没有理解的比较透彻,建议去找找:脏读(写-读依赖)、更新丢(写-写依赖)、幻读(读-写依赖)这几个东西的概念,最好能自己举例说明。

分类: mysql 2010-09-13 14:11 761人阅读 评论(0) 收藏 举报

以下实验仅在MYSQL 5.0.45做的实验.

方法1:用mysql命令锁住表.

  1. public void test() {
  2. String sql = "lock tables aa1 write";
  3. // 或String sql = "lock tables aa1 read";
  4. // 如果想锁多个表 lock tables aa1 read ,aa2 write , .....
  5. String sql1 = "select * from aa1 ";
  6. String sql2 = "unlock tables";
  7. try {
  8. this.pstmt = conn.prepareStatement(sql);
  9. this.pstmt1 = conn.prepareStatement(sql1);
  10. this.pstmt2 = conn.prepareStatement(sql2);
  11. pstmt.executeQuery();
  12. pstmt1.executeQuery();
  13. pstmt2.executeQuery();
  14. } catch (Exception e) {
  15. System.out.println("异常" + e.getMessage());
  16. }
  17. }

一、对于read lock 和 write lock官方说明:
   1.如果一个线程获得一个表的READ锁定,该线程(和所有其它线程)只能从该表中读取。
      如果一个线程获得一个表的WRITE锁定,只有保持锁定的线程可以对表进行写入。
      其它的线程被阻止,直到锁定被释放时为止。

2.当您使用LOCK TABLES时,您必须锁定您打算在查询中使用的所有的表。
      虽然使用LOCKTABLES语句获得的锁定仍然有效,但是您不能访问没有被此语句锁定的任何的表。
      同时,您不能在一次查询中多次使用一个已锁定的表——使用别名代替,
      在此情况下,您必须分别获得对每个别名的锁定。

二、对与read lock 和 write lock个人说明:
    1.read lock 和 write lock 是线程级(表级别).
    2.在同一个会话中加了read lock锁. 只能对这个表进行读操作.对这个表以外的任何表都无法进行增、删、改、查的操作.
       但是在不同会话中,只能对加了read lock的表进行读操作.但可以对read lock以外的表进行增、删、改、查的操作.
    3.在同一个会话中加了write lock锁.只能对这个表进行读、写操作.对这个表以外的任何表都无法进行增、删、改、查的操作.
      但是在不同会话中,无法对加了write lock的表进行读、写操作.但可以对write lock以外的表进行增、删、改、查的操作.
   4.如果表中使用了别名.(SELECT * FROM aa1 AS byname_table)
      在对aa1加锁时,必须把别名加上去(lock tables aa1 as byname_table read)
      在同一个会话中.必须使用别名进行查询.
      在不同的会话中.可以不需要使用别名进行查询.
   5.在多个会话中可以对同一个表进行lock read操作.但不能在多个会话中对同一个表进行lock write操作(这些锁将等待已锁的表释放自身的线程锁)
     如果多个会话对同一个表进行lock read操作.那么在这些会话中,也只能对以锁的表进行读操作.
   6.如果要你锁住了一个表,需要嵌套查询.你必须使用别名,并且,要锁定别名.
     例如.lock table aa1 read ,aa1 as byname_table read;
     select * from aa1 where id in (select * from aa1 as xx  where id=2);  
   7.解锁必须用unlock tables;

另:
      在JAVA程序中,要想解锁,需要调用 unlock tables来解锁.
      如果没有调用unlock tables.
      关闭connection 、程序结束 、调用GC 都能解锁.

方法2:用记录锁锁表.

  1. public void test() {
  2. String sql = "select * from aa1 for update";
  3. // select * from aa1 lock in share mode;
  4. try {
  5. conn.setAutoCommit(false);
  6. this.pstmt = conn.prepareStatement(sql);
  7. pstmt.executeQuery();
  8. } catch (Exception e) {
  9. System.out.println("异常" + e.getMessage());
  10. }
  11. }

1.for update 与 lock in share mode 属于行级锁和页级锁

2.for update 排它锁,lock in share mode 共享锁

3.对于记录锁.必须开启事务.

4.行级锁定事实上是索引记录的锁定.只要是用索引扫描的行(或没索引全表扫描的行),都将被锁住.

5.在不同的隔离级别下还会使用next-key locking算法.即所扫描的行之间的“间隙”也会也锁住(在Repeatable read和Serializable隔离级别下有间隙锁).

6.在mysql*享锁的含义是:在被共享锁锁住的行,即使内容被修改且并没有提交.在另一个会话中依然看到最新修改的信息.

在同一会话中加上了共享锁.可以对这个表以及这个表以外的所有表进行增、删、改、查的操作.

在不同的会话中.可以查到共享锁锁住行的最新消息.但是在Read Uncommitted隔离级别下不能对锁住的表进行删,

改操作.(需要等待锁释放才能操作...)
                     在Read Committed隔离级别下不能对锁住的表进行删,改操作.(需要等待锁释放才能操作...)
                     在Repeatable read隔离级别下不能对锁住行进行增、删、改操作.(需要等待锁释放才能操作...)
                     在Serializable隔离级别下不能对锁住行进行增、删、改操作.  (需要等待锁释放才能操作...)

7.在mysql中排他锁的含义是:在被排它锁锁住的行,内容修改并没提交,在另一个会话中不会看到最新修改的信息。

在不同的会话中.可以查到共享锁锁住行的最新消息.但是Read Uncommitted隔离级别下不能对锁住的表进行删,

改操作.(需要等待锁释放才能操作...)
                     在Read Committed隔离级别下不能对锁住的表进行删,改操作.(需要等待锁释放才能操作...)
                     在Repeatable read隔离级别下不能对锁住行进行增、删、改操作.(需要等待锁释放才能操作...)
                     在Serializable隔离级别下不能对锁住行进行增、删、改操作. (需要等待锁释放才能操作...)

8.在同一个会话中的可以叠加多个共享锁和排他锁.在多个会话中,需要等待锁的释放.

9.SQL中的update 与 for update是一样的原理.

10.等待超时的参数设置:innodb_lock_wait_timeout=50 (单位秒).

11.任何可以触发事务提交的命令,都可以关闭共享锁和排它锁.

上一篇:Hibernate Annotation笔记


下一篇:java io系列01之 "目录"