当多个请求同时请求数据库时,如果不进行并发控制,会导致数据不一致,InnoDB保证数据一致性的方式有以下两种:
- 锁
- 数据多版本控制
一,使用锁控制
悲观锁:就是每次拿数据都会认为别的事务也会拿数据,拿来数据就先上锁,这样别的线程拿数据就会阻塞。像行锁,页锁,表锁以及共享锁,排它锁都是悲观锁。我们说的那些锁一般都是悲观锁。
乐观锁:就是没有锁,默认其他线程不会改变数据,所以不加锁,只是在提交的时候判断是不是被更改过了,一般用版本戳实现。
按照锁的模式分为共享锁和排它锁
共享锁(Share Locks,S锁):一个线程给数据加上共享锁后,其他线程只能读取数据,不能修改。
排它锁(eXclusive Locks,X锁):一个线程给数据加上排它锁后,其他线程不能读取也不能修改。
兼容性规则为:共享锁和共享锁兼容,排它锁和其他的锁都排斥。
如果一个请求的锁模式与当前的锁兼容,InnoDB就将请求授予该事务,反之,如果两者不兼容,该事物就会等到锁释放。
对于update,delete和insert语句,InnoDB会自动给涉及的数据加排它锁;
对于普通的select语句,InnoDB不会加任何锁;
事务也可以通过以下语句显示的给事务加共享锁和排它锁:
select * from table_name where ... lock in share mode 会给事务加上共享锁;
select * from table_name where ... for update 会给事务加上排它锁。
按照锁的类型分为行锁和表锁以及页锁
行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件来检索数据才会用到行锁,否则InnoDB将会使用表锁。
行锁:select * from table_name where id = 1 for update 。id 字段为唯一索引字段,所以使用的就是行锁,且是排它锁。
表锁:select * from table_nane where name = ‘小巷’ for update 。name字段不是唯一索引字段,所以是表锁。
页锁:行锁锁行,表锁锁表,页锁折中,锁相邻的一组数据。
通过加锁控制,可以保证数据的一致性,但是同样一条数据,不论用什么样的锁,只可以并发读,并不可以读写并发,这时就要引入数据多版本控制了。
二,使用数据多版本(MVCC)控制
使用锁控制并发时,只要是写数据的任务没有完成,数据就不可以被其他的任务获取,就连读数据的select操作也会阻塞,这对并发度要求较大的环境有很大的影响,为了解决这个问题引出了数据多版本。
数据多版本实现的原理是:
1,写任务发生时,首先复制一份旧数据,以版本号区分
2,写任务操作新克隆的数据,直至提交
3,并发读的任务可以继续从旧数据(快照)读取数据,不至于堵塞
- 排它锁 是 串行执行
- 共享锁 是 读读并发
- 数据多版本 是 读写并发
三,redo,undo,回滚段
在InnoDB的具体实现上,依赖redo日志,undo日志,回滚段(rollback segment)
redo日志的作用?
数据库事务提交后,按照随机方式写入磁盘上性能太低,为了提高效率先写到redo日志里,再刷到磁盘上(此时变成了顺序写)。
假如我在提交事务时数据库崩溃了,重启时,会从redo日志里把没有刷到磁盘的数据刷到磁盘上。
简言之,redo的作用是为了保障已提交事务的ACID特性
undo日志的作用?
数据库修改数据但未提交时,会将事务修改数据的镜像(即旧数据)存到undo日志里,当事务回滚或数据库崩溃时,会从undo日志里获取旧数据,避免未提交数据对数据库的影响。
简言之,undo的作用是为了保障未提交的事务不会对数据库的ACID产生影响
回滚段的作用?
存储undu日志的地方,就是回滚段。
- 快照读不加锁
- InnoDB所有的普通select都是快照读,所以可并发
https://blog.csdn.net/xiangwanpeng/article/details/55106732