MySQL-45讲学习笔记(1-3)

0. 开篇词

0.1 学习专题目的

  1. 在工作中对于MySQL,最重要的两点就是:如何设计表,还有如何对表查询进行优化!
  2. 能够写出逻辑正确的SQL语句,同时也能够知道语句是不是最优的
  3. 数据库使用出现了问题,能够快速定位问题,最终解决问题。
    高性能MySQL

0.2 MySQL心路历程(丁奇)

为什么要了解数据库原理

  1. 了解原理能让你更巧妙地解决问题
  2. 了解原理能帮你更好地定位问题
  3. 看得懂源码让你有更多的方法

0.2.2 MySQL学习路径

  1. 那首先要会,然后可以发现问题
  2. 下一步就是实践
  3. 推荐学习书籍:《高性能MySQL》,极力推荐书籍(比较厚,后面再看吧)

0.2.3 有哪些比较好的习惯和提高SQL效率的方法?

  1. 多写SQL,培养自己对SQL语句执行效率的感觉
  2. 在写的过程中,多进行分析,这个语句时间复杂度是多少,是全表扫描还是需要进行回表。

0.2.4 怎么学习C、C++?

先输入,然后按照自己的理解进行输出,知识的转化。

0.2.5 学数据库要保持什么心态?

很多知识是比较枯燥的,你需要坚持,然后在其中找到乐趣。多给自己一些反馈,相信时间的力量。

1. 讲基础架构:一条SQL查询语句是如何执行的

1.1 MySQL的框架有几个组件, 各是什么作用?

  1. 首先框架分为两块有,一个是Server层,另一个是Dao层,两个结合起来工作。其中Server层负责功能的逻辑实现,Dao层负责具体的数据查询。
  2. Dao层:该层主要是拥有存储引擎,进行数据的存储,提供读写的接口。
  3. Server层:连接器;查询缓存;分析器;优化器;执行器;
  4. 部件的作用:

1.2 Server层和存储引擎层各是什么作用?

  1. Server层:主要是对SQL查询语句的翻译,翻译成机器能够听懂的语言。MySQL功能层面的事情!
  2. 存储引擎层:通过翻译的语言,对数据库进行数据查询。负责存储数据的功能!

1.3 you have an error in your SQL syntax 这个保存是在词法分析里还是在语法分析里报错?

连接器,分析器,优化器和执行器,故语法报错在分析器的时候就卡壳了,因为是语法错误。

1.4 对于表的操作权限验证在哪里进行?

在执行器阶段进行,因为有些触发型的sql语句,在最后才知道执行的内容是什么,所以在最后一道保险处进行执行。

2.5 执行器的执行查询语句的流程是什么样的?

  1. SQL语句输入:首先书写一条SQL语句
  2. 客户端:SQL语句从客户端到服务端中的后端
  3. 连接器:执行语句前,先连接数据库。管理连接和一些权限验证
  4. 缓存查询:首选对SQL所要查询的内容在缓存中进行查询,如果存在,直接返回
  5. 分析器,优化器和执行器:分析器对SQL语句的语法和词法进行分析,单词拼错或者语句不规范直接返回错误;优化器对SQL语句找到最适合的索引;执行器调动存储引擎,对数据库中的数据进行查询。

1.6 附属疑问:

  1. 如果表T中没有字段k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。你觉得这个错误是在我们上面提到的哪个阶段报出来的呢?
    答案:在分析器阶段,分析语句的时候,也会分析所在的列是否存在。表的结构不属于数据,是可以被提前给解析出来的。
  2. 有个问题不太明白,为什么对权限的检查不在优化器之前做?
    答案:有些时候,SQL语句要操作的表不只是SQL字面上那些。比如如果有个触发器,得在执行器阶段(过程中)才能确定。优化器阶段前是无能为力的。
  3. 不建议使用查询缓存
    因为使用查询缓存只是在当数据库中的数据固定不变时,因为一旦更改数据库中的文件,那么就会删除全部的缓存内容!

2. 讲日志系统:一条SQL更新语句是如何执行的

查询和更新的逻辑相似!

2.1 更新语句流程

  1. 执行语句前:客户端与连接器相连,并进行权限验证
  2. 查询缓存/分析器:如果是查询缓存成功,直接返回结果;如果是分析器,则会分析这个SQL语句的语法和语义错误,发生错误进行返回。
  3. 优化器:根据语句存在的索引,找到最优索引值
  4. 执行器:根据找到的索引值,与数据库连接,找到对应的数据。(与查询的差异,存在两个日志的书写,方便)

2.2 两个日志:redo log(重做日志)和bin log(归档日志)

2.2.1 redo log(重做日志),server层日志

问题解决:在更新mysql数据库的时候存在一个为题:如果每次修噶一下数据,就对数据库进行更新,会导致更新太频繁,浪费资源,解决方法有:先把一批需要更新的内容记录下来,积攒了一部分之后再进行统一更新,多了一个缓存的作用,积攒一部分再一起做!

