原子性,隔离性,持久性3种特性共同实现保证的一致性
ACID:
原子性:全部成功执行或者全部不执行
一致性:保证了当事务结束后,系统状态是一致的,最核心和最本质的要求
隔离性:使得并发执行的事务,彼此无法看到对方的中间状态,锁,mvcc(多版本并发控制)
持久性:保证了事务完成后所作的改动都会被持久化 ,redo log
数据库的事务隔离级别有四种,分别是读未提交、读已提交、可重复读、序列化,不同的隔离级别下会产生脏读、幻读、不可重复读等相关问题,因此在选择隔离级别的时候要根据应用场景来决定,使用合适的隔离级别。
各种隔离级别和数据库异常情况对应情况如下: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写锁阻塞读的案例:
当一个线程获得对一个表的写锁之后,只有持有锁的线程可以对表进行更新 *** 作。其他线程的读写 *** 作都会等待,直到锁释放为止。
lock table mylock write;
select * from mylock;
insert into mylock values(5,‘e’);当前session对表的查询会被阻塞
select * from mylock;
unlock tables;当前session能够立刻执行,并返回对应结果
MyISAM读阻塞写的案例:
一个session使用lock table给表加读锁,这个session可以锁定表中的记录,但更新和访问其他表都会提示错误,同时,另一个session可以查询表中的记录,但更新就会出现锁等待。
lock table mylock read;
select * from mylock;当前session可以查询该表记录:
select * from mylock;
select * from person
Table ‘person’ was not locked with LOCK TABLES当前session可以查询或者更新未锁定的表
select * from mylock
insert into person values(1,‘zhangsan’);
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将使用表锁!
如果加排他锁可以使用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');
select * from tab_no_index where id = 1;set autocommit=0
select * from tab_no_index where id =2
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');
select * from tab_with_indexwhere id = 1;set autocommit=0
select * from tab_with_indexwhere id =2
3、由于mysql的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是依然无法访问到具体的数据
insert into tab_with_index values(1,'4');
虽然session2访问的是和session1不同的记录,但是因为使用了相同的索引,所以需要等待锁
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)