mysql-事务

  1. 事务概述
    在一个事件中,有多个组成单元,这些单元要么全部成功,要么全部失败,这个事件就可以称之为一个事务。

    1. 案例:
      转账:
      update user set money = money -100 where name= ‘a’;
      update user set money = money +100 where name= ‘b’;
      为了保证两句sql同时成功或失败,需要将它们写入一个数据库的事务当中。

    2. 在Mysql客户端中书写事务
      数据库中事务里的sql语句,可以选择提交或回滚。如果提交,数据库中的数据,才会发生真实修改。如果回滚,数据库的数据,会恢复到修改之前的状态。
      start transaction; 开启事务
      commit; 提交事务
      rollback; 回滚事务
      a. sql语句:
      start transaction;
      update user set money = money -100 where name= ‘a’;
      update user set money = money +100 where name= ‘b’;
      commit; 或 rollback;

    3. 使用JDBC控制事务
      在jdbc中,一个sql语句就是一个事务。现在需要多条sql位于一个事务中,所以需要开启事务。
      conn.setAutoCommit(boolean) 开启事务。默认值位true。代表开启自动提交,一句sql就是一个事务。修改为false,代表关闭自动提交,意味着开启使用,多个sql可以在同一个事务中执行。
      conn.commit() 提交事务
      conn.rollback() 回滚事务
      conn.rollback(Savepoint sp) 回滚到保存点
      a. 代码实现:
      package cn.tedu.trans;

      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.sql.SQLException;

    //事务测试
    public class TransDemo1 {

    public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    try {
    Class.forName(“com.mysql.jdbc.Driver”);
    conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/day16”, “root”, “root”);
    //开启事务
    conn.setAutoCommit(false);//默认值为true,代表会自动提交。
    //自动提交意味着一句sql就是一个事务,执行sql就会立刻提交这个事务,修改数据库中的数据。
    //设置为false,代表不会自动提交,表示开启事务,需要手动提交或回滚。
    ps = conn.prepareStatement(“update user set money = money -100 where name=?”);
    ps.setString(1, “a”);
    ps.executeUpdate();

    int i =1/0;

    ps = conn.prepareStatement(“update user set money = money +100 where name = ?”);
    ps.setString(1, “b”);
    ps.executeUpdate();

    conn.commit();//在sql书写完成之后,选择使用commit方法提交事务。
    } catch (Exception e) {
    if(conn !=null){
    try {
    conn.rollback();
    } catch (SQLException e1) {
    e1.printStackTrace();
    }
    }

e.printStackTrace();
}finally{
if(conn !=null){
try {

conn.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
if(rs !=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}
if(ps !=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
ps =null;
}
}

		                }
		
		        }
		
		}

b. 回滚到保存点
package cn.tedu.trans;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;

