事务的传播特性与原理

事务的传播特性与原理,第1张

事务的传播特性与原理 事务的特性及其原理 事务的特性

原子性,隔离性,持久性3种特性共同实现保证的一致性

​ ACID:

​ 原子性:全部成功执行或者全部不执行

​ 一致性:保证了当事务结束后,系统状态是一致的,最核心和最本质的要求

​ 隔离性:使得并发执行的事务,彼此无法看到对方的中间状态,锁,mvcc(多版本并发控制)

​ 持久性:保证了事务完成后所作的改动都会被持久化 ,redo log

​ 数据库的事务隔离级别有四种,分别是读未提交、读已提交、可重复读、序列化,不同的隔离级别下会产生脏读、幻读、不可重复读等相关问题,因此在选择隔离级别的时候要根据应用场景来决定,使用合适的隔离级别。

各种隔离级别和数据库异常情况对应情况如下: 隔离级别脏读不可重复 读幻读READ- UNCOMMITTED√√√READ-COMMITTED×√√REPEATABLE- READ××√SERIALIZABLE××× SQL 标准定义了四个隔离级别:

READ-UNCOMMITTED(读取未提交): 事务的修改,即使没有提交,对其他事务也都是可见的。事务能够读取未提交的数据,这种情况称为脏读。READ-COMMITTED(读取已提交): 事务读取已提交的数据,大多数数据库的默认隔离级别。当一个事务在执行过程中,数据被另外一个事务修改,造成本次事务前后读取的信息不一样,这种情况称为不可重复读。REPEATABLE-READ(可重复读): 这个级别是MySQL的默认隔离级别,它解决了脏读的问题,同时也保证了同一个事务多次读取同样的记录是一致的,但这个级别还是会出现幻读的情况。幻读是指当一个事务A读取某一个范围的数据时,另一个事务B在这个范围插入行,A事务再次读取这个范围的数据时,会产生幻读SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。

Undo log原理(原子性)

Undo Log是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC)

在 *** 作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态

redo log原理(持久性)

当发生数据修改的时候,innodb引擎会先将记录写到redo log中,并更新内存,此时更新就算是完成了,同时innodb引擎会在合适的时机将记录 *** 作到磁盘中

Redolog是固定大小的,是循环写的过程

有了redolog之后,innodb就可以保证即使数据库发生异常重启,之前的记录也不会丢失,叫做crash-safe

binlog(与特性无关)

inlog是server层的日志,主要做mysql功能层面的事情

与redo日志的区别:

1、redo是innodb独有的,binlog是所有引擎都可以使用的

2、redo是物理日志,记录的是在某个数据页上做了什么修改,binlog是逻辑日志,记录的是这个语句的原始逻辑

3、redo是循环写的,空间会用完,binlog是可以追加写的,不会覆盖之前的日志信息

锁(隔离性) 表的粒度

**表级锁:**开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

**行级锁:**开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

存储引擎锁
CREATE TABLE `mylock` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `mylock` (`id`, `NAME`) VALUES ('1', 'a');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('2', 'b');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('3', 'c');
INSERT INTO `mylock` (`id`, `NAME`) VALUES ('4', 'd');
MYISAM

表共享读锁,表独占写锁

MyISAM写锁阻塞读的案例:

​ 当一个线程获得对一个表的写锁之后,只有持有锁的线程可以对表进行更新 *** 作。其他线程的读写 *** 作都会等待,直到锁释放为止。

session1session2获取表的write锁定
lock table mylock write;当前session对表的查询,插入,更新 *** 作都可以执行
select * from mylock;
insert into mylock values(5,‘e’);当前session对表的查询会被阻塞
select * from mylock;释放锁:
unlock tables;当前session能够立刻执行,并返回对应结果

MyISAM读阻塞写的案例:

​ 一个session使用lock table给表加读锁,这个session可以锁定表中的记录,但更新和访问其他表都会提示错误,同时,另一个session可以查询表中的记录,但更新就会出现锁等待。

session1session2获得表的read锁定
lock table mylock read;当前session可以查询该表记录:
select * from mylock;当前session可以查询该表记录:
select * from mylock;当前session不能查询没有锁定的表
select * from person
Table ‘person’ was not locked with LOCK TABLES当前session可以查询或者更新未锁定的表
select * from mylock
insert into person values(1,‘zhangsan’);当前session插入或者更新表会提示错误
insert into mylock values(6,‘f’)
Table ‘mylock’ was locked with a READ lock and can’t be updated
update mylock set name=‘aa’ where id = 1;
Table ‘mylock’ was locked with a READ lock and can’t be updated当前session插入数据会等待获得锁
insert into mylock values(6,‘f’);释放锁
unlock tables;获得锁,更新成功 innodb

共享锁和排它锁

只能通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。

InnoDB行锁实现方式

​ InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

1、在不通过索引条件查询的时候,innodb使用的是表锁而不是行锁

create table tab_no_index(id int,name varchar(10)) engine=innodb;
insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
session1session2set autocommit=0
select * from tab_no_index where id = 1;set autocommit=0
select * from tab_no_index where id =2select * from tab_no_index where id = 1 for updateselect * from tab_no_index where id = 2 for update;

session1只给一行加了排他锁,但是session2在请求其他行的排他锁的时候,会出现锁等待。原因是在没有索引的情况下,innodb只能使用表锁。

2、创建带索引的表进行条件查询,innodb使用的是行锁

create table tab_with_index(id int,name varchar(10)) engine=innodb;
alter table tab_with_index add index id(id);
insert into tab_with_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
session1session2set autocommit=0
select * from tab_with_indexwhere id = 1;set autocommit=0
select * from tab_with_indexwhere id =2select * from tab_with_indexwhere id = 1 for updateselect * from tab_with_indexwhere id = 2 for update;

3、由于mysql的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是依然无法访问到具体的数据

insert into tab_with_index  values(1,'4');
session1session2set autocommit=0set autocommit=0select * from tab_with_index where id = 1 and name=‘1’ for updateocommit=0select * from tab_with_index where id = 1 and name=‘1’ for updateselect * from tab_with_index where id = 1 and name=‘4’ for update
虽然session2访问的是和session1不同的记录,但是因为使用了相同的索引,所以需要等待锁

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5708982.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存