C++八股--6--mysql 日志与并发控制
这里向大家介绍一下数据库基础:共分为以下章节
10前序.日志系统
这是数据库的核心。我放到首页来介绍,给大家一个前置概念,方便进行更好的学习
日志文件我们用来记录事务对数据库更新操作的文件,分为以记录为单位的文件和数据块为单位的文件
1.以记录为单位:每条记录包含 事务标识,操作类型,操作对象,更新前的值,更新后的值。
2.以数据块为单位:包含事务标识,被更新的数据块。
note:必须先写日志文件,再写数据库:不然你出故障找谁去
日志文件我们用到的为以下几种
1.undo log 回滚日志
实现事务原子性和多版本并发控制(MVCC)的核心机制,主要记录事务修改前的数据状态,方便回滚
主要作用
事务回滚:当事务失败或主动执行rollback的时候,利用undo log使其恢复到修改前状态
一致性读:为其它事务提供历史版本数据,避免读写冲突
崩溃恢复
工作原理:
每次事务修改前,都会将原始数据复制到undo log
版本链:同一行数据的多次修改会形成版本链,通过事务ID和指针串联历史版本
2.redo log 重做日志
确保事务的持久性,记录物理数据页的修改
这里解释一下原因,学过操作系统的朋友都知道,存储都是以块为单位的,但是我们不能因为这个阻塞进程吧
那么我们就通过redo log记录修改,如果内存向磁盘中还未写完就发生故障(如果正常完成会给个信号的)
那么依靠redo log,我们就可以再次进行写
作用:数据库重启,通过redo log重放未刷盘的数据
3.slow query log 慢查询日志
优化mysql 必不可少的日志,记录执行超过用户设定时间的语句
10.数据库恢复技术
数据库恢复技术说白了就是事务
10.1 事务定义:首先需要理解一下什么是事务
一个数据库操作序列
一个不可分割的工作单位
恢复和并发控制的基本单位
通俗来说:就是一组数据库操作叫做事务
语法:共有三个关键字:begin transaction, commit, rollback
事务特性:ACID
原子性:atomicity :数据库中列不可再分
一致性:consistency :确保一致性:我给你转50,你那边必须加五十,达成总数一致
隔离性:isolation 这个是重点,会有专门的章节介绍
持久性:durability 利用redo log确保修改成功
在现实生活中,故障是不可避免的,如果银行系统故障给你发了一百万,那银行肯定是不愿意的
那么对于数据库故障状态恢复到某一已知的正确状态就显得尤为重要
那么故障都有什么:事务内部故障,系统故障,介质故障,计算机病毒
对于事务内部的故障:由于事务语句逻辑错误造成
我们通常使用undolog(回滚日志)来解决,还是那句话:mysql的核心是日志
对于系统故障:通常未造成系统停止运转的任何事件,使得系统需要重新启动
恢复策略,1.事务未提交,还是undo log
2.事务已经提交,那么就是redo log
介质故障:就是硬件发生故障了
恢复策略:装入数据库发生介质故障前某个时刻的副本(就是备份然后恢复)
重做自此时开始的所有成功事务,将这些事务已经提交的结果重新记入数据库
讲道理:这不就是备份重做吗
计算机病毒:这个就比较抽象,类型很多,啥都能干
数据库恢复:说白就是数据转储(备份),日志(这个是核心,喷不了)
那说到备份,这里介绍一下吧
转储就是将DBA复制一份到磁带或者另一个磁盘保存起来
数据库遭到破坏可以将后备副本重新装入
转储分为静态和动态
1.静态
在系统无运行事务的时候进行的操作(静态都这样)
转储开始时处于事务一致性状态(最起码要保证数据库是正确的吧,不然我复制干嘛)
期间不允许对数据库任何存取,修改(联想打开文件无法复制)
2.动态
转储操作和用户事务并发运行
转储期间允许修改和存取
动态不能确保副本中数据正确有效
动态转储进行故障恢复:需要把动态转储期间各事务对数据库修改操作活动记录下来,建立日志文件(不愧是核心,哪里都有)
海量转储:每次全部存储
增量存储:只存储上次转储后更新过的数据
11.并发控制
在数据库的实际使用中,我们不得不考虑多线程下的数据库并发
也就是多个事务共同运行带来的问题
这里出现的问题分为以下三种:
1.脏读(这个在实际中是不被允许的):一个事务读取了另一个未提交事务修改的数据,然后另一个事务rollback
2.幻读:同一个事务多次查询同一范围内的数据,结果集的行数不同(别的事务插入或者删除了数据导致,这个事务觉得自己眼睛花了,所以叫幻读)
3.不可重复读:同一事务多次读取同一数据,结果不一致
这里就要重提我们前文提到的事务的四大特性中的隔离性了,我们核心就是 锁+mvcc(多版本并发控制)了
这里我先简要介绍一下事务的隔离级别
1.读未提交:事务可以读取其他事务未提交的数据
2.读已提交:事务只能读取其他事务已提交的数据
3.可重复读:同一事务内多次读取同一数据的结果一致,即使其他事务修改并提交了该数据。
4.串行化:所有事务按顺序执行,完全隔离(实际不考虑)。以上三种问题都可以避免
上述四种做法随着隔离级别的提高,效率也依次降低,(mysql默认工作在可重复读,oracle默认工作在读已提交)
那么既然谈到锁了,我们就介绍一下锁吧
锁在作用范围上分为表锁和行锁,表锁就是对表加锁(这实际上会造成效率低下),行级锁是mysql的innodb支持的锁,相较于表锁效率更高
事务执行对数据对象加锁,这样就会阻塞其余事务对其的操作
在类型上,锁分为排它锁和共享锁
排它锁(写锁 X锁):事务T对对象A上锁后,只允许T读取和修改A,其余事务不允许再加锁了,直到T释放锁
共享锁(读锁 S锁):事务T对对象A上锁后其余人也可以继续加X锁,但是不能加X锁,知道所有事务的锁释放
使用该机制S锁解决不可重复读的问题,我都不允许你改了,我肯定不能读到不同的数据了吧
该行可以不看:实际上mysql中我们的行级锁(又称为间隙锁)其根据索引锁住的为间隙
下面在介绍一些相关概念:
可串行化调度:多个事务并发执行时正确的,仅当其结果按照某一次序执行这些事务的结果相同
解释一下,就是多个事务语句交替执行的结果和按照顺序执行这些事务的结果一致
两段封锁协议:最常用的一种封锁协议,理论上证明使用两端封锁协议产生的为可串行化调度
事务分为两个阶段进行加锁和解锁
1.在对任何数据进行读写操作之前,需要获取对该数据的封锁
2.在释放一个封锁后,事务不在申请和获得任何其他封锁
最后介绍一下意向锁,
当使用表锁的时候,涉及到一个效率的问题。
当你想对一个表加锁的时候,最起码需要确定这张表没有被其它事务获取过X锁!这张表中的数据没有被获取过X锁!
这里就引入了意向共享锁和意向排他锁来解决效率问题,行锁导致效率降低
意向共享锁(IS):事务要对行加共享锁之前,需要先获取该表的IS锁
意向排他锁(IX):事务要对行加排它锁之前,需要先获取该表的IX锁
在实际上,意向锁时存储引擎自动加的,为表级锁,互相兼容