【MySQL 原理篇】- 凭这个,我拿下字节面试

 若是想查看原图,请点击这里 刘卡卡 | ProcessOn

超链接  

  • 索引
    • 从存储结构上看,有哪些索引
    • 从存储结构上来划分:BTree索引(B-Tree或B+Tree索引),Hash索引,full-index全文索引,R-Tree索引。这里所描述的是索引存储时保存的形式,
    • B+
    • 超链接 在一棵 B+ 树中,每个节点都是一个页,每次新建节点的时候,就会申请一个页空间。同一层上的节点之间,通过页的结构构成一个双向的链表(页文件头中的两个指针字段)。非叶子节点,包括了多个索引行,每个索引行里存储索引键和指向下一层页面的页面指针。最后是叶子节点,它存储了关键字和行记录,在节点内部(也就是页结构的内部)记录之间是一个单向的链表,但是对记录进行查找,则可以通过页目录采用二分查找的方式来进行。
      • 为什么用B+树,而不是B树?B+树原理?
      • 首先,B+ 树查询效率更稳定。因为 B+ 树每次只有访问到叶子节点才能找到对应的数据,而在 B 树中,非叶子节点也会存储数据,这样就会造成查询效率不稳定的情况,有时候访问到了非叶子节点就可以找到关键字,而有时需要访问到叶子节点才能找到关键字。其次,B+ 树的查询效率更高,这是因为通常 B+ 树比 B 树更矮胖(阶数更大,深度更低),查询所需要的磁盘 I/O 也会更少。同样的磁盘页大小,B+ 树可以存储更多的节点关键字。不仅是对单个关键字的查询上.在查询范围上,B+ 树的效率也比 B 树高。这是因为所有关键字都出现在 B+ 树的叶子节点中,并通过有序链表进行了链接。而在 B 树中则需要通过中序遍历才能完成查询范围的查找,效率要低很多。
        • Hash索引|和B+树的区别?
        • MySQL为什么要使用B+树,而不是使用红黑树,跳表?
      • 效率/磁盘IO
      • B+树索引的本质是B+树在数据库中的实现。但是B+树索引有一个特点是高扇出性,因此在数据库中,B+树的高度一般在2到3层。也就是说查找某一键值的记录,最多只需要2到3次IO开销。按磁盘每秒100次IO来计算,查询时间只需0.02到0.03秒。
      • 一些问题
        • 如果加索引的字段比较大,会影响树的高度吗?
        • 【会】InnoDB存储引擎默认一个数据页大小为16kb,非叶子节点存放(key,pointer),pointer为6个字节,key为4个字节,即非叶子节点能存放16kb/14左右的key,pointer,而叶子节点如果一条数据大小为100字节,那一个叶子节点大约可存放160条数据。如果高度为3,则可存放数据为:16kb/14 * 16kb/14 * 160大约1亿多数据。因此InnoDB存储引擎b+树的高度基本为2-3.
    • Hash索引
  • 集群
    • 复制
      • 主从复制的方式
      • 其实就是一个分布式事务的处理
        • 异步复制(默认)
        • 超链接  一个事务日志同步的完整过程是这样的:1、在备库 B 上通过 change master 命令,设置主库 A IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。2、在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread sql_thread。其中 io_thread 负责与主库建立连接。3、主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B4、备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。5sql_thread 读取中转日志,解析出日志里的命令,并执行。
          • 分支主题
        • 增强半同步复制
        • * 该方法当主库写数据到BINLOG后,就开始等待从库的应答ACK,直到至少一个从库写入Relay Log后,并将数据落盘,然后返回给主库消息,通知主库可以执行Commit操作,然后主库开始提交到事务引擎层,应用此时可以看到数据发生了变化 *  当然,如果从库宕机或者通信超时,MySQL会自动调整为异步模式,事务正常提交并返回结果给客户端 *  因此,该方式当主从库之间的网络情况越好,从库就越实时 *  当主库每提交一个事务后,不会立即返回,而是等待其中一个从库接收到Binlog并成功写入Relay-log中才返回客户端,所以这样就保证了一个事务至少有两份日志,一份保存在主库的Binlog,另一份保存在其中一个从库的Relay-log中,从而保证了数据的安全性和一致性。
        • 全同步复制
        • 指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
  • 事务
    • 什么是数据库的事务?
    • 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
    • 事务的四大特性是什么?
    • ACID事务就是一组原子性的操作,这些操作要么全部发生,要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。
    • 原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做一致性。
    • 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
    • 隔离性。一个事务的执行不能其它事务干扰。即一个事务内部的//操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
    • 持久性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
    • 数据库的并发一致性问题?
    • 并发事务带来哪些问题:
    • 1、脏读:事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据。当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
    • 2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果 不一致。
    • 3、幻读:系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
    • 不可重复读侧重于修改,幻读侧重于新增或删除(多了或少量行),脏读是一个事务回滚影响另外一个事务。
    • 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
    • 4、数据更新丢失。指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。
      • 脏读
        • 怎么解决的?
      • 不可重复读
      • 【怎么解决的】可重复读隔离级别下就可以,一个事务只在第一次 SELECT 的时候会获取一次 Read View,而后面所有的 SELECT 都会复用这个 Read View
      • 幻读
        • 怎么解决的?
        • 在可重复读的情况下,InnoDB 可以通过 Next-Key 锁 +MVCC 来解决幻读问题。
    • 数据库的隔离级别有哪些?
    • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到
    • 读已提交:一个事务提交之后,它做的变更才会被其他事务看到
    • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
    • 串行化:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
      • 读未提交
        • 读提交
        • 在事务 A 读取数据时增加了共享锁,一旦读取,立即释放锁,事务 B 读取修改数据时增加了行级排他锁,直到事务结束才释放锁。也就是说,事务 A 在读取数据时,事务 B 只能读取数据,不能修改。当事务 A 读取到数据后,事务 B 才能修改。这种隔离级别,可以避免脏读,但依然存在不可重复读以及幻读的问题。(不可重复读:当事务A第二次读数据时,读到的可能是B修改后的数据)
          • 可重复读
            • 串行读
      • 隔离级别是如何实现的?
      • 未提交读(Read Uncommitted):在事务 A 读取数据时,事务 B 读取数据加了共享锁,修改数据时加了排它锁。这种隔离级别,会导致脏读、不可重复读以及幻读。 【已提交读(Read Committed)】在事务 A 读取数据时增加了共享锁,一旦读取,立即释放锁。事务 B 读取修改数据时增加了行级排他锁,直到事务结束才释放锁。也就是说,事务 A 在读取数据时,事务 B 只能读取数据,不能修改。当事务 A 读取到数据后,事务 B 才能修改。这种隔离级别,可以避免脏读,但依然存在不可重复读以及幻读的问题。(不可重复读:当事务A第二次读数据时,读到的可能是B修改后的数据) 【可重复读(Repeatable Read)】在事务 A 读取数据时增加了共享锁,事务结束,才释放锁,事务 B 修改数据时增加了行级排他锁,直到事务结束才释放锁。也就是说,事务 A 在没有结束事务时,事务 B 只能读取数据,不能修改。当事务 A 结束事务,事务 B 才能修改。这种隔离级别,可以避免脏读、不可重复读,但依然存在幻读的问题。(幻读:A在事务过程中,添加了一两行记录时,B可能) 可序列化(Serializable):在事务 A 读取数据时增加了共享锁,事务结束,才释放锁,事务 B 读取修改数据时增加了表级排他锁,直到事务结束才释放锁。可序列化解决了脏读、不可重复读、幻读等问题,但隔离级别越来越高的同时,并发性会越来越低。 但这种通过锁方式实现的事务,性能不高(一旦数据被加上排他锁,其他事务将无法加入共享锁,且处于阻塞等待状态,如果一张表有大量的请求,这样的性能将是无法支持的。)InnoDB 中的 RC 和 RR 隔离事务是基于多版本并发控制(MVCC)实现高性能事务。
        • 数据库的锁与隔离级别的关系?
    • 原理
      • 事务回滚的实现原理
      • 事务是基于重做日志文件(redo log)和回滚日志(undo log)实现的。每提交一个事务必须先将该事务的所有日志写入到重做日志文件进行持久化,数据库就可以通过重做日志来保证事务的原子性和持久性。每当有修改事务时,还会产生 undo log,如果需要回滚,则根据 undo log 的反向语句进行逻辑操作,比如 insert 一条记录就 delete 一条记录。undo log 主要实现数据库的一致性。
      • 事务日志
      • I     nnodb 事务日志包括 redo log 和 undo log。undo log 指事务开始之前,在操作任何数据之前,首先将需操作的数据备份到一个地方。redo log 指事务中操作的任何数据,将最新的数据备份到一个地方。事务日志的目的:实例或者介质失败,事务日志文件就能派上用场。
        • 两阶段提交
      • 什么是MVCC?当前读?一致性读?
    • 快照?
      • Read View
      • 当前活跃的事务 ID集合
    • 在事务中可以混合使用存储引擎么?
    • 一些问题
      • 你在命令行输入一个sql语句,执行到一半的时候Ctrl+C,这个时候数据库是一个什么样的状态?
  • 执行流程
    • MySQL执行查询的过程?
    • MySQL执行更新的过程
    • 日志
      • redo log
      • 重做日志用来记录数据页上进行了什么修改在 MySQL 中,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就采用了日志(redo log)来提升更新效率而日志和磁盘配合的整个过程,其实就是 MySQL 里的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。在同一个事务中,每当数据库进行修改数据操作时,将修改结果更新到内存后,会在redo log添加一行记录记录“需要在哪个数据页上做什么修改”,并将该记录状态置为prepare,等到commit提交事务后,会将此次事务中在redo log添加的记录的状态都置为commit状态,之后在适当的时候(如系统空闲时)将修改落盘时,会将redo log中状态为commit的记录的修改都写入磁盘【循环写】redolog采用循环写的方式记录,当写到结尾时,会回到开头循环写日志redolog的大小是固定的,在mysql中可以通过修改配置参数innodb_log_files_in_group和innodb_log_file_size配置日志文件数量和每个日志文件大小参数innodb_log_file_size:指定每个redo日志大小,默认值48MBinnodb_log_files_in_group:指定日志文件组中redo日志文件数量,默认为2innodb_log_group_home_dir:指定日志文件组所在路劲,默认值./,指mysql的数据目录datadirinnodb_mirrored_log_groups:指定日志镜像文件组的数量,默认为1,此功能属于未实现的功能,在5.6版本中废弃,在5.7版本中删除了。redo log也是需要写入磁盘的,但是它是顺序IO,比起直接将内存的脏页写到磁盘的随机IO要快很多write pos表示日志当前记录的位置,当ib_logfile_4写满后,会从ib_logfile_1从头开始记录;check point表示将日志记录的修改写进磁盘,完成数据落盘,数据落盘后checkpoint会将日志上的相关记录擦除掉,即write pos->checkpoint之间的部分是redo log空着的部分,用于记录新的记录,checkpoint->write pos之间是redo log待落盘的数据修改记录。当writepos追上checkpoint时,得先停下记录,先推动checkpoint向前移动,空出位置记录新的日志。有了redo log,当数据库发生宕机重启后,可通过redo log将未落盘的数据恢复,即保证已经提交的事务记录不会丢失。特点redo log的大小是固定的,日志上的记录修改落盘后,日志会被覆盖掉,无法用于数据回滚/数据恢复等操作。redo log是innodb引擎层实现的,并不是所有引擎都有。重做日志都是以512字节进行存储的,称之为重做日志块,与磁盘扇区大小一致,这意味着重做日志的写入可以保证原子性,不需要doublewrite技术innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数建议设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
        • 3种状态
        • 三种状态分别是:
        • 1、存在 redo log buffer 中,物理上是在 MySQL 进程内存中,就是图中的红色部分;
        • 2、写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里面,也就是图中的黄色部分;
        • 3、持久化(fasync)到磁盘,对应的是 hard disk,也就是图中的绿色部分。日志写到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盘的速度就慢多了。
          •  redo log 的写入策略
          • 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。
        • 什么时候会写入
        • 超链接 1、事务提交时2master thread 线程每秒一次的轮询操作3redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。4、并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。
      • bin log
      • 特点binlog记录了数据库表结构和表数据变更binlog是server层实现的,意味着所有引擎都可以使用binlog日志binlog通过追加的方式写入的,可通过配置参数max_binlog_size设置每个binlog文件的大小,当文件大小大于给定值后,日志会发生滚动,之后的日志记录到新的文件上。binlog有两种记录模式,statement格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。备注: 每个事务 binlog 的末尾,会记录一个 XID event,标志着事务是否提交成功,也就是说,recovery 过程中,binlog 最后一个 XID event 之后的内容都应该被 放弃。sync_binlog: 设置为1,表示每次事务的binlog都直接持久化到磁盘(注意是这里指的是binlog日志本身落盘),保证mysql重启后binlog记录是完整的。show variables like 'sync_binlog';对于事务引擎,每次事务提交的时候,都会写binlog对于非事务引擎,每条SQL语句都会写到binlog作用数据复制在主服务器和从服务器间进行数据复制,保持数据的一致性数据恢复数据库的数据出问题以后,可以通过binlog来对数据进行恢复
        • 工作原理
        • 超链接 事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。每个线程有自己 binlog cache,但是共用同一份 binlog 文件。图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。图中的 fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为 fsync 才占磁盘的 IOPSwrite fsync 的时机,是由参数 sync_binlog 控制的:sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsyncsync_binlog=1 的时候,表示每次提交事务都会执行 fsyncsync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync
          • 三种格式
          • 录入格式:statement用来记录执行的语句,该模式的话对于函数无法保证数据一致性,如now函数row用来记录发生改变的行以及对应的改变,但是存在效率问题,可能一条SQL能设置完的数据,需要一条一条遍历整个表才能做完mixed综合了上述两者,没有函数时,使用statement,有函数时使用row,但是对于系统变量仍然会出现主从不一致
          • redo log 和 binlog 是怎么关联起来的?
          • 双一
        • binlog可以简化掉吗?
        • binlog可以简化掉吗?这里需要分场景来看:1. 1. 如果是主从模式下,binlog是必须的,因为从库的数据同步依赖的就是binlog;2. 2. 如果是单机模式,并且不考虑数据库基于时间点的还原,binlog就不是必须,因为有redo log就可以保证crash-safe能力了;但如果万一需要回滚到某个时间点的状态,这时候就无能为力,所以建议binlog还是一直开启;
        • 和 redo log区别
        • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。redo log 是物理日志,记录的是在某个数据页上做了什么修改;binlog 是逻辑日志,记录的是这个语句的原始逻辑。redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。追加写是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。redo log事务开始的时候,就开始记录每次的变更信息,而binlog是在事务提交的时候才记录。MySQL需要保证redo log和binlog的数据是一致的,如果不一致,主从服务器间的数据就会不一致了
    • 为什么要加锁
    • 当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。保证多用户环境下保证数据库完整性和一致性。
    • 数据库粒度锁
      • 概览
        • 分支主题
      • 全局锁、表级别锁(表锁、元数据锁(MDL))?
      • 表锁MDL
        • 表锁
        • 表锁的语法是 lock tables … read/write。与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。
        • MDL
        • MDL 不需要显式使用,在访问一个表的时候会被自动加上。MDL 的作用是,保证读写的正确性当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。
      • 表锁 行锁 都有这些锁
      • ①在mysql中有表锁,LOCK TABLE my_tabl_name READ;  用读锁锁表,会阻塞其他事务修改表数据。LOCK TABLE my_table_name WRITE; 用写锁锁表,会阻塞其他事务读和写。②Innodb引擎又支持行锁,行锁分为共享锁,一个事务对一行的共享只读锁。排它锁,一个事务对一行的排他读写锁。
      • 行锁
        • 行锁算法
          • 间隙锁?
            • next-key lock
        • 行锁是怎么实现的
        • InnoDB是基于索引来完成行锁例: select * from tab_with_index where id = 1 for update;for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起
    • 数据库管理锁
      • 共享锁 排他锁 意向锁
      • 从锁的类别上来讲,有共享锁和排他锁。共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。
      • 意向锁有什么用?
      • 事务A锁住了表中的一行,让这一行只能读,不能写。之后,事务B申请整个表的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。数据库要怎么判断这个冲突呢?step1:判断表是否已被其他事务用表锁锁表step2:判断表中的每一行是否已被行锁锁住。注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。在意向锁存在的情况下,上面的判断可以改成step1:不变step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。
      • 几种典型语句的加(释放)锁DML流程
      • 1.select语句操作MDL锁流程   1)Opening tables阶段,加共享锁     a)   加MDL_INTENTION_EXCLUSIVE锁     b)   加MDL_SHARED_READ锁   2)事务提交阶段,释放MDL锁     a)   释放MDL_INTENTION_EXCLUSIVE锁     b)   释放MDL_SHARED_READ锁2. DML语句操作MDL锁流程\  1)Opening tables阶段,加共享锁     a)   加MDL_INTENTION_EXCLUSIVE锁     b)   加MDL_SHARED_WRITE锁  2)事务提交阶段,释放MDL锁     a)   释放MDL_INTENTION_EXCLUSIVE锁     b)   释放MDL_SHARED_WRITE锁3. alter操作MDL锁流程  1)Opening tables阶段,加共享锁     a)   加MDL_INTENTION_EXCLUSIVE锁     b)   加MDL_SHARED_UPGRADABLE锁,升级到MDL_SHARED_NO_WRITE锁  2)操作数据,copy data,流程如下:     a)   创建临时表tmp,重定义tmp为修改后的表结构     b)   从原表读取数据插入到tmp表  3)将MDL_SHARED_NO_WRITE读锁升级到MDL_EXCLUSIVE锁     a)   删除原表,将tmp重命名为原表名  4)事务提交阶段,释放MDL锁     a)   释放MDL_INTENTION_EXCLUSIVE锁     b)   释放MDL_EXCLUSIVE锁
    • 死锁
      • 什么是死锁?如何避免?如何检测死锁?
      • 死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。常见的解决死锁的方法1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;如果业务处理不好可以用分布式事务锁或者使用乐观锁
    • 数据库加锁规则
    • 超链接 【可重复读隔离级别下】原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。原则 2:查找过程中访问到的对象才会加锁。优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。可重复读隔离级别遵守两阶段锁协议,所有加锁的资源,都是在事务提交或者回滚的时候才释放的
      • 两阶段锁
      • 在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
  • 表/数据页
    • 整体
    • 一个表空间包括了一个或多个段,一个段包括了一个或多个区,一个区包括了多个页,而一个页中可以有多行记录,这些概念我简单给你讲解下。区(Extent)是比页大一级的存储结构,在 InnoDB 存储引擎中,一个区会分配 64 个连续的页。因为 InnoDB 中的页大小默认是 16KB,所以一个区的大小是 64*16KB=1MB。段(Segment)由一个或多个区组成,区在文件系统是一个连续分配的空间(在 InnoDB 中是连续的 64 个页),不过在段中不要求区与区之间是相邻的。段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存在。当我们创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段。
    • 数据页     
      • 超链接 概览
        • 分支主题
      • 页结构
      • 。数据页包括七个部分,分别是文件头(File Header)、页头(Page Header)、最大最小记录(Infimum+supremum)、用户记录(User Records)、空闲空间(Free Space)、页目录(Page Directory)和文件尾(File Tailer)1、首先是文件通用部分,也就是文件头和文件尾。它们类似集装箱,将页的内容进行封装,通过文件头和文件尾校验的方式来确保页的传输是完整的。2、
        • 文件头和文件尾
        • 先是文件通用部分,也就是文件头和文件尾。它们类似集装箱,将页的内容进行封装,通过文件头和文件尾校验的方式来确保页的传输是完整的。1、在文件头中有两个字段,分别是 FIL_PAGE_PREV 和 FIL_PAGE_NEXT,它们的作用相当于指针,分别指向上一个数据页和下一个数据页。连接起来的页相当于一个双向的链表,如下图所示:需要说明的是采用链表的结构让数据页之间不需要是物理上的连续,而是逻辑上的连续。2、我们之前讲到过 Hash 算法,这里文件尾的校验方式就是采用 Hash 算法进行校验。举个例子,当我们进行页传输的时候,如果突然断电了,造成了该页传输的不完整,这时通过文件尾的校验和(checksum 值)与文件头的校验和做比对,如果两个值不相等则证明页的传输有问题,需要重新进行传输,否则认为页的传输已经完成。
          • 记录部分
          • 页的主要作用是存储记录,所以“最小和最大记录”和“用户记录”部分占了页结构的主要空间。另外空闲空间是个灵活的部分,当有新的记录插入时,会从空闲空间中进行分配用于存储新记录
            • 索引部分
            • 这部分重点指的是页目录,它起到了记录的索引作用,因为在页中,记录是以单向链表的形式进行存储的。单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索,因此在页目录中提供了二分查找的方式,用来提高记录的检索效率。
              • 分支主题
      • 页分类
      • 有数据页(保存 B+ 树节点)、系统页、Undo 页和事务数据页等。数据页是我们最常使用的页
  • 关键机制
    • MVCC
      • 超链接 一些问题
        • Mvcc使用到回滚段,如果一个事务存在,那回滚日志就不能删除,所以就会特别大,MySQL使用了什么优化策略吗?
    • change buffer
      • 是什么?
      • 当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。需要说明的是,虽然名字叫作 change buffer,实际上它是可以持久化的数据。也就是说,change buffer 在内存中有拷贝,也会被写入到磁盘上。将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭(shutdown)的过程中,也会执行 merge 操作。显然,如果能够将更新操作先记录在 change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用 buffer pool 的,所以这种方式还能够避免占用内存,提高内存利用率。
        • 什么条件下可以使用 change buffer 呢
        • 对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用
    • WAL机制
    • WAL技术,也就是先写日志,再写磁盘。当内存数据页跟磁盘数据页内容不一致的时候,我们成这个内存页为“脏页”。内存数据写入磁盘后,内存和磁盘上的数据页内容就一致了,称为“干净页”。MySQL 从 内存更新到磁盘的过程,称为刷脏页的过程(flush)作者:如梦又似幻链接:https://www.jianshu.com/p/f242bc1e95ff来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
      • BufferPool
        • 整体结构图
          • 子主题 1
        • 数据页加载的三种方式
        • 超链接 如果缓冲池中没有该页数据,那么缓冲池有以下三种读取数据的方式,每种方式的读取效率都是不同的【内存读取】如果该数据存在于内存中,基本上执行时间在 1ms 左右,效率还是很高的【随机读取】如果数据没有在内存中,就需要在磁盘上对该页进行查找,整体时间预估在 10ms 左右,这 10ms 中有 6ms 是磁盘的实际繁忙时间(包括了寻道和半圈旋转时间),有 3ms 是对可能发生的排队时间的估计值,另外还有 1ms 的传输时间,将页从磁盘服务器缓冲区传输到数据库缓冲区中。【顺序读取】顺序读取其实是一种批量读取的方式,因为我们请求的数据在磁盘上往往都是相邻存储的,顺序读取可以帮我们批量读取页面,这样的话,一次性加载到缓冲池中就不需要再对其他页面单独进行磁盘 I/O 操作了。如果一个磁盘的吞吐量是 40MB/S,那么对于一个 16KB 大小的页来说,一次可以顺序读取 256040MB/16KB)个页,相当于一个页的读取时间为 0.4ms。采用批量读取的方式,即使是从磁盘上进行读取,效率也比从内存中只单独读取一个页的效率要高。
      • checkPoint 刷脏页
        • 怎么做的?
          • 什么时候会刷脏页
          • 1、对应的就是 InnoDB 的 redo log 写满了。这时候系统会停止所有更新操作,把 checkpoint 往前推进,redo log 留出空间可以继续写2、对应的就是系统内存不足。当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘。3、 MySQL 认为系统“空闲”的时候4、对应的就是 MySQL 正常关闭的情况。这时候,MySQL 会把内存的脏页都 flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据,启动速度会很快。
            • 刷脏页的控制策略
    • crash safe怎么保证的?
      • 超链接  两阶段提交
      • 从上面可以看出,因为redo log影响主库的数据,binlog影响从库的数据,所以redo log和binlog必须保持一致才能保证主从数据一致,这是前提。相信很多有过开发经验的同学都知道分布式事务,这里的redo log和binlog其实就是很典型的分布式事务场景,因为两者本身就是两个独立的个体,要想保持一致,就必须使用分布式事务的解决方案来处理。而将redo log分成了两步,其实就是使用了两阶段提交协议(Two-phase Commit,2PC)。
        • 多事务情况下的问题
        • 两阶段提交虽然能够保证单事务两个日志的内容一致,但在多事务的情况下,却不能保证两者的提交顺序一致T1 (--prepare--binlog---------------------commit)T2 (-----prepare-----binlog----commit)T3 (--------prepare-------binlog------commit)解析:    redo log prepare的顺序:T1 --》T2 --》T3    binlog的写入顺序:T1 --》 T2 --》T3    redo log commit的顺序:T2 --》 T3 --》T1结论:由于binlog写入的顺序和redo log提交结束的顺序不一致,导致binlog和redo log所记录的事务提交结束的顺序不一样,最终导致的结果就是主从数据不一致。因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。
      • 组提交
        • 针对通过在两阶段提交中加锁控制事务提交顺序这种实现方式遇到的性能瓶颈问题,有没有更好的解决方案呢?
      • 数据恢复
      •      
        • 数据恢复流程
          • 分支主题
        • 怎么进行数据恢复
        • binlog 会记录所有的逻辑操作,并且是采用追加写的形式。当需要恢复到指定的某一秒时,比如今天下午二点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:首先,找到最近的一次全量备份,从这个备份恢复到临时库然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。
        • 数据库崩溃恢复,是从 redo log 更新过来的还是从 buffer pool 更新过来的呢?
        • 作者:星空之座2021链接:https://www.nowcoder.com/discuss/816108来源:牛客网实际上,redo log 并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在由 redo log 更新过去数据最终落盘的情况。数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程与 redo log 毫无关系。在崩溃恢复场景中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让 redo log 更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。checkpoint->write pos之间是redo log待落盘的数据修改记录,因此崩溃恢复就是将这部分的数据页读取到内存中,然后使用redo日志更新内存内容,最终将脏页落盘。
    • LRU原理

超链接 
分支主题


子主题 1


分支主题


分支主题


分支主题


分支主题


分支主题


分支主题


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值