事务和锁
事务
事物的特性
持久性
事务commit之后会被刷新到磁盘,不可逆
隔离性问题
脏读
A事务读到了B事务修改但是没有提交的数据,A做业务操作,此时B回滚,那么A拿的就是脏数据
虚读
A读了一批数据做业务,B来新增了一条数据,A再来读发现多了一条数据
不可重复读
A读了一条数据做业务,B来把这条数据修改了,A再来读发现数据变了
隔离级别解决隔离问题
读未提交
设置数据库隔离级别为读未提交,不能解决任何问题
可重复度
解决虚读,会出现不可重复读
oracle默认隔离级别
串行化
解决不可重复度,执行效率低下,不推荐
数据库锁
乐观锁
cas理论实现乐观锁,每行记录维护一个version字段
悲观锁
行级锁
使用主键作为条件,会对查询出来的数据行加锁,只有当事务提交或者回滚才会释放锁,自动释放锁,会出现死锁
表锁
for update查询数据条数不确定,或者没有使用主键作为查询条件,会使用表锁,与上面的区别只在于锁的记录行数
间隙锁
当执行一条for update 不是数据库表索引时,会把该索引所落到的间隙锁住,已存在小于for uodate的最大值<间隙<已存在大于for uodate的最小值
库锁
通过Flush tables with read lock命令实现,数据库会变成只读状态,unlock tables命令解锁。
与数据库设置只读相比,这个命令会在客户端异常时自动释放锁,只读不会。不应该使用库锁做数据库备份,应该用mysql自带备份工具mysqldump做备份,在可重复读隔离级别下会自动开启一个事务保证导出数据的一致性
for update会被多个线程同时持有,导致死锁,当某一行数据不存在时,对其加for update锁,这个锁是会被多个线程同时持有,这时对这个数据更新时,都不能成功,死锁,第一个更新的线程会一直等待,第二个更新的线程执行检测到死锁会报错,此时第二个线程回滚,第一个线程就能更新成功
mvcc机制 (可重复读)
undolog示例: 1(id) 'chen'(name) 100(tx_id) 上一条的地址
前提:当事务开启,执行第一条更新语句时候,会向mysql申请事务id(tx_id),严格按照时间顺序生成,此tx_id会在记录undolog的时候同时记录<br> 执行第一条查询语句,会生成针对该事务的一致性视图(read view),就相当于一个快照,read view在事务提交之前一直有效,read view是由当前时间所有未提交的事务id数组和已提交的最大事务id组成,例1,[100,150,200]300,例2,[100,150,200]80,例3,[100,150,200]180,根据read view,可将undolog(根据tx_id)分为三块,例1中min_id=100,max_id=300,例2中min_id=80,max_id=200,例3中_min_id=100,max_id=200
版本连比对规则
1,如果是当前事务id修改的,那么一定可见
2,如果tx_id<min_id,那么一定是已提交的事务,可见
3,如果tx_id>max_id,那么一定是之后生成的事务,不可见
4,如果min_id<=tx_id<=max_id,如果tx_id在read view的数组里面,不可见,反之,可见
查询语句如何拿到自己想要的数据?根据最后一条undolog依次向前找,根据上述规则,直到找到可见的数据,返回
关于读已提交,与上述规则一致,只是每次读的时候生成新的read view
sql执行过程
基本过程
客户端->server层->存储引擎层->数据文件
具体步骤
server层
缓存
查询缓存是否存在数据,如果已存在,直接返回给客户端,如果不存在,告诉连接器没有缓存
查询分析器
没有缓存情况下,会调用分析器,拆分sql语句,语法分析,此处会返回语法错误
存储引擎层
1,从磁盘将查询到的数据load到buffer pool,这里是根据数据页load的,所以查询一条数据,他附近的数据也会被load进去,mysql数据页大小为16kb
2,写undolog,将修改之前的数据记录在undolog中,方便以后回滚
3,更新buffer pool
4,写redolog buffer(redolog日志的一块buffer区)
5,提交事务,将redolog buffer写入redolog日志,此时redolog是准备状态
6,写binlog
7,binlog写完会通知redolog将状态改为commit状态,并且返回给客户端事务提交成功
8,buffer pool不定期将数据刷新到磁盘
细节详解
server实现了cache,但是这个cache很鸡肋,因为这里面的任意一条数据一旦被更新,整个cache就失效,所以在mysql8以后就移除了
buffer pool是不定期往数据文件写的,所以可能还没有更新到文件mysql宕机,这样数据一致性不能保证,再次重启,mysql会根据redolog恢复buffer pool,然后再往数据文件中写
提交事务成功的标志,二阶段提交完成,redolog变成commit,就认为事务提交成功,而不需要等到数据同步到磁盘,WAL机制(缓存定期写磁盘,比每次都直接往磁盘写要快,并且写磁盘不是顺序写入,效率低)