数据库事务
定义
事务是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
以A转账给B为例:
- 正常操作:A扣钱,B收钱
- 异常:A扣钱,异常导致B未收到钱,则事务会回滚,将A的钱退回
A扣钱+B收钱,这两个逻辑单元就组成了一个事务,要么一起操作成功,要么一起操作失败
数据库事务的特性 ACID
- 原子性
Atomic:表示组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有的操作执行成功,整个事务才提交。 - 一致性
Consistency:事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。 - 隔离性
Isolation:在并发数据操作的时,不同事务拥有各自的数据空间,它们的操作不会对对方产生干扰。 - 持久性
Durabiliy:一旦事务提交成功后,事务中的所有数据操作都必须被持久化到数据库中。
数据并发问题
-
脏读:A事务读到了B事务的未提交的数据,并在这个数据的基础上进行操作
-
不可重复读:A事务读取了B事务已经提交的更改数据(更改或者删除)
解决方案:对操作的事务添加行级锁,阻止操作中的数据发生变化
-
虚读/幻象读:A事务读取B事务提交的新增数据,导致A多次统计结果不一致
解决方案:添加表级锁 — 将整张表锁定,防止新增数据
-
第一类丢失更新:A事务撤销时,把已经提交的B事务的更新数据覆盖了
-
第二类丢失更新:A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失
数据库锁机制
锁定对象不同:表锁定和行锁定
并发事务锁定的关系:共享锁定和独占锁定
数据库常用5种锁定:行共享锁定、行独占锁定、表共享锁定、表共享行独占锁定、表独占锁定
事务隔离级别
只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加合适的锁。
事务的隔离级别和数据库并发性是对立的,READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量。
| 隔离级别 | 脏读 | 不可重复读取 | 幻象读 | 第一类丢失更新 | 第二类丢失更新 |
|---|---|---|---|---|---|
| READ UNCOMMITED | O | O | O | X | O |
| READ COMMITED | X | O | O | X | O |
| REPEATABLE READ | X | X | O | X | X |
| SERIALIZABLE | X | X | X | X | X |
Spring与线程安全
ThreadLocal
Spring中多个DAO可以复用同一个模板实例而不会发生冲突,主要是应用了ThreadLocal.
ThreadLocal:不是一个线程,而是保存线程本地化对象的容器,当运行于多线程环境的某个对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。
实现:在ThreadLocal类中有一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,值为对应线程的变量副本。
解决多线程中的并发访问:
线程同步机制:通过对象的锁机制保证同一时间只有一个线程访问变量,以时间换空间,访问串行化,对象共享化
ThreadLocal:为每个线程提供一个独立的变量副本,从而隔离了多个线程堆访问数据的冲突。以空间换时间,访问并行化,对象独享化
一般在Web应用中,从接收请求到返回响应经过的所有程序调用都同属于一个线程。
Spring利用ThreadLocal对Bean改造实现线程安全
public class Demo{
//一个非线程安全的变量
private Connection conn;
public void add() throw SQLException{
//引用非线程安全变量
Statement statement=getConnection().createStatement();
}
}
/*********************** ThreadLocal 线程安全 **************************************/
public class Demo {
//使用ThreadLocal保存Connection变量
private static ThreadLocal<Connection> connectionThreadLocal=new ThreadLocal<Connection>();
public static Connection getConnection(){
//如果connThreadLocal没有本线程对应的Connection,
// 则创建一个新的Connection,保存到线程本地变量中
if(connectionThreadLocal.get()==null){
Connection connection=ConnectionMan.getConnection();
connectionThreadLocal.set(connection);
return connection;
}else{
//直接返回线程本地变量
return connectionThreadLocal.get();
}
}
public void add(){
try {
Statement statement=getConnection().createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
下一篇:Spring学习笔记[7]之Spring事务管理 https://blog.youkuaiyun.com/Rabbit_Judy/article/details/81842676
702

被折叠的 条评论
为什么被折叠?



