1. 事务概述
1.1 事务概念
事务就是一件事情,这个事情可能有多个单元组成,要求这些单元要么都成功要么都不成功。
1.2 Mysql中的事务
(1)Mysql中默认事务处理
在mysql登录的情况下执行以下命令:showvariables like '%commit%';
autocommint 值是 on,说明开启自动提交(Oracle中 autocommit 默认就是 off)。mysql数据库默认是开启事务的,一条sql一个事务。oracle它默认情况下autocommit是off,就需要手动提交事务。关闭mysql的自动事务处理命令:
set autocommit =off;( set autocommit = 0)
如果设置autocommit 为 off,意味着以后每条SQL 都会处于同一个事务中,相当于第一条SQL执行前执行了 start transaction。
(2)Mysql中常见事务操作命令
starttransaction:-- 开启事务一旦手动开启了事务,事务自动提交失效.
commit;-- 提交事务
rollback;-- 事务回滚
1.3 JDBC中的事务
java.sql.Connection接口中提供了关于事务操作的方法:
setAutoCommit(boolean flag); 参数为false相当于starttransaction
commit(); 事务提交
rollback(); 事务回滚
设置事务回滚点:
SavepointsetSavepoint(String name)
在当前事务中创建一个具有给定名称的保存点,并返回表示它的新 Savepoint 对象。
例如: Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
2. 事务的特性(ACID)
原子性(Atomicity):即表示事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功, 要么全部执行失败;
一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致。例如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的;
隔离性(Isolation):指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰;
持久性(Durability):指的是一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
3. 事务的隔离级别
3.1 不考虑事务隔离性将会产生的问题
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。如果不考虑隔离性,可能会引发如下问题:
(1)脏读:指一个事务读取另一个事务未提交的数据
(2)不可重复读:在一个事务先后两次读取发生数据不一致情况,第二次读取到另一个事务已经提交数据(强调数据更新 update)
(3)虚读(幻读) :在一个事务中,第二次读取发生数据记录数的不同,读取到另一个事务已经提交数据(强调数据记录变化 insert )
3.2事务的4种隔离级别
数据库内部定义了四种隔离级别,用于解决三种隔离问题
(1)SERIALIZABLE(串行化)
不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
性能最差;
(2)REPEATABLEREAD(可重复读)
防止脏读和不可重复读;(不能处理幻读)
性能比SERIALIZABLE好
(3)READCOMMITTED(读已提交数据)
防止脏读;(不能处理不可重复读、幻读)
性能比REPEATABLE READ好
(4)READUNCOMMITTED(读未提交数据)
可能出现任何事务并发问题
性能最好
mysql数据库默认的事务隔离级别-----repeatable read级别.
oracle数据默认的事务隔离级别 ----read committed
安全性:serializable> repeatable read > read committed > read uncommitted
性能 :serializable <repeatable read < read committed < read uncommitted
3.3 事务隔离级别的设置
(1)mysql中设置
数据库默认有事务的隔离级别,mysql 中查看与修改事务的隔离级别
set session transaction isolation level 隔离级别;设置事务隔离级别
select @@tx_isolation; 查询当前事务隔离级别
(2)jdbc中设置事务隔离级别
在java.sql.Connection接口中提供
con. setTransactionIsolation(int level)
参数可选值如下:
Connection.TRANSACTION_READ_UNCOMMITTED;
Connection.TRANSACTION_READ_COMMITTED;
Connection.TRANSACTION_REPEATABLE_READ;
Connection.TRANSACTION_SERIALIZABLE。
(注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。)
4. ThreadLocal简介
public classThreadLocal<T>extends Object
该类提供了线程局部(thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
它的底层是使用了一个Map集合
Map<Thread,Object>
它的key就是当前的线程对象.
set(Object obj) 它就相当于 map.put(Thread.currentThread(),obj);
get()它就相当于 map.get(Thread.currentThread());
ThreadLocal可以理解成是一个Map集合Map<Thread,Object>,set方法是向ThreadLocal中存储数据,那么当前的key值就是当前线程对象。get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值。如果我们是在同一个线程中,只要在任意的一个位置存储了数据,在其它位置上,就可以获取到这个数据。
5. 事务的丢失更新问题(lost update )
5.1 事务丢失更新概述
两个或多个事务更新同一行,但这些事务彼此之间都不知道其它事务进行的修改,因此后一个的更改覆盖了前一个修改。
5.2丢失更新问题的解决方法
(1)悲观锁(PessimisticLocking)-------假设丢失更新一定会发生,利用数据库内部锁机制,管理事务
mysql数据库内部提供的两种常用锁机制:共享锁(读锁)和排它锁(写锁),允许一张数据表中数据记录,添加多个共享锁,添加共享锁记录,对于其他事务可读不可写的
一张数据表中数据记录,只能添加一个排它锁,在添加排它锁的数据不能再添加其他共享锁和排它锁的,对于其他事物可读不可写的。所有数据记录修改操作,自动为数据添加排它锁
添加共享锁方式:select* from account lock in share mode ;(读锁、共享锁)
添加排它锁方式:select* from account for update; (写锁、排它锁)
锁必须在事务中添加 ,如果事务结束了 锁就释放了
解决丢失更新:事务在修改记录过程中,锁定记录,别的事务无法并发修改。
(2)乐观锁(Optimistic Locking)-------假设丢失更新不会发生,采用程序中添加版本字段解决丢失更新问题。
采用记录的版本字段,来判断记录是否修改过-------------- timestamp【通过时间戳字段】
timestamp 可以自动更新
create table product (
id int,
name varchar(20),
updatetime timestamp
);
insert into product values(1,'小米',null);
update product set name='三星' where id = 1;
timestamp 在插入和修改时都会自动更新为当前时间
解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,与修改时版本字段不一致,说明别人进行修改过数据(重改)。
事务总结:
(1)事务的特性:ACID;
(2)事务开始边界与结束边界:开始边界(con.setAutoCommit(false)),结束边界(con.commit()或con.rollback());
(3)事务的隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。多个事务并发执行时才需要考虑并发事务。