用户山西人在绥远 發表於 2025-8-14 09:00:00

MySQL 中常见的日志有哪些?是如何实现事务的?

<h2 id="mysql-中常见的日志有哪些">MySQL 中常见的日志有哪些?</h2>
<p>MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 <code>bin log</code>(二进制日志)和 <code>redo log</code>(重做日志)和 <code>undo log</code>(回滚日志)。</p>
<p><strong>bin log</strong></p>
<p><code>bin log</code>是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库。</p>
<p><strong>redo log</strong></p>
<p><code>redo log</code>是innodb引擎级别,用来记录innodb存储引擎的事务日志,不管事务是否提交都会记录下来,用于数据恢复。当数据库发生故障,innoDB存储引擎会使用<code>redo log</code>恢复到发生故障前的时刻,以此来保证数据的完整性。将参数<code>innodb_flush_log_at_tx_commit</code>设置为1,那么在执行commit时会将<code>redo log</code>同步写到磁盘。</p>
<p><strong>undo log</strong></p>
<p>除了记录<code>redo log</code>外,当进行数据修改时还会记录<code>undo log</code>,<code>undo log</code>用于数据的撤回操作,它保留了记录修改前的内容。通过<code>undo log</code>可以实现事务回滚,并且可以根据<code>undo log</code>回溯到某个特定的版本的数据,<strong>实现MVCC</strong>。</p>
<h2 id="bin-log和redo-log有什么区别">bin log和redo log有什么区别?</h2>
<ol>
<li><code>bin log</code>会记录所有日志记录,包括InnoDB、MyISAM等存储引擎的日志;<code>redo log</code>只记录innoDB自身的事务日志。</li>
<li><code>bin log</code>只在事务提交前写入到磁盘,一个事务只写一次;而在事务进行过程,会有<code>redo log</code>不断写入磁盘。</li>
<li><code>bin log</code>是逻辑日志,记录的是SQL语句的原始逻辑;<code>redo log</code>是物理日志,记录的是在某个数据页上做了什么修改。</li>
</ol>
<h2 id="bin-log-有哪些日志类型">bin log 有哪些日志类型?</h2>
<p>binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:</p>
<ul>
<li>STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,所以针对这种格式, binlog 可以称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;</li>
<li>ROW:记录行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;</li>
<li>MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;</li>
</ul>
<blockquote>
<p>注意:不同的日志类型在主从复制下除了有动态函数的问题,同样对对更新时间也有影响。一般来说,数据库中的update_time都会设置成ON UPDATE CURRENT_TIMESTAMP,即自动更新时间戳列。在主从复制下:<br>
如果日志格式类型是STATEMENT,由于记录的是sql语句,在salve端是进行语句重放,那么更新时间也是重放时的时间,此时slave会有时间延迟的问题;<br>
如果日志格式类型是ROW,这是记录行数据最终被修改成什么样了,这种从库的数据是与主服务器完全一致的。</p>
</blockquote>
<h2 id="什么是-write-ahead-logging-wal技术它的优点是什么mysql-中是否用到了-wal">什么是 Write-Ahead Logging (WAL)技术?它的优点是什么?MySQL 中是否用到了 WAL?</h2>
<p>WAL(Wite-Ahead Logging)技术是一种数据库事务日志管理技术,它确保在修改真正的数据之前,先将修改记录写入日志,这使得即使系统崩溃,通过日志也能恢复教据,保证了数据的特久性和一致性</p>
<p>WAL它的核心思想就是先写日志,再写数据,大致执行流程如下:</p>
<ol>
<li>当一个事务开始时,所有对数据库的修改都会先记录到一个日志文件中,而不是直接应用到数据库文件,这些日志记录了数据的变更信息,可以用于恢复数据.。</li>
<li>当日志记录被安全地写入磁盘后,才会将这些修改应用到数据库文件中。</li>
</ol>
<p>在 MySQL InnoDB 存储引擎中,重做日志 (Redo Log)就是 WAL的实现,用于保证事务的持久性和崩溃恢复能力。InnoDB 重做日志的工作机制如下:</p>
<ol>
<li>当一个事务开始时,所有对数据库的修改首先记录到重做日志缓冲区中。</li>
<li>重做日志缓冲区的数据会周期性地刷新到磁盘上的重做日志文件(ib_logfile0和ib_logfile1)。</li>
<li>当事务提交时,InnoDB 确保重做日志已写入磁盘,然后将数据页的修改写入数据文件。</li>
<li>如果系统崩溃,InnoDB 会在启动时通过重做日志重新应用所有未完成的事务,以恢复数据库到一致状态。</li>
</ol>
<h2 id="mysql插入一条-sql-语句redo-log-记录的是什么">MySQL插入一条 SQL 语句,redo log 记录的是什么?</h2>
<p>因为 redo log 是物理日志,记录“某页(Page)某位置的数据被修改为某值”。它不记录逻辑操作(如“插入一行”,而是直接记录对页的变更。所以在插入操作中,redolog 记录的是事务在数据页上的修改<br>
数据页的插入点、记录的偏移量和插入的实际数据并更新页目录、页头等元数据</p>
<p>插入操作 redo log 具体执行流程</p>
<ol>
<li>修改缓冲页(Buffer Pool):数据先写入内存中的缓冲池,而不是直接写入磁盘.</li>
<li>生成 redo log:同时生成一条 redo log,记录插入对数据页的物理修改细节。</li>
<li>日志先行(Write-Ahead Logging, WAL):redo log 先被写入磁盘上的 redo log 文件。</li>
</ol>
<h2 id="mysql的binlog有几种格式分别有什么区别">MySQL的binlog有几种格式?分别有什么区别?</h2>
<p>有三种格式,statement,row和mixed。</p>
<ul>
<li>statement:每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。</li>
<li>row:不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大。</li>
<li>mixed:一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。</li>
</ul>
<h2 id="undo-log-是如何保证事务的原子性的">undo log 是如何保证事务的原子性的?</h2>
<p>通过使用 undo log,数据库系统可以记录事务过程中每次数据修改之前的旧值。当事务失败或被中止时,数据库可以利用这些记录将数据恢复到事务开始之前的状态,从而保证事务的原子性。</p>
<p>Undo log 的这种机制确保了即使在事务执行过程中出现错误,数据库仍然能够保持一致性和原子性。</p>
<h2 id="慢查询日志有什么用">慢查询日志有什么用?</h2>
<p>慢查询日志用于捕捉和记录执行时间超过指定阈值的 SQL 查询。慢查询日志在数据库性能优化和问题诊断中发挥着重要作用。</p>
<h2 id="事务的四大特性">事务的四大特性?</h2>
<p><strong>事务特性ACID</strong>:<strong>原子性</strong>(<code>Atomicity</code>)、<strong>一致性</strong>(<code>Consistency</code>)、<strong>隔离性</strong>(<code>Isolation</code>)、<strong>持久性</strong>(<code>Durability</code>)。</p>
<ul>
<li>原子性(Atomicity):语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;实现主要基于<strong>undo log</strong></li>
<li>持久性(Durability):保证事务提交后不会因为宕机等原因导致数据丢失;实现主要基于<strong>redo log</strong></li>
<li>隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性保证事务执行尽可能不受其他事务影响;InnoDB默认的隔离级别是RR,RR的实现主要基于<strong>锁机制</strong>(包含next-key lock)、<strong>MVCC</strong>(包括数据的隐藏列、基于<strong>undo log的版本链、ReadView</strong>)</li>
<li>一致性(Consistency):事务追求的最终目标,是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。一致性的实现既需要数据库层面的保障,也需要应用层面的保障</li>
</ul>
<p>常见的InnoDB是支持事务的,但是MyISAM是不支持事务的</p>
<p>详情可以看这篇文章:MySQL事务原理:从ACID到隔离级别的全解析</p>
<h2 id="数据库的三大范式">数据库的三大范式</h2>
<p><strong>第一范式1NF</strong></p>
<p>确保数据库表字段的原子性。</p>
<p>比如字段 <code>userInfo</code>: <code>广东省 10086'</code> ,依照第一范式必须拆分成 <code>userInfo</code>: <code>广东省</code><code>userTel</code>:<code> 10086</code>两个字段。</p>
<p><strong>第二范式2NF</strong></p>
<p>首先要满足第一范式,另外包含两部分内容,一是表必须有一个主键;二是非主键列必须完全依赖于主键,而不能只依赖于主键的一部分。</p>
<p>举个例子。假定选课关系表为<code>student_course</code>(student_no, student_name, age, course_name, grade, credit),主键为(student_no, course_name)。其中学分完全依赖于课程名称,姓名年龄完全依赖学号,不符合第二范式,会导致数据冗余(学生选n门课,姓名年龄有n条记录)、插入异常(插入一门新课,因为没有学号,无法保存新课记录)等问题。</p>
<p>应该拆分成三个表:学生:<code>student</code>(stuent_no, student_name, 年龄);课程:<code>course</code>(course_name, credit);选课关系:<code>student_course_relation</code>(student_no, course_name, grade)。</p>
<p><strong>第三范式3NF</strong></p>
<p>首先要满足第二范式,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。</p>
<p>假定学生关系表为Student(student_no, student_name, age, academy_id, academy_telephone),主键为"学号",其中学院id依赖于学号,而学院地点和学院电话依赖于学院id,存在传递依赖,不符合第三范式。</p>
<p>可以把学生关系表分为如下两个表:学生:(student_no, student_name, age, academy_id);学院:(academy_id, academy_telephone)。</p>
<p><strong>2NF和3NF的区别?</strong></p>
<ul>
<li>2NF依据是非主键列是否完全依赖于主键,还是依赖于主键的一部分。</li>
<li>3NF依据是非主键列是直接依赖于主键,还是直接依赖于非主键。</li>
</ul>
<h2 id="并发事务带来了哪些问题">并发事务带来了哪些问题?</h2>
<p>先了解下几个概念:脏读、不可重复读、幻读。</p>
<ul>
<li><strong>脏读</strong>是指在一个事务处理过程里读取了另一个未提交的事务中的数据。</li>
<li><strong>不可重复读</strong>是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,另一个事务修改了数据并提交了。</li>
<li><strong>幻读</strong>是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录。对幻读的正确理解是一个事务内的读取操作的结论不能支撑之后业务的执行。假设事务要新增一条记录,主键为id,在新增之前执行了select,没有发现id为xxx的记录,但插入时出现主键冲突,这就属于幻读,读取不到记录却发现主键冲突是因为记录实际上已经被其他的事务插入了,但当前事务不可见。</li>
</ul>
<h2 id="不可重复读和幻读有什么区别">不可重复读和幻读有什么区别?</h2>
<ul>
<li>不可重复读的重点是内容修改或者记录减少比如多次读取一条记录发现其中某些记录的值被修改;</li>
<li>幻读的重点在于记录新增比如多次执行同一条查询语句(DQL)时,发现查到的记录增加了。</li>
</ul>
<p>幻读其实可以看作是不可重复读的一种特殊情况,单独把幻读区分出来的原因主要是解决幻读和不可重复读的方案不一样。</p>
<h2 id="事务隔离级别有哪些">事务隔离级别有哪些?</h2>
<p>事务隔离就是为了解决上面提到的脏读、不可重复读、幻读这几个问题。</p>
<p>MySQL数据库为我们提供的四种隔离级别:</p>
<ul>
<li><strong>Serializable</strong> (串行化):通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。</li>
<li><strong>Repeatable read</strong> (可重复读):MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,解决了不可重复读的问题。</li>
<li><strong>Read committed</strong> (读已提交):一个事务只能看见已经提交事务所做的改变。可避免脏读的发生。</li>
<li><strong>Read uncommitted</strong> (读未提交):所有事务都可以看到其他未提交事务的执行结果。</li>
</ul>
<h2 id="生产环境数据库一般用的什么隔离级别呢">生产环境数据库一般用的什么隔离级别呢?</h2>
<p><strong>生产环境大多使用RC</strong>。为什么不是RR呢?</p>
<blockquote>
<p>可重复读(Repeatable Read),简称为RR<br>
读已提交(Read Commited),简称为RC</p>
</blockquote>
<p>缘由一:在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多!<br>
缘由二:在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行!</p>
<p>也就是说,RC的并发性高于RR。</p>
<p>并且大部分场景下,不可重复读问题是可以接受的。毕竟数据都已经提交了,读出来本身就没有太大问题!</p>
<h2 id="并发事务的控制方式有哪些">并发事务的控制方式有哪些?</h2>
<p>MySQL 中并发事务的控制方式无非就两种:<strong>锁</strong> 和 <strong>MVCC</strong>。锁可以看作是悲观控制的模式,多版本并发控制(MVCC,Multiversion concurrency control)可以看作是乐观控制的模式。</p>
<p><strong>锁</strong> 控制方式下会通过锁来显式控制共享资源而不是通过调度手段,MySQL 中主要是通过 <strong>读写锁</strong> 来实现并发控制。</p>
<ul>
<li><strong>共享锁(S 锁)</strong>:又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。</li>
<li><strong>排他锁(X 锁)</strong>:又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条记录加任何类型的锁(锁不兼容)。</li>
</ul>
<p>读写锁可以做到读读并行,但是无法做到写读、写写并行。另外,根据根据锁粒度的不同,又被分为 <strong>表级锁(table-level locking)</strong> 和 <strong>行级锁(row-level locking)</strong> 。InnoDB 不光支持表级锁,还支持行级锁,默认为行级锁。行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类。</p>
<p><strong>MVCC</strong> 是多版本并发控制方法,即对一份数据会存储多个版本,通过事务的可见性来保证事务能看到自己应该看到的版本。通常会有一个全局的版本分配器来为每一行数据设置版本号,版本号是唯一的。</p>
<p>MVCC 在 MySQL 中实现所依赖的手段主要是: <strong>隐藏字段、read view、undo log</strong>。</p>
<ul>
<li>undo log : undo log 用于记录某行数据的多个版本的数据。</li>
<li>read view 和 隐藏字段 : 用来判断当前版本数据的可见性。</li>
</ul>
<h2 id="mysql是如何实现事务的">Mysql是如何实现事务的?</h2>
<p>MySQL 主要是通过:锁、RedoLog、Undo Log、MVCC 来实现事务:MySQL利用锁(行锁、间隙锁等等)机制,使用数据并发修改的控制,满足事务的隔离性</p>
<p>它会记录事务对数据库的所有修改,当 MSQL 发生宕机或崩溃时,通过重放redolog 就可以恢复数据,用来满足事务的持久性。</p>
<p>Redo Log(重做日志),UndoLog(回滚日志),它会记录事务的反向操作,简单地说就是保存数据的历史版本,用于事务的回滚,使得事务执行失败之后可以恢复之前的样子。实现原子性和隔离性</p>
<p>MVCC(多版本并发控制),满足了非锁定读的需求,提高了并发度,实现了读已提交和可重复读两种隔离级别,实现了事务的隔离性。</p>
<p>其实事务主要是为了实现一致性,具体是通过 AID,即原子性、隔离性和持久性来达到一致性的目的。</p>
<h2 id="事务的二阶段提交是什么">事务的二阶段提交是什么?</h2>
<p>MySQL事务的二阶段提交是指在MySQL中,为了确保redo log(重做日志)和binlog(二进制日志)之间的一致性,使用的一种机制,MySQL 通过二阶段提交来保证在crash recovery(崩溃恢复)时,不会出现数据丢失或数据不一致的情况。</p>
<p>二阶段提交的两个阶段:</p>
<ul>
<li>准备阶段(Prepare phase):在事务提交时,MySQL 的InnoDB引擎会先写入 redolog,并将其状态标记为prepare,表示事务已经准备提交但还未真正完成,此时的 redo 1og是预提交状态,还未标记为完成交。</li>
<li>提交阶段(Commit phase): 当redo log 的状态变为 pepare后,MySQLSener会写入 binlog(记录用户的DML操作),binlog写入成动后,MySQL 会通过 lnnoDB,将 redo log 状态改为commit,完成整个事务的提交过程。</li>
</ul>
<p>详情可以看这篇文章:Mysql篇-三大日志</p>
<h2 id="mysql-的隔离级别是基于锁实现的吗">MySQL 的隔离级别是基于锁实现的吗?</h2>
<p>MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。</p>
<p>SERIALIZABLE 隔离级别是通过锁来实现的,READ-COMMITTED 和 REPEATABLE-READ 隔离级别是基于 MVCC 实现的。不过, SERIALIZABLE 之外的其他隔离级别可能也需要用到锁机制,就比如 REPEATABLE-READ 在当前读情况下需要使用加锁读来保证不会出现幻读。</p>
<h2 id="表级锁和行级锁了解吗有什么区别">表级锁和行级锁了解吗?有什么区别?</h2>
<p>MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发写的情况下性非常差。InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level locking),默认为行级锁。</p>
<p>行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。</p>
<p><strong>表级锁和行级锁对比</strong>:</p>
<ul>
<li><strong>表级锁:</strong> MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。</li>
<li><strong>行级锁:</strong> MySQL 中锁定粒度最小的一种锁,是 <strong>针对索引字段加的锁</strong> ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。</li>
</ul>
<h2 id="行级锁锁的到底是什么行级锁的使用有什么注意事项">行级锁锁的到底是什么?行级锁的使用有什么注意事项?</h2>
<p>MySQL 的 InnoDB 存储引擎实现的行级锁,实际上锁定的是基于索引的行记录。这意味着,行锁针对的是数据表中的索引记录,而非数据表本身的物理行。</p>
<p>InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 <code>UPDATE</code>、<code>DELETE</code> 语句时,如果 <code>WHERE</code>条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。这个在我们日常工作开发中经常会遇到,一定要多多注意!!!</p>
<p>不过,很多时候即使用了索引也有可能会走全表扫描,这是因为 MySQL 优化器的原因。</p>
<h2 id="innodb-有哪几类行锁">InnoDB 有哪几类行锁?</h2>
<p>InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:</p>
<ul>
<li><strong>记录锁(Record Lock)</strong>:也被称为记录锁,属于单个行记录上的锁。</li>
<li><strong>间隙锁(Gap Lock)</strong>:锁定一个范围,不包括记录本身。</li>
<li><strong>临键锁(Next-Key Lock)</strong>:Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题(MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。</li>
</ul>
<p><strong>在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。</strong></p>
<p>一些大厂面试中可能会问到 Next-Key Lock 的加锁范围,详细可以查看:行级锁怎么加的? 。</p>
<h2 id="共享锁和排他锁">共享锁和排他锁</h2>
<p>SELECT 的读取锁定主要分为两种方式:共享锁和排他锁。</p>
<pre><code class="language-sql">select * from table where id&lt;6 lock in share mode;--共享锁
select * from table where id&lt;6 for update;--排他锁
</code></pre>
<p>这两种方式主要的不同在于<code>LOCK IN SHARE MODE </code>多个事务同时更新同一个表单时很容易造成死锁。</p>
<p>申请排他锁的前提是,没有线程对该结果集的任何行数据使用排它锁或者共享锁,否则申请会受到阻塞。在进行事务操作时,MySQL会对查询结果集的每行数据添加排它锁,其他线程对这些数据的更改或删除操作会被阻塞(只能读操作),直到该语句的事务被<code>commit</code>语句或<code>rollback</code>语句结束为止。</p>
<p><code>SELECT... FOR UPDATE</code> 使用注意事项:</p>
<ol start="33">
<li><code>for update</code> 仅适用于innodb,且必须在事务范围内才能生效。</li>
<li>根据主键进行查询,查询条件为<code>like</code>或者不等于,主键字段产生<strong>表锁</strong>。</li>
<li>根据非索引字段进行查询,会产生<strong>表锁</strong>。</li>
</ol>
<h2 id="mvcc-实现原理">MVCC 实现原理?</h2>
<p>详细请查看:ReadView</p>
<p>MVCC(<code>Multiversion concurrency control</code>) 就是同一份数据保留多版本的一种方式,进而实现并发控制。在查询的时候,通过<code>read view</code>和版本链找到对应版本的数据。</p>
<p>作用:提升并发性能。对于高并发场景,MVCC比行级锁开销更小。</p>
<p><strong>MVCC 实现原理如下:</strong></p>
<ol>
<li>首先获取事务自己的事务 ID;</li>
<li>获取 ReadView;</li>
<li>查询得到的数据,然后与 ReadView 中的事务版本号(m_ids,min_trx_id,max_trx_id)进行比较;</li>
<li>如果不符合 ReadView 规则,就需要从 Undo Log 中(即根据roll_point)获取历史快照;</li>
<li>最后返回符合规则的数据。 在隔离级别为读已提交(Read Committed)时,一个事务中的每一次 SELECT 查询都会重新获取一次Read View</li>
</ol>
<p><strong>Read View的规则</strong></p>
<ol>
<li>如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View <strong>前</strong> 已经提交的事务生成的,所以该版本的记录<strong>对当前事务可见</strong>。</li>
<li>如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View <strong>后</strong> 才启动的事务生成的,所以该版本的记录<strong>对当前事务不可见</strong>。</li>
<li>如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
<ul>
<li>如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录<strong>对当前事务不可见</strong>。</li>
<li>如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。</li>
</ul>
</li>
</ol>
<h2 id="如果-mysql中没有-mvcc会有什么影响">如果 MySQL中没有 MVCC,会有什么影响?</h2>
<p>如果没有 MVCC,系统必须频繁地对读写操作进行加锁来保证数据的正确性,因为增加了锁的获取和释放的开销,会导致整体系统响应速度变慢,这种实现叫 LBCC (Lock-Based Concurrent Control).</p>
<h2 id="快照读和当前读">快照读和当前读</h2>
<p>表记录有两种读取方式。</p>
<ul>
<li>
<p>快照读:读取的是快照版本。普通的<code>SELECT</code>就是快照读。通过mvcc来进行并发控制的,不用加锁。</p>
</li>
<li>
<p>当前读:读取的是最新版本。<code>UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE</code>是当前读。</p>
</li>
</ul>
<p>快照读情况下,InnoDB通过<code>mvcc</code>机制避免了幻读现象。而<code>mvcc</code>机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。</p>
<h2 id="selectfor-update会锁表还是锁行">select...for update会锁表还是锁行?</h2>
<p>如果查询条件用了索引/主键,那么<code>select ... for update</code>就会加行锁。</p>
<p>如果是普通字段(没有索引/主键),那么<code>select ..... for update</code>就会加表锁。</p>
<h2 id="update-是锁行还是锁表">update 是锁行还是锁表?</h2>
<p>首先,InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。</p>
<ol>
<li>当执行update语句时,where中的过滤条件列,如果用到索引,就是锁行;如果无法用索引,就是锁表。</li>
<li>如果两个update语句同时执行,第一个先执行触发行锁,但是第二个没有索引触发表锁,因为有个行锁住了,所以还是会等待行锁释放,才能锁表。</li>
<li>当执行insert或者delete语句时,锁行。</li>
</ol>
<h2 id="mysql是如何避免幻读的">MySQL是如何避免幻读的?</h2>
<ul>
<li>在快照读情况下,MySQL通过<code>mvcc</code>来避免幻读。</li>
<li>在当前读情况下,MySQL通过<code>next-key</code>来避免幻读(加行锁和间隙锁来实现的)。</li>
</ul>
<p>next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。</p>
<p><code>Serializable</code>隔离级别也可以避免幻读,会锁住整张表,并发性极低,一般不会使用。</p>
<h2 id="乐观锁和悲观锁是什么">乐观锁和悲观锁是什么?</h2>
<p>数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。</p>
<ul>
<li>悲观锁:假定会发生并发冲突,会对操作的数据进行加锁,直到提交事务,才会释放锁,其他事务才能进行修改。实现方式:使用数据库中的锁机制。</li>
<li>乐观锁:假设不会发生并发冲突,只在提交操作时检查是否数据是否被修改过。给表增加<code>version</code>字段,在修改提交之前检查<code>version</code>与原来取到的<code>version</code>值是否相等,若相等,表示数据没有被修改,可以更新,否则,数据为脏数据,不能更新。实现方式:乐观锁一般使用版本号机制或<code>CAS</code>算法实现。</li>
</ul>
<h2 id="mysql只操作同一条记录也会死锁吗">MySQL只操作同一条记录也会死锁吗</h2>
<p>在MySQL中,即使多个事务仅对同一条记录进行操作,也可能发生死锁。这通常与InnoDB存储引擎的锁管理机制有关。以下是一些触发这种情况的场景:</p>
<p>索引导致的锁竞争:如果事务在WHERE子句中使用不同的索引来查找相同的行,InnoDB可能会导致多个事务以不同顺序锁住这些索引,从而产生死锁。<br>
自增锁(auto-increment lock):在使用自增列时,不同事务可能在等待获取表级锁来增加自增值,某些情况下也会出现死锁。<br>
外键约束:外键检查过程中可能会涉及多个表,多个事务可能会因为不同的锁顺序而陷入死锁。</p>
<h2 id="mysql-中如果发生死锁应该如何解决">MySQL 中如果发生死锁应该如何解决?</h2>
<p>自动检测与回滚:MySQL自带死锁检测机制(innodb deadlock detect),当检测到死锁时,数据库会自动回滚其中一个事务,以解除死锁。通常会回滚事务中持有最少资源的那个。也有锁等待超时的参数(innodblock wait timeout),当获取锁的等待时间超过阈值时,就释放锁进行回滚</p>
<p>手动 kill 发生死锁的语句:可以通过命令,手动快速地找出被阻塞的事务及其线程ID,然后手动 kill它,及时释放资源</p>
<h2 id="mysql-中长事务可能会导致哪些问题">MySQL 中长事务可能会导致哪些问题?</h2>
<ul>
<li>长时间的锁竞争,阻塞资源:长事务持有锁的时间较长,容易导致其他事务在尝试获取相同锁时发生阻塞,从而增加系统的等待时间和降低并发性能。业务线程也会因为长时间的数据库请求等待而阻塞,部分业务的阻塞可能还会影响到别的服务,导致产生雪崩,最终使得服务全面崩盘,导致非常严重的线上事故。</li>
<li>死锁风险:长事务更容易产生死锁,因为多个事务可能在互相等待对方释放锁,导致系统无法继续执行。</li>
<li>主从延迟:主库需要长时间执行,然后传输给从库,从库又要重放好久,期间可能有很长一段时间数据是不同步的。</li>
<li>回滚导致时间浪费:如果长事务执行很长一段时间,中间突发状况导致抛错,使得事务回滚了,之前做的执行都浪费了。</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19030197
頁: [1]
查看完整版本: MySQL 中常见的日志有哪些?是如何实现事务的?