事务

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_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE。多个事务并发执行时才需要考虑并发事务。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章