事物的四大特性、锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/laixiao_hero/article/details/39344799

1、事务:      事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。例如:A——B转帐,对应于如下两条sql语句

update account set money=money-100 where name=‘a’;

update account set money=money+100 where name=‘b’;

2、  使用:start  transaction  ————开启事务

Rollback                   回滚事务(前一个操作数据库被撤销掉,事务结束,相当于撤销)

Commit                  提交事务(所有操作数据库语句完成后,才真正保存数据库)

3、设置回滚点

新建数据库工具类JdbcUtils.java:

//导入接口(若导入其它就会报错)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;

public class JdbcUtils {
	/* 1.获取数据库连接的函数*/
	
	@Test
	public static Connection getConnection() {
		Connection con = null;	//创建用于连接数据库的Connection对象
		try {
			Class.forName("com.mysql.jdbc.Driver");// 加载Mysql数据驱动
			
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/day10", "root", "root");// 创建数据连接
			
		} catch (Exception e) {
			System.out.println("数据库连接失败" + e.getMessage());
		}
		return con;	//返回所建立的数据库连接
	}

	/**
	 * 关闭连接
	 */
	public static void close(ResultSet rs, Statement stat,Connection conn){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				rs = null;
			}
		}
		if(stat!=null){
			try {
				stat.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				stat = null;
			}
		}
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				conn = null;
			}
		}
	
	}
}

新建JDBCTranDemo.java

//实践是检验真理的
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;

public class JDBCTranDemo {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		Savepoint sp = null;
		try{
			conn = JdbcUtils.getConnection();
			conn.setAutoCommit(false);//1、取消自动提交状态
			
			ps = conn.prepareStatement("update account set money=money-100 where name = ?");
			ps.setString(1, "a");
			ps.executeUpdate();

			ps = conn.prepareStatement("update account set money=money+100 where name=?");
			ps.setString(1, "b");
			ps.executeUpdate();
			
			//2、设置回滚点(就像游戏中的存档),如果抛异常,则调到try catch模块
			sp = conn.setSavepoint();
			
			ps = conn.prepareStatement("update account set money=money-100 where name = ?");
			ps.setString(1, "a");
			ps.executeUpdate();
			
			String str = null;//设置异常点
			str.toUpperCase();
			
			ps = conn.prepareStatement("update account set money=money+100 where name=?");
			ps.setString(1, "b");
			ps.executeUpdate();
			
			conn.commit();//4、没有抛异常时,执行到这里,直接提交数据。
		}catch (Exception e) {
			try {
				if(sp == null){
					conn.rollback();//3、如果回滚点为null说明没有执行到设置回滚点代码时就抛了异常,应该所有语句进行回滚
				}else{
					conn.rollback(sp);//回滚到回滚点
					conn.commit();//3、说明sp不为null,可以会滚到回滚点,接着执行其他操作,但是要注意,如果希望之前的语句起作用,仍然需要做提交操作
				}
				
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally{
			JdbcUtils.close(rs, ps, conn);
		}
	}
}

4、事务的四大特性(ACID):

原子性(Atomicity):事务的一组操作是原子的不可再分割的,这组操作要么同时完成要么同时不完成。

一致性(Consistency): 事务在执行前后数据的完整性保持不变。数据库在某个状态下符合所有的完整性约束的状态叫做数据库具有完整性。在解散一个部门时应该同时处理员工表中的员工保证这个事务结束后,仍然保证所有的员工能找到对应的部门,满足外键约束。

隔离性(Isolation当多个事务同时操作一个数据库时,可能存在并发问题,此时应保证各个事务要进行隔离,事务之间不能互相干扰。(事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。)————不可以

持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,不能再回滚。




《特性之一:事务的隔离性》

1、常识:1、如果将数据库设计成单线程的数据库,可防止所有的线程安全问题,保证了隔离性,但是效率低下;

                            2、如果两个线程并发修改,一定会互相捣乱,这时必须利用锁机制防止多个线程并发的修改。

                            3、如果两个线程并发查询,没有线程安全问题。

 

4、如果两个线程(具体问题具体分析):一个修改,一个查询··《参看:事务隔离级别子.txt》

问题1脏读一个事务读取到了另一个事务未提交的数据。

         1| a    | 1000

        2 | b   |  1000

       

        b--->a

        start transaction;

        update account set money=money-100 wherename='b';

         updateaccount set money=money+100 where name='a';

         rollback; 

        

        

         select* from account where name = 'a';1000 1000

        

问题2不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同(其它人执行修改数据操作了).

         starttransaction:

         活期存款:1000

         定期存款:1000

         固定资产: 2000

         --------------

                   开启事务

                   取走获取存款1000

                   提交事务

         --------------

         总资产:3000

        

         问题3幻读(虚读):一个事务读取到了另一个事务插入的数据(查询和修改异步;已提交)

         a2000

         b2000

         c2000

         starttransaction;

         selectsum(money) from account;6000

         --------------

                   开启事务

                   创建一个账户并存入1000块钱

                   提交了事务

         --------------

         selectcount(*)from account;4

         avgMoney= allMoney/count;6000/4=1500

 

 

2、四大隔离级别(具体问题具体分析,合理选择隔离级别):

1、Readuncommitted:不防止任何隔离性问题,数据库将会有脏读、不可重复度、幻读的问题。

         2、Readcommitted:数据库可以防止脏读,但有不可重复度、幻读的问题。

         3、Repeatableread:数据库可以防止脏读、不可重复度,但是不能防止幻读。

         4、Serializable:将数据库串行化,可以避免脏读、不可重复读、幻读。(含有锁机制)

        

         安全性来说:Serializable>Repeatableread>Read committed>Read uncommitted

         效率来说:Serializable<Repeatableread<Read committed<Read uncommitted

 

通常来说,一般的应用都会选择Repeatable read或Read committed作为数据库隔离级别来使用。(mysql默认的数据库隔离级别为:REPEATABLE-READ;Oracle默认: Read committed)

 

 

3、使用:

1、select @@tx_isolation       查询当前事务隔离级别

l  2、set       global  transaction  isolation level  Read uncommitted;设置事务隔离级别(设置数据库默认的隔离级别:要写global;不写的话,修改的只是当前窗口的级别)

《数据库的锁机制》

1、共享锁:共享锁和共享锁可以共存。(在非Serializable隔离级别查询,不加任何锁,否则加排他锁)

2、排他锁:排他锁和所有锁都不能共存。(所有隔离级别下增删改,都会加排他锁)

                   在非串行化下,所有的查询都不加锁,所有的修改操作都会加排他锁。

                   在串行化下,所有的查询都加共享锁,所有的修改都加排他锁。

3、互相等待会出现死锁,数据库会默认干掉一边,放行一边。

 

 

4、更新丢失:如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。

解决方案:乐观锁和悲观锁不是数据库中真正存在的锁,只是人们在解决更新丢失时的不同的解决方案,体现的是人们看待事务的态度。



使用原则:乐观锁:查询多,修改少;悲观锁:修改多,查询少。

1、加悲观锁(效率低下):悲观锁会悲观地认为每一次操作都会造成更新丢失问题,在每次查询时就加上排它锁。

 

select 列名 from 表名 where id=1 for update ;

select * from table lock inshare mode(读锁、共享锁)

select * from table forupdate (写锁、排它锁)

 

2、加乐观锁:乐观锁会乐观地认为每次查询都不会造成更新丢失,利用一个版本字段进行控制。

update order set stat=1 and version=version+1 where id=1and version=0;





展开阅读全文

没有更多推荐了,返回首页