类比我开了一家酒店,每天喝酒的人需要记录账单,我有两种记录方式:1)对每个人的消费记录进行记录,需要频繁打开账本;2)在大堂前放一个黑板,消费一笔记一笔,黑板写满了,更新到账本中去,(这里账本就是MySQL数据库,黑板就是redo log日志,日志容量是有限的。容量是循环使用的,使用后面的,在保存前面的基础上进行删除)

redo log作用:当数据库发生异常重启的时候,能够保证之前对数据库的操作内容,全部得到保存,也叫做crash-safe能力。

2.2.2 bin log(归档日志),引擎层日志

问题一:为什么需要两个日志:因为之前非InnoDB引擎只有bin log日志,用于数据库重启异常,需要有一个日志进行操作记录,之后InnoDB引擎出来了,又多了个redo log日志,用于实现crash-safe功能。只有binlog不具备crash-safe能力,

问题二:为什么redo log具有crash-safe能力,而binlog不具备‘’
因为redo记录的内容为“循环写”物理日志,会详细到修改了那一页的信息,而binlog日志比较大条,只是“追加写”的逻辑日志,只能记录添加,删除,修改了哪一个内容,不能够细致到哪一步。
为什么redo log具有crash-safe能力,而binlog不具备

2.2.3 **redo log(重做日志)**与bin log(归档日志) 区别

  1. 使用范围:redo log日志是InnoDB引擎独有的,binlog是MySQL的server层实现的,是所有引擎都可以使用的。
  2. 存储内容:redo log是物理日志,记录:在哪个数据页修改了什么内容,做了什么修改;binlog是逻辑日志,记录行的内容/sql语句两种情况。故redolog具有crash-safe功能,而binlog不具备
  3. 存储容量:redo log是循环写的,空间固定总会用完,binlog是可以追加书写的!

2.3 执行器和InnoDB引擎在执行简单update语句时的内部流程

如上图所示:其中浅色为InnoDB引擎的作用,深色为执行器的作用。

2.4 两阶段提交:保证数据两个日志一致性

过程:先提交redolog日志(暂时不上传),再提交binlog日志,两个日志齐备后自动触发commit,上传redolog日志。(相当于在这里多了一个事务的操作,两个日志同时成功或者同时失败)

错误情况:先写redolog和先写binlog日志都会发生数据不一致的错误。

2.5 问题回答

疑问一:前面我说到定期全量备份的周期“取决于系统重要性,有的是一天一备,有的是一周一备”。那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?

回答一:主要看对数据丢失的重要性程度;内容的更新频率,是否是增删改操作多还是查询操作多!

疑问二:如果使用的是InnoDB搜索引擎,是不是可以不要binlog了?

回答二不可以,因为redolog日志是循环写, 修改一部分就会写进磁盘里,不能够恢复任意一个时段的数据情况,只有不存在容量大小限制的binlog日志能够。redolog日志填补了一个crash-safe功能。

大神对本章的见解:

  1. redo是物理的,binlog是逻辑的;现在由于redo是属于InnoDB引擎,所以必须要有binlog,因为你可以使用别的引擎
  2. 保证数据库的一致性,必须要保证2份日志一致,使用的2阶段式提交;其实感觉像事务,不是成功就是失败,不能让中间环节出现,也就是一个成功,一个失败
  3. 如果有一天mysql只有InnoDB引擎了,有redo来实现复制,那么感觉oracle的DG就诞生了,物理的速度也将远超逻辑的,毕竟只记录了改动向量
  4. binlog几大模式,一般采用row,因为遇到时间,从库可能会出现不一致的情况,但是row更新前后都有,会导致日志变大
  5. 最后2个参数,保证事务成功,日志必须落盘,这样,数据库crash后,就不会丢失某个事务的数据了
  6. 备份时间周期的长短,感觉有2个方便
    1)恢复数据丢失的时间,既然需要恢复,肯定是数据丢失了。如果一天一备份的话,只要找到这天的全备,加入这天某段时间的binlog来恢复,如果一周一备份,假设是周一,而你要恢复的数据是周日某个时间点,那就,需要全备+周一到周日某个时间点的全部binlog用来恢复,时间相比前者需要增加很多;看业务能忍受的程度
    2)数据库丢失,如果一周一备份的话,需要确保整个一周的binlog都完好无损,否则将无法恢复;而一天一备,只要保证这天的binlog都完好无损;当然这个可以通过校验,或者冗余等技术来实现,相比之下,上面那点更重要

3. 讲事务隔离:为什么你改了我还看不见

事务是在引擎层实现功能的,且InnoDB引擎才具有,MyISAM不具备。
MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务(MyISAM不支持)