//事务测试
public class TransDemo2 {

public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Savepoint sp =null;
try {
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/day16”, “root”, “root”);

conn.setAutoCommit(false);//默认值为true,代表会自动提交。
ps = conn.prepareStatement(“update user set money = money -100 where name=?”);
ps.setString(1, “a”);
ps.executeUpdate();

ps = conn.prepareStatement(“update user set money = money +100 where name = ?”);
ps.setString(1, “b”);
ps.executeUpdate();

//添加保存点
sp = conn.setSavepoint();

ps = conn.prepareStatement(“update user set money = money -100 where name=?”);
ps.setString(1, “a”);
ps.executeUpdate();

int i =1/0;

ps = conn.prepareStatement(“update user set money = money +100 where name = ?”);
ps.setString(1, “b”);
ps.executeUpdate();

conn.commit();//在sql书写完成之后,选择使用commit方法提交事务。
} catch (Exception e) {
if(conn !=null){
try {
if(sp != null){
conn.rollback(sp);//回滚事务
//剩下的代码继续提交
conn.commit();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}

e.printStackTrace();
}finally{
if(conn !=null){
try {

conn.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}

if(rs !=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}
if(ps !=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
ps =null;
}
}

		                }
		
		        }
		
		}
  1. 事务的四大特性(ACID)
    原子性(Atomicity):事务是一个不可分割的单位,事务中的每一个单元都必须同时成功或失败。
    一致性(Consistency):事务修改数据前后,数据库中的数据完整性仍然保持一致。
    隔离性(Isolation):数据库中的一个事务,可能被其他事务的影响,为了避免这种影响,数据提供一些隔离级别的选项,让用户自行选择隔离别。
    持久性(Durability):一旦数据库的事务提交,数据就会发生永久性的改变,任何操作都无法撤销这个过程,即使数据库损毁也不能恢复到原有数据内容。

    a. 数据库中针对四大特性中的三个特性:原子性,一致性,持久性都作出的具体的实现,开发时直接使用它的特性即可。唯独隔离性,并未作出具体实现。原因是:
    每一个数据库的用户对隔离级别的看法不同,数据库不能写死其隔离级别,如果写死,可能对某些操作过于不友好,所以隔离性应该由用户自行设置。
    b. 如果你是数据库的开发人员,如何来设计数据的隔离级别:
    考虑数据库的隔离级别,就是分析数据库中事务之间的线程安全问题,所以可以从数据涉及读和写来分析。
    i. 两个事务同时读取数据:
    这种情况不会存在线程安全问题。因为两个事务对应的线程都未对数据进行修改,任何人读取的数据内容都相同。
    ii. 两个事务同时写入数据:
    这种情况一定会存在线程安全问题。因为两个事务对应的线程都会对数据进行修改,第二次修改可能会覆盖第一次的操作,所以应该将两个事务严格分开。可以通过修改数据库的隔离级别,来保证两个事务分别执行,避免出现线程安全问题。
    iii. 一个事务写,一个事务读:
    在面对一个事务写,一个事务读这种情况时,可以选择修改数据库的隔离级别达到自己想要的效果。如果要求数据库安全性与正确性,则可以选择最高的隔离级别。如果要求数据的写入速度,则可以选择数据库的最低隔离级别。多数情况需要两者兼顾,可以选择中间两个隔离级别。面对数据库中存储的脏读,不可重复读,虚读/幻读这些情况,也可以根据自己的需求,修改数据库的隔离级别。
    防止这些问题的隔离级别,推荐使用repeatable read。原因是这个级别,不会出现脏读,和不可重复读。而虚读出现的情况极少。可以认为当前隔离级别是安全且能够保证执行效率的。

    1. 情景分析
      a. 脏读:一个事务读取到另一个未提交事务的数据,导致数据前后读取不一致。

      a:1000 买家
      b:1000 商家

      a:start transaction;
      update user set money = money -100 where name=‘a’;
      update user set money =money +100 where name =‘b’;
      -----------------------------------------------------------------------------
      b:start transaction;
      select * from user where name =‘b’;-----1100
      -----------------------------------------------
      a:rollback;
      -------------------------------------------------------
      b:select * from user where name =‘b’;-----1000
      b. 不可重复读:一个事务读取到另一个已经提交事务的数据,在事务提交前后,读取的数据内容不一致,这种现象就称之为不可重复读。
      ------------------------------------------------------------------------a:1000 1000 1000------------------------------------------------------------------------
      b:start transaction;
      select hq from account where name=‘a’;----1000;
      select dq from account where name=‘a’;----1000;
      select gp from account where name=‘a’;----1000;
      -----------------------------------------------------------------
      a:start transaction;
      update account set hq = hq-100 where name=‘a’;
      commit;
      ------------------------------------------------------------------
      b:
      select hq+dq+gq from account where name=‘a’;—2900
      --------------------------------------------------------------------
      hq 1000
      dq 1000
      gp 1000
      sum 2900
      c. 虚读/幻读:在整表操作中,一个事务读取到另外一个已经提交事务的数据,导致事务提交前后数据不一致的现象,这种现象称之为虚读/幻读。
      -----------------------------------------------------------------------------a:1000
      b:2000

      d:
      start transaction;
      select sum(money) from user; ----3000
      select count(money) from user; ----2
      ---------------------------------------------------------------------
      c: 3000
      start transaction;
      insert into user values(‘c’,3000);
      commit;
      ----------------------------------------------------------------------
      d:
      select avg(money) from user;---- 1500

    2. 数据库隔离级别
      在数据库的事务特性当中,唯独没有实现隔离性的内容,转而是为用户提供四个隔离界别的选项,用户可以根据自己的需求,选择合适隔离级别。
      a. 四个隔离级别:
      read uncommitted; 脏读,不可重复读,虚读/幻读,都会出现。数据库安全性最低,但是效率最高。
      read committed; 可以防止脏读。不可以防止不可重复读及虚读/幻读。数据库安全性较低,效率较高。
      repeatable read; 可以防止脏读,和不可重复读。不可以方式虚读/幻读.数据库的安全性较高,效率较低。mysql默认的隔离级别。
      serializable; 可以防止脏读,不可重复度和虚读/幻读.它是mysql最高的隔离级别。是一个串行化的处理方式,虽然安全性较高,但是执行效率过低。
      b. 隔离级别的选择
      安全:serializable > repeatable read >read committed>read uncommitted
      性能:read uncommitted>read committed> repeatable read>serializable
      i. 由于read uncommitted隔离级别过低,安全性保证较差,所以实际开发中使用较少。serializable隔离级别过高,安全性虽然较高,但是效率极低,对于开发影响较大,所以实际开发中很少使用。
      ii. read committed和repeatable read两个隔离级别较为使用,对于数据安全性和语句执行效率都有较好的保证,开始时多数会采用两者其中一个。

    3. 修改数据库的隔离级别
      set global/session transaction isolation level read uncommitted;
      set transaction isolation level read uncommitted;临时修改,只对下一次的事务生效

    4. 查询当前数据库的隔离级别
      select @@tx_isoaltion;

    5. 数据库中的锁
      共享锁和排它锁
      a. 在非serializable隔离级别之下,查询不加锁。增删改添加排它锁。
      b. 在serializable隔离级别之下,查询添加共享锁。增删改添加排它锁。
      c. 特点:
      i. 共享锁和共享锁可以共存。
      ii. 共享锁和排它锁不能共存。
      iii. 排它锁和排它锁不能共存。

    6. ~表级锁和行级锁
      表级锁:添加锁之后,当前表格,不允许另外的事务操作。
      行级锁:添加锁之后,当前表格中的某一行或几行,不允许另外的事务操作。

    7. 死锁
      两个线程相互等待对方释放资源,这种现象称之为死锁。
      解决死锁的方案:
      i. 销毁其中任意一个线程。
      ii. 修改代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值