数据库笔记

本文主要分析在不牵扯分布式的情况下事务与一致性的实现办法。先说一下事务的ACID四个特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
    其他的几个特性应该很好理解,但是一致性这个概念不太好理解,在这一致性指的是数据比较合理,在经过一系列的操作之后数据依然是我们想要看到的,并不是指的语法上的规则,常见的说明例子就是转账问题。
    redu和undo机制是数据库实现事务的基础。redu日志用来在断电/数据库崩溃等状况发生时重演一次刷数据的过程,把redo日志里的数据刷到数据库里,保证了事务的持久性(Durability);undo日志是在事务执行失败的时候撤销对数据库的操作,保证了事务的原子性(Atomicity)。
    但是只有只有原子性和持久性是无法保证数据一致性的,例如:事务1对A用户加了100元,首先获取A的数据,但是这时事务2也对A加了100元,结果应该是加了200元,但是事务1覆盖了事务2,最后只加了100元,所以需要把两个事务隔离起来,即隔离性(Isolation),使的两个事务并发和串行执行的结果都是一致的。
    数据库使用锁的方式保证隔离性(也就是最终数据保证了一致性),InnoDB实现的方式有以下两种:
悲观锁控制
乐观锁(数据多版本)控制
悲观锁:拿来数据就先上锁,这样别的线程拿数据就会阻塞。像行锁,页锁,表锁以及共享锁,排它锁都是悲观锁。我们说的那些锁一般都是悲观锁。
乐观锁:悲观锁只是一种概念,其实没有锁,MySql默认其他线程不会改变数据,所以不加锁,只是在提交的时候判断是不是被更改过了,一般用版本戳(MVCC )来实现乐观锁。

一,使用(悲观)锁控制
按照锁的使用方式分为共享锁和排它锁。
共享锁(Share Locks,S锁):一个线程给数据加上共享锁后,其他线程只能读取数据,不能修改。
排它锁(eXclusive Locks,X锁):一个线程给数据加上排它锁后,其他线程不能读取也不能修改。
没有锁:InnoDB所有的普通select都是快照读,都不加锁。
兼容性规则为:共享锁和共享锁兼容,排它锁和其他的锁都排斥。
    如果一个请求的锁模式与当前的锁兼容,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_nane where name = ‘小巷’  for update 。name字段不是唯一索引字段,所以是表锁。
行锁:select * from table_name where  id = 1 for update 。id 字段为唯一索引字段,所以使用的就是行锁,且是排它锁。
页锁(又叫Gap锁):又称为间隙锁。所谓表锁锁表,行锁锁行,那么页锁折中,锁相邻的一组数据,可以用来防止幻读。
通过加锁控制,可以保证数据的一致性,但是同样一条数据,不论用什么样的锁,只可以并发读,并不可以读写并发(因为写的时候加的是排他锁所以不可以读),这时就要引入数据多版本控制了,也就是乐观锁这个概念。
    看完以上可能觉得有点,又是按照粒度又是按照使用方式分的。单独理解就行,没必要关联一下,另外上面说到的这些锁都算作是悲观锁的概念。
二,使用数据多版本(MVCC)控制
    使用锁控制并发时,只要是写数据的任务没有完成,数据就不可以被其他的任务获取,就连读数据的select操作也会阻塞,这对并发度要求较大的环境有很大的影响,为了解决这个问题引出了数据多版本。
数据多版本实现的原理是:
1,写任务发生时,首先复制一份旧数据,以版本号区分
2,写任务操作新克隆的数据,直至提交
3,并发读的任务可以继续从旧数据(快照)读取数据,不至于堵塞
排它锁 是 串行执行
共享锁 是 读读并发
数据多版本 是 读写并发

三,redo,undo,回滚段

在InnoDB的具体实现上,依赖redo日志,undo日志,回滚段(rollback segment)
redo日志的作用?
    数据库事务提交后,按照随机方式写入磁盘上性能太低,为了提高效率先写到redo日志里,再刷到磁盘上(此时变成了顺序写)。
假如我在提交事务时数据库崩溃了,重启时,会从redo日志里把没有刷到磁盘的数据刷到磁盘上(redo意为把刷磁盘的操作进行一次重演)。简言之,redo的作用是为了保障已提交事务的ACID特性,也就是保证了数据的D(Durability)持久性。

undo日志的作用?
    数据库修改数据但未提交时,会将事务修改数据的镜像(即旧数据)存到undo日志里,当事务回滚或数据库崩溃时,会从undo日志里获取旧数据,避免未提交数据对数据库的影响。简言之,undo的作用是为了保障未提交的事务不会对数据库的ACID产生影响。也就是保证了操作的A(Atomicity)原子性。
回滚段的作用?
    存储undu日志的地方,就是回滚段。

InnoDB所有的普通select都是快照读,快照读不加锁。这也是InnoDB支持高并发的一个原因之一

posted @ 2020-03-27 16:49  moxin0509  阅读(130)  评论(0编辑  收藏  举报