3.1 隔离性与隔离级别

3.1.1 事务的四大特征:原子性,一致性,持久性,隔离性。

1) 原子性:事务内的操作,要么全部成功,要么全部失败
2)一致性:我们不追求整个操作过程中每一时刻的一致性(强一致性),转而追求最终结果的一致性(最终一致性)。
3)持久性:修改后的数据最终会保存到磁盘中,永久改变。
4)隔离性:事务之间是相互隔离的,互不影响,影响程度通过隔离级别控制。

3.1.2 事务的隔离级别:读取未提交,读取已提交,可重复度,可串行化

读取未提交:事务之间可以读取未提交的信息,会出现脏读(即一些临时的数据,不需要去读)
读取已提交:只能读取事务提交了的数据,但是会出现幻读(对同一个数据进行两次读,如果在中间过程中,其他事务对数据修改了,会造成数据的不一致,像幻觉一样)
可重复读:一个事务,如果对相同数据进行读操作,数据会保持一直,及时在事务中间,其他事务已经改变了数据,
可串行化:一个事务执行过程中会进行上锁(写会加写锁,读会加读锁),当事务提交后才会解锁,下一个事务再重头执行。

3.1.2 事务四大隔离级别的功能实例

  1. 建表,表中只有一列数据
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
  1. 两个事务同时执行操作
    项目 | Value
    -------- | -----
    电脑 | $1600
    手机 | $12
    导管 | $1
事务一 事务二
启动事务查询得到值1 启动事务
查询得到值1
将1改成2
查询得到值V1
提交事务
查询得到值V2
提交事务
查询得到值V3
  1. 不同事务隔离方式的结果
隔离方式 V1 V3 V3
读取未提交 2 2 2
读取已提交 1 2 2
可重复度 1 1 2
可串行化 2 2 2

数据库为MySQL时,默认的隔离级别为可重复度
数据库为Oracle时,默认的隔离级别为读取已提交
总结:没有最好的隔离级别,只有最合适的隔离级别,具体问题具体分析。

3.2 事务隔离的实现

3.2.1 视图实现隔离

事务的隔离是通过视图实现的:每个视图对应一张独立的表格,可以进行数据的增删改查。
读取未提交:没有视图的概念
读取已提交:在每个SQL执行开始的时候创建
可重复读:在事务开启的时候创建(贯穿整个事务过程,用一个视图显示)

3.2.2 可重复读是如何解决幻读问题的?

通过MVCC(多版本并发控制):每一个数据后面有两个隐藏列(版本号和过期号,同一个版本下的数据会形成条链子,不同版本下的多事务能够并发执行)多一条数据在系统中存在多个版本,即多版本并发控制。
轻松理解:多版本并发控制MVCC

3.2.3 三个日志存储历史数据

  1. Bin Log:服务层的日志,常用来进行数据恢复,数据库的复制,还是逻辑层的数据存储,不会保留过程
  2. Redo Log:记录数据在物理层的操作,即更改了那个数据,更改了几次,怎么更改的,保留数据修改的全过程
  3. Undo Log:用于数据的撤回操作,即回滚操作:插入对应删除;修改对应修改为原来的数据。

3.3 事务的启动方式

  1. 显式启动事务:事务开启(begin或者start transaction),事务提交(commit);事务回滚(rollback)
  2. set autocommit=0,该命令会把这个线程的自动提交关掉。这样只要执行一个select语句,事务就启动,并不会自动提交,直到主动执行commit或rollback或断开连接。
  3. 你可以在information_schema库的innodb_trx这个表中查询长事务,比如下面这个语句,用于查找持续时间超过60s的事务。
select * from information_schema.innodb_trx where 
TIME_TO_SEC(timediff(now(),trx_started))>60

3.4 疑惑解答

  1. 回滚日志(redo log)什么时候会被删除?
    当前系统判断没有事务需要使用该回滚日志的时候,会进行自动的删除。
  2. 为什么建议你尽量不要使用长事务
    因为如果使用长事务,redo log(回滚信息)中会存储大量的修改信息,不能够得到删除,导致占用内存,减低执行的效率。
  3. 如何减小长事务:打开线程自动提交,set autocommit=1,即使用第一种线程启动方式(存在每次事务执行需要begin一次,可使用语句commit work and chain语法,提交上一个事务,并且自动开启下一个事务)。

3.5 思考题

  1. 疑问:如果你是业务开发负责人同时也是数据库负责人,你会有什么方案来避免出现或者处理长事务这种情况呢?
  2. 解答:使用第一种显式的事务启动方法;缩小事务的范围;如果无法避免,那么久要保证逻辑回滚日志空间足够,并且实时监测Innodb_trx表,发现长事务进行报警。

参考文献

  1. 极客时间:MySQL实战45讲

猜你喜欢

转载自blog.csdn.net/qq_42974034/article/details/128687938