-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path11256.html
491 lines (480 loc) · 47.6 KB
/
11256.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>MySQL实战 基础篇(一) | 征蓬</title><meta name="keywords" content="MySQL"><meta name="author" content="huy"><meta name="copyright" content="huy"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="基础篇一:查询、更新语句的执行流程,事物隔离级别的现象和实现,长事物的风险。">
<meta property="og:type" content="article">
<meta property="og:title" content="MySQL实战 基础篇(一)">
<meta property="og:url" content="http://example.com/11256.html">
<meta property="og:site_name" content="征蓬">
<meta property="og:description" content="基础篇一:查询、更新语句的执行流程,事物隔离级别的现象和实现,长事物的风险。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/pics/scenery/06.png">
<meta property="article:published_time" content="2021-04-16T23:55:08.000Z">
<meta property="article:modified_time" content="2021-10-15T03:10:21.187Z">
<meta property="article:author" content="huy">
<meta property="article:tag" content="MySQL">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/pics/scenery/06.png"><link rel="shortcut icon" href="/pics/avatar/8.png"><link rel="canonical" href="http://example.com/11256"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="/font/wcsf.ttf" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = {
root: '/',
algolia: undefined,
localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容:${query}"}},
translate: undefined,
noticeOutdate: undefined,
highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
copy: {
success: '复制成功',
error: '复制错误',
noSupport: '浏览器不支持'
},
relativeDate: {
homepage: false,
post: false
},
runtime: '',
date_suffix: {
just: '刚刚',
min: '分钟前',
hour: '小时前',
day: '天前',
month: '个月前'
},
copyright: undefined,
lightbox: 'fancybox',
Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#121212","position":"bottom-left"},
source: {
jQuery: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js',
justifiedGallery: {
js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
},
fancybox: {
js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
}
},
isPhotoFigcaption: false,
islazyload: false,
isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
title: 'MySQL实战 基础篇(一)',
isPost: true,
isHome: false,
isHighlightShrink: false,
isToc: true,
postUpdate: '2021-10-15 11:10:21'
}</script><noscript><style type="text/css">
#nav {
opacity: 1
}
.justified-gallery img {
opacity: 1
}
#recent-posts time,
#post-meta time {
display: inline !important
}
</style></noscript><script>(win=>{
win.saveToLocal = {
set: function setWithExpiry(key, value, ttl) {
if (ttl === 0) return
const now = new Date()
const expiryDay = ttl * 86400000
const item = {
value: value,
expiry: now.getTime() + expiryDay,
}
localStorage.setItem(key, JSON.stringify(item))
},
get: function getWithExpiry(key) {
const itemStr = localStorage.getItem(key)
if (!itemStr) {
return undefined
}
const item = JSON.parse(itemStr)
const now = new Date()
if (now.getTime() > item.expiry) {
localStorage.removeItem(key)
return undefined
}
return item.value
}
}
win.getScript = url => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
document.head.appendChild(script)
})
win.activateDarkMode = function () {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
}
}
win.activateLightMode = function () {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
}
}
const t = saveToLocal.get('theme')
if (t === 'dark') activateDarkMode()
else if (t === 'light') activateLightMode()
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
if (asideStatus === 'hide') {
document.documentElement.classList.add('hide-aside')
} else {
document.documentElement.classList.remove('hide-aside')
}
}
const detectApple = () => {
if (GLOBAL_CONFIG_SITE.isHome && /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
document.documentElement.classList.add('apple')
}
}
detectApple()
})(window)</script><link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end start --><link rel="stylesheet" href="https://npm.elemecdn.com/hexo-butterfly-tag-plugins-plus@latest/lib/assets/font-awesome-animation.min.css" media="defer" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-butterfly-tag-plugins-plus@latest/lib/tag_plugins.min.css" media="defer" onload="this.media='all'"><script async src="https://npm.elemecdn.com/hexo-butterfly-tag-plugins-plus@latest/lib/assets/carousel-touch.js"></script><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.1"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/pics/Asoul/8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/archives/"><div class="headline">文章</div><div class="length-num">54</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/categories/"><div class="headline">分类</div><div class="length-num">12</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/pics/scenery/06.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">征蓬</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">MySQL实战 基础篇(一)</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-04-16T23:55:08.000Z" title="发表于 2021-04-17 07:55:08">2021-04-17</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-10-15T03:10:21.187Z" title="更新于 2021-10-15 11:10:21">2021-10-15</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/MySQL/">MySQL</a></span></div><div class="meta-secondline"></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><blockquote>
<p>该系列是极客时间林晓斌的 MySQL实战45讲 课程笔记。</p>
</blockquote>
<h2 id="MySQL逻辑架构图">MySQL逻辑架构图</h2>
<p><img src="/pics/MySQL/%E9%80%BB%E8%BE%91%E6%9E%B6%E6%9E%84%E5%9B%BE.png" alt="MySQL 逻辑架构图"></p>
<h2 id="查询语句的执行过程">查询语句的执行过程</h2>
<p>大体来说,MySQL可以分为两部分:Server层和存储引擎层。</p>
<ul>
<li>Server层</li>
</ul>
<p>包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL大多数核心服务功能,以及所有内置函数(例如:日期、时间、数学和加密函数等)。</p>
<p>所有跨存储引擎的功能都在这一层实现,比如:存储过程、触发器、视图等。</p>
<ul>
<li>存储引擎层</li>
</ul>
<p>负责数据的存储和提取。</p>
<p>架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。</p>
<p>现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。</p>
<p>也就是说,如果执行 CREATE TABLE 建表的时候,如果不指定引擎类型,默认是用 InnoDB。也可以在建表语句中使用 engine = memory,指定使用内存引擎创建表。</p>
<p>不同存储引擎的表数据存取方式、支持的功能不同。</p>
<p>从图中可以看出,不同存储引擎共同一个 <strong>Server层</strong>。</p>
<p>下面以这条查询语句为例,了解执行流程和每个组件的作用:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> ID <span class="operator">=</span> <span class="number">10</span>;</span><br></pre></td></tr></table></figure>
<h3 id="连接器">连接器</h3>
<article class="message is-info"><div class="message-body">
<p><b>第一步:通过连接器连接数据库。</b>连接器负责与客户端建立连接、获取权限、维持和管理连接。</p>
</div></article>
<p>连接命令如下,其中,mysql是客户端工具,用来和服务端建立连接;-h 指定IP;-P 指定端口;-u 指定用户名;p指定密码。:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql <span class="operator">-</span>h$ip <span class="operator">-</span>P$port <span class="operator">-</span>u$<span class="keyword">user</span> <span class="operator">-</span>p</span><br></pre></td></tr></table></figure>
<p>在完成TCP握手后,连接器使用输入的用户名和密码验证身份:</p>
<ul>
<li>
<p>如果用户名或密码错误,会收到一个"Access denied for user"的错误,然后客户端程序结束执行。</p>
</li>
<li>
<p>如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。</p>
<p>如果权限发生变化,将会在下次重新连接后生效。</p>
</li>
</ul>
<p>可以使用 show processlist 命令查看空闲的连接,如果长时间没有操作,连接器会自动断开,是由参数 wait_timeout 控制的,默认值是 8 小时。</p>
<p>连接断开后如果继续发起请求,会收到错误提示:Lost connection to MySQL server during query。如果要继续请求,重新建立连接后执行就可以了。</p>
<hr>
<ul>
<li>
<p>长链接:连接成功后,如果客户端持续有请求,则一直使用同一个连接。</p>
</li>
<li>
<p>短连接:每次执行完很少的几次查询就断开连接,下一次查询再重新建立连接。</p>
</li>
</ul>
<p><strong>长连接太多会占用内存,可能导致内存占用过多,被系统杀进程(MySQL异常重启)</strong>;**</p>
<p><strong>短连接频繁建立耗时</strong></p>
<p>尽量使用长连接,上述问题的解决方法可以参考下列两种方式:</p>
<ol>
<li>
<p>定期断开长连接。使用过一段时间后,或者程序里执行内存较大的操作后,断开连接,之后要查询再重连。</p>
</li>
<li>
<p>MySQL 5.7 或更新版本,可以执行 mysql_reset_connection 初始化连接资源。不需要重新连接和权限校验。</p>
</li>
</ol>
<h3 id="查询缓存">查询缓存</h3>
<article class="message is-info"><div class="message-body">
<p>第二步:查询缓存。</b>建立连接后就可以执行SQL语句,会先查询缓存。</p>
</div></article>
<p>MySQL拿到一个查询请求后,会先在缓存中查找,是否执行过该语句。</p>
<p>之前执行结果是以 key-value 形式保存的,key是查询语句,value是查询结果。如果能根据语句找到key,就会将value直接返回给客户端。</p>
<p>如果语句不在缓存中,则继续执行后面的阶段,执行完成后将结果存入缓存。</p>
<hr>
<p><strong>查询缓存弊大于利,且MySQL 8.0后删除了该功能。</strong></p>
<ol>
<li>
<p>查询缓存时,需要语句和参数与缓存中的key完全相同。</p>
</li>
<li>
<p>表更新后,该表的全部缓存会被情况。除非是系统配置表,或者静态码表,否则不推荐使用缓存。</p>
</li>
</ol>
<p>对于需要查询的语句,可以设置参数进行查询(MySQL 8.0后删除):</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> SQL_CACHE <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> ID <span class="operator">=</span> <span class="number">10</span>;</span><br></pre></td></tr></table></figure>
<h3 id="分析器">分析器</h3>
<article class="message is-info"><div class="message-body">
<p>第三步:对SQL语句进行解析。</b>如果缓存没有命中,分析器会解析SQL语句,了解用户意图。</p>
</div></article>
<p>这一步会判断语句是否正确,表、列是否存在等。</p>
<p>分析器会按照词法、语法的步骤分析传入的SQL语句:</p>
<ol>
<li>词法分析</li>
</ol>
<p>将输入语句中的 select 关键字识别出来,是查询语句。把字符串 TABLE 识别为 表名TABLE,字符串 ID 识别为 列ID。</p>
<ol start="2">
<li>语法分析</li>
</ol>
<p>分析器根据词法分析的结果,判断查询语句是否符合MySQL的语法要求。</p>
<p>如果出现语法错误,会收到提示,例如:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ELECT <span class="operator">*</span> <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> ID<span class="operator">=</span><span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">ERROR <span class="number">1064</span> (<span class="number">42000</span>): You have an error <span class="keyword">in</span> your <span class="keyword">SQL</span> syntax; <span class="keyword">check</span> the manual that corresponds <span class="keyword">to</span> your MySQL server version <span class="keyword">for</span> the <span class="keyword">right</span> syntax <span class="keyword">to</span> use near <span class="string">'ELECT * FROM TABLE WHERE ID=1'</span> <span class="keyword">at</span> line <span class="number">1</span></span><br></pre></td></tr></table></figure>
<p>语法错误通常会提示第一个出现错误的位置,只需要关注 user near 后的内容。</p>
<h3 id="优化器">优化器</h3>
<article class="message is-info"><div class="message-body">
<p>第四步:对SQL语句进行优化,选择处理方式。</b>执行前的最后一步,这一步将决定SQL语句的执行方案。</p>
</div></article>
<p>优化器是在表里有多个索引时,决定使用哪一个索引;或者在多表关联查询时,决定各个表的连接顺序。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> TABLE1 t1 <span class="keyword">JOIN</span> TABLE2 t2 <span class="keyword">USING</span>(ID) <span class="keyword">WHERE</span> t1.C <span class="operator">=</span> <span class="number">10</span> <span class="keyword">and</span> t2.D <span class="operator">=</span> <span class="number">20</span>;</span><br></pre></td></tr></table></figure>
<p>以这条查询语句为例:</p>
<ul>
<li>
<p>可以先TABLE1中取出 C = 10 的记录,再根据 ID 关联到TABLE2,再判断TABLE2中 D 的值是否为 20;</p>
</li>
<li>
<p>也可以TABLE2中取出 D = 20 的记录,再根据 ID 关联到TABLE1,再判断TABLE1中 C 的值是否为 10.</p>
</li>
</ul>
<p>这两种执行方案的逻辑结果相同,但是执行效率不同,优化器的作用就是决定使用哪一个方案。</p>
<h3 id="执行器">执行器</h3>
<article class="message is-info"><div class="message-body">
<p>第五步:执行SQL语句。</b>最后一步,确定了意图和方案后,开始执行语句。</p>
</div></article>
<p>开始执行前,会先判断用户对这个表是否有操作的权限,如果没有,就会返回权限错误。</p>
<p><strong>限验证不止在执行器中,在分析器中和缓存结果返回前都会进行验证。</strong></p>
<p>判断用户有该表的权限后,语句会打开表继续执行。打开表时,执行器根据表的引擎定义,调用这个引擎的相关接口。</p>
<p>在这条语句中,如果ID字段没有索引,执行器的流程如下:</p>
<ol>
<li>
<p>调用 InnoDB 引擎接口,取这个表的第一行,判断ID是否为10,如果不是则跳过,如果是就把这行结果存到结果集中;</p>
</li>
<li>
<p>调用引擎接口取下一行,进行相同的逻辑判断,直到这个表的最后一行;</p>
</li>
<li>
<p>执行器将上述遍历过程中,所有满足条件的结果行组成记录,作为结果集返回给客户端。</p>
</li>
</ol>
<p>至此,这个查询语句就执行完了。</p>
<p>如果ID字段有索引,执行的逻辑也差不多:</p>
<ol>
<li>
<p>第一次调用的是 “取满足条件的第一行” 这个接口;</p>
</li>
<li>
<p>循环调用 “取满足条件的下一行” 接口;</p>
</li>
<li>
<p>遍历完成后返回给客户端。</p>
</li>
</ol>
<p>数据库的慢查询日志中有一个 rows_examined 的字段,表示这条语句在执行过程中扫描了多少行。这个值是在执行器每次调用引擎获取数据行时累加的。</p>
<p>有些场景下,执行器调用一次,在内部引擎扫描了多行,因此引擎扫描行数和 rows_examined 不是完全相同的。</p>
<article class="message is-info"><div class="message-header">
<p>总结:</p>
</div><div class="message-body">
<p><br>1. 引擎层各种获取数据的方法都是定义好的,是静态方法;<br>
<br>2. 优化器生成的执行计划,决定了执行器会选择存储引擎的哪个方法获取数据;<br>
<br>3. InnoDB存储引擎层的优化措施很多,对执行器来说是一个黑箱,只要调用即可。</p>
</div></article>
<h2 id="更新语句的执行过程">更新语句的执行过程</h2>
<p>以下面这条更新语句为例:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">UPDATE <span class="keyword">TABLE</span> <span class="keyword">SET</span> B <span class="operator">=</span> B <span class="operator">+</span> <span class="number">1</span> <span class="keyword">WHERE</span> ID <span class="operator">=</span> <span class="number">10</span>;</span><br></pre></td></tr></table></figure>
<p>和查询语句类似,通过连接器和数据库建立连接。</p>
<p>这条更新语句会表 TABLE 的缓存全部清空,这也是不推荐使用缓存查询的原因。</p>
<p>然后按照分析器、优化器、执行器的流程继续执行。</p>
<p>与查询流程不一样的出,除了上述部分,更新语句还涉及到两个重要的日志模块:</p>
<ol>
<li>
<p>redo log (重做日志)</p>
</li>
<li>
<p>binlog (归档日志)</p>
</li>
</ol>
<article class="message is-danger"><div class="message-header">
<p>客户端在执行下列语句时,数据库服务端会涉及到 redo log 和 binlog 两个日志文件的更新:</p>
</div><div class="message-body">
<p>DDL(create)<br>
<br>DML(insert, update, delete)<br>
<br>DCL(grant, revoke)</p>
</div></article>
<h3 id="redo-log">redo log</h3>
<article class="message is-info"><div class="message-header">
<p>举例:</p>
</div><div class="message-body">
<p>不知道你还记不记得《孔乙己》这篇文章,酒店掌柜有一个黑板,专门用来记录客人的赊账记录。<br>
<br><br>如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,黑板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本。<br>
<br><br>如果有人要赊账或者还账的话,掌柜一般有两种做法:<br>
<br><br><b>一</b>:直接把账本翻出来,把这次赊的账加上去或者扣除掉;<br>
<br><br><b>二</b>:先在黑板上记下这次的账,等打烊以后再把账本翻出来核算。<br>
<br><br>在生意红火柜台很忙时,掌柜一定会选择后者,因为前者操作实在是太麻烦了。<br>
<br><br>首先,你得找到这个人的赊账总额那条记录。你想想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老花镜慢慢找,找到之后再拿出算盘计算,最后再将结果写回到账本上。<br>
<br><br>这整个过程想想都麻烦。相比之下,还是先在黑板上记一下方便。<br>
<br><br>你想想,如果掌柜没有黑板的帮助,每次记账都得翻账本,效率是不是低得让人难以忍受?</p>
</div></article>
<p>MySQL中也是如此,如果每次更新操作都需要写进磁盘,磁盘找到对应记录再更新,整个IO成本和查找成本都很高。MySQL也使用了类似酒店掌柜黑板的思路来解决这个问题。</p>
<p>黑板和账本配合的过程,就是MySQL中长水的 WAL技术(Write-Ahead logging),先写日志,再写磁盘(先写到黑板上,再更新账本)。</p>
<p>具体而言,需要更新一条记录时,InnoDB会先把记录写到 redo log 中,并更新内存,这时更新就算完成了。在空闲的时候,InnoDB会把这条操作记录更新到磁盘里面。</p>
<hr>
<p>如果今天需要更新的数据特别多怎么办?</p>
<p>InnoDB的 redo log 大小是固定的,比如一组四个文件,每个文件大小是1GB,那这块“黑板”就可以记录4GB的操作。从头开始写,写到末尾就回到开头循环。</p>
<p><img src="/pics/MySQL/redoLog%E8%AF%BB%E5%86%99%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="redoLog读写示意图"></p>
<ul>
<li>
<p>write pos:是当前记录的位置,一边写一边后移,写到 logFile-3的末尾就会回到logFile-0的开头,继续操作。</p>
</li>
<li>
<p>check point:是当前要擦除的位置,往后推移并循环,擦除前需要把记录更新到数据文件。</p>
</li>
<li>
<p>write pos 和 check point 之间:表示还空着的部分,可以记录新的操作。如果write pos追上的话,则不能执行新的更新,需要暂时停止,擦除记录后继续操作。</p>
</li>
</ul>
<p>有了 redo log,InnoDB才能保证,即使数据库发生异常重启,之前提交的记录也不会丢失,这个功能被称为<strong>crash-safe</strong>。</p>
<article class="message is-info"><div class="message-header">
<p>总结:</p>
</div><div class="message-body">
<ol>
<li>redo log 是 InnoDB 特有的,属于存储引擎层;</li>
<li>redo log保存在内存和硬盘中,分为 redo log buffer(日志缓冲,易丢失) 和 redo log file(持久的)[^1];</li>
<li>redo log 记录的不是数据页更新后的状态,而是数据页的改动。</li>
</ol>
</div></article>
<h3 id="binlog">binlog</h3>
<article class="message is-danger"><div class="message-header">
<p>注意:MySQL 5.7 版本的 binlog 默认关闭</p>
</div><div class="message-body">
<p>使用命令:<code>show variables like 'log_bin%';</code> 查看,需要手动开启。</p>
</div></article>
<p>最开始没有 InnoDB 引擎,MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。</p>
<ol>
<li>binlog 是在MySQL的Server层实现;</li>
<li>binlog 是逻辑日志,记录的是这个语句的原始逻辑,例如:给 ID = 10 这一行的 B 字段 + 1;</li>
<li>binlog 存储空间不固定,也就是说一个 binlog 文件写到一定大小后,会自动切换到下一个,不会覆盖以前的日志。</li>
</ol>
<article class="message is-info"><div class="message-header">
<p>为什么binlog记录了操作却不支持crash-safe?</p>
</div><div class="message-body">
<ol>
<li>redo log 记录的是没有写入磁盘的日志,写入磁盘的日志会被删除;而 binlog 记录的是全量日志;</li>
<li>假设数据库crash,想将未写入磁盘但是已写入 redo log 和 binlog 中的数据恢复到内存中,<br>
binlog无法恢复,因为没有类似 check point 的记录,不能明确哪些数据已经写入到磁盘,哪些数据还没有写入。</li>
</ol>
</div></article>
<p>对这两个日志系统有一定了解后,再来看执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。</p>
<ol>
<li>执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。</li>
<li>执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。</li>
<li>引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。</li>
<li>执行器生成这个操作的 binlog,并把 binlog 写入磁盘。</li>
<li>执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。</li>
</ol>
<p><img src="/pics/MySQL/update%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.png" alt="update执行流程示意图"></p>
<p>在示意图中最后三步,将 redo log 的提交拆分成了两步,prepare 和 commit,这就是<strong>两阶段提交</strong>。</p>
<h3 id="两阶段提交">两阶段提交</h3>
<p>因为 redo log 和 binlog 是完全不同的逻辑,如果先写 redo log 再写 binlog,或者使用反过来的顺序,很容易出现问题。</p>
<p>以上面的更新语句为例,假设当前 ID = 10 的这一行,字段 B 的数值为0,需要将其 +1;执行update的过程中,在写入完第一个日志后,还没有开始写入第二个日志前,发生了crash。</p>
<ol>
<li>redo log 写入完成,binlog 尚未写入</li>
</ol>
<p>假设此时数据库crash,只凭借 redo log 也可以恢复数据,字段 B 的数值已经变为1。</p>
<p>但是 binlog 没有写入就发生日常,这一条语句不会出现在 binlog 的备份日志中,如果日后需要使用 binlog 恢复数据,恢复后的字段 B 仍为0,与原库的数据不符。</p>
<ol start="2">
<li>redo log 没有写入,binlog 写入成功</li>
</ol>
<p>因为 redo log 还没有写入就发生异常,重启后判断这个事物无效,将会回滚这次操作,字段 B 的值仍为0。</p>
<p>但是 binlog 已经保存了这次操作,之后使用 binlog 恢复数据时,会把字段 B 改为1,与原库中的数据不符。</p>
<p>可以看出,如果不使用两阶段提交,很容易出现恢复数据时前后两次数据不一致的情况。</p>
<p>这个情况不止出现在恢复数据时,数据库扩容通常使用全量备份 + binlog 实现,如果出现恢复时,前后两次数据不一致的情况,就会导致线上的主从数据库不一致。</p>
<p>redo log 和 binlog 都可以表示事物的提交状态,两阶段提交是为了保证逻辑上的相同。</p>
<h3 id="小结">小结</h3>
<ol>
<li>怎样让数据库恢复到半个月内任意一秒的状态?</li>
</ol>
<p>binlog 记录了全部的逻辑操作,如果要恢复误删除的数据,就必须要有一个时间段内的全部 binlog,比如要恢复半个月内的任意一次操作,那必须有半个月内全部 binlog 备份。</p>
<p>例如:某天上午十点有一次误删表的操作,应该如何恢复呢?</p>
<pre><code>- 首先,找到最近一次的全量备份,将其恢复到临时库;
- 然后从备份的时间点开始,取出 binlog,一直到误删的前一时刻;
- 就可以按需将临时库的数据恢复到正式库中。
</code></pre>
<ol start="2">
<li>redo log和binlog的区别:</li>
</ol>
<table>
<tr>
<td></td>
<td>redo log</td>
<td>binlog</td>
</tr>
<tr>
<td>所在层级不同</td>
<td>引擎层,InnoDB特有的</td>
<td>Server层</td>
</tr>
<tr>
<td>记录内容不同</td>
<td>物理日志:在某个数据页上做了什么修改</td>
<td>逻辑日志:记录原始逻辑,给id=2这一行的c字段+1</td>
</tr>
<tr>
<td>写入的文件和方式不同</td>
<td>写入的空间固定,循环写</td>
<td>空间不固定,追加写入。文件写完后会自动切换到下一个文件,不覆盖之前的日志。</td>
</tr>
</table>
<ol start="3">
<li>两阶段提交</li>
</ol>
<p>是跨系统维持数据库逻辑一致性的常用方案。</p>
<ol start="4">
<li>
<p>相关参数设置</p>
<ul>
<li>innodb_flush_log_at_trx_commit = 1</li>
</ul>
<p>表示每次事物的 redo log 都会持久化到磁盘中,可以保证数据库异常重启后,数据不丢失。</p>
<ul>
<li>sync_binlog = 1</li>
</ul>
<p>表示每次事物的 binlog 都会持久化到磁盘中,可以保证数据库异常重启后,binlog 不丢失。</p>
</li>
</ol>
<h2 id="问题">问题</h2>
<p><a target="_blank" rel="noopener" href="https://time.geekbang.org/column/article/68633">02 | 日志系统:一条SQL更新语句是如何执行的?-极客时间 (geekbang.org)</a><br>
网页搜索 WL,评论区</p>
<h3 id="redo-log的概念是什么-为什么会存在">redo log的概念是什么?为什么会存在</h3>
<p>redo log 是 InnoDB 特有的,在存储引擎层,包含两部分:</p>
<ol>
<li>内存中的 日志缓冲 redo log buffer</li>
<li>硬盘中的 日志文件 redo log file</li>
</ol>
<h3 id="什么是WAL-write-ahead-log-机制-好处是什么">什么是WAL(write-ahead log)机制, 好处是什么</h3>
<h3 id="redo-log-为什么可以保证crash-safe机制">redo log 为什么可以保证crash safe机制</h3>
<h3 id="binlog的概念是什么-起到什么作用-可以做crash-safe吗">binlog的概念是什么, 起到什么作用, 可以做crash safe吗?</h3>
<h3 id="binlog和redolog的不同点有哪些">binlog和redolog的不同点有哪些?</h3>
<h3 id="物理一致性和逻辑一直性各应该怎么理解">物理一致性和逻辑一直性各应该怎么理解?</h3>
<h3 id="执行器和innoDB在执行update语句时候的流程是什么样的">执行器和innoDB在执行update语句时候的流程是什么样的?</h3>
<h3 id="如果数据库误操作-如何执行数据恢复">如果数据库误操作, 如何执行数据恢复?</h3>
<h3 id="什么是两阶段提交-为什么需要两阶段提交-两阶段提交怎么保证数据库中两份日志间的逻辑一致性-什么叫逻辑一致性">什么是两阶段提交, 为什么需要两阶段提交, 两阶段提交怎么保证数据库中两份日志间的逻辑一致性(什么叫逻辑一致性)?</h3>
<h3 id="如果不是两阶段提交-先写redo-log和先写bin-log两种情况各会遇到什么问题">如果不是两阶段提交, 先写redo log和先写bin log两种情况各会遇到什么问题?</h3>
<h2 id="参考-14">参考</h2>
<p>[^1]: <a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_42366095/article/details/113435651">mysql undo log位置_MySQL 日志(redo log 和 undo log) 都是什么鬼?</a></p>
<p>[^2]: <a target="_blank" rel="noopener" href="https://juejin.cn/post/6860252224930070536#heading-0">必须了解的mysql三大日志-binlog、redo log和undo log - 掘金 (juejin.cn)</a></p>
<p>[^3]: <a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/QLnbpRNKd9TqPHDSsioKwg">MySQL的redo log和binlog日志</a></p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">huy</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://example.com/11256.html">http://example.com/11256.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://example.com" target="_blank">征蓬</a>!</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/MySQL/">MySQL</a></div><div class="post_share"><div class="social-share" data-image="/pics/scenery/06.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://jsdelivr.pai233.top/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://jsdelivr.pai233.top/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/48002.html" title="MySQL实战 基础篇(二)"><img class="cover" src="/pics/scenery/22.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-26</div><div class="title">MySQL实战 基础篇(二)</div></div></a></div><div><a href="/47099.html" title="MySQL实战 基础篇(三)"><img class="cover" src="/pics/scenery/29.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-05-12</div><div class="title">MySQL实战 基础篇(三)</div></div></a></div><div><a href="/7044.html" title="MySQL实战 基础篇(五)"><img class="cover" src="/pics/scenery/28.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-07-02</div><div class="title">MySQL实战 基础篇(五)</div></div></a></div><div><a href="/55610.html" title="MySQL实战 基础篇(四)"><img class="cover" src="/pics/scenery/16.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-06-21</div><div class="title">MySQL实战 基础篇(四)</div></div></a></div><div><a href="/26960.html" title="MySQL常用"><img class="cover" src="/pics/scenery/29.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-08-17</div><div class="title">MySQL常用</div></div></a></div><div><a href="/51857.html" title="索引补充"><img class="cover" src="/pics/scenery/27.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-09-01</div><div class="title">索引补充</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#MySQL%E9%80%BB%E8%BE%91%E6%9E%B6%E6%9E%84%E5%9B%BE"><span class="toc-number">1.</span> <span class="toc-text">MySQL逻辑架构图</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><span class="toc-number">2.</span> <span class="toc-text">查询语句的执行过程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%9E%E6%8E%A5%E5%99%A8"><span class="toc-number">2.1.</span> <span class="toc-text">连接器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E8%AF%A2%E7%BC%93%E5%AD%98"><span class="toc-number">2.2.</span> <span class="toc-text">查询缓存</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E6%9E%90%E5%99%A8"><span class="toc-number">2.3.</span> <span class="toc-text">分析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BC%98%E5%8C%96%E5%99%A8"><span class="toc-number">2.4.</span> <span class="toc-text">优化器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A7%E8%A1%8C%E5%99%A8"><span class="toc-number">2.5.</span> <span class="toc-text">执行器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9B%B4%E6%96%B0%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><span class="toc-number">3.</span> <span class="toc-text">更新语句的执行过程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#redo-log"><span class="toc-number">3.1.</span> <span class="toc-text">redo log</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#binlog"><span class="toc-number">3.2.</span> <span class="toc-text">binlog</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4"><span class="toc-number">3.3.</span> <span class="toc-text">两阶段提交</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E7%BB%93"><span class="toc-number">3.4.</span> <span class="toc-text">小结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%97%AE%E9%A2%98"><span class="toc-number">4.</span> <span class="toc-text">问题</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#redo-log%E7%9A%84%E6%A6%82%E5%BF%B5%E6%98%AF%E4%BB%80%E4%B9%88-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E5%AD%98%E5%9C%A8"><span class="toc-number">4.1.</span> <span class="toc-text">redo log的概念是什么?为什么会存在</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFWAL-write-ahead-log-%E6%9C%BA%E5%88%B6-%E5%A5%BD%E5%A4%84%E6%98%AF%E4%BB%80%E4%B9%88"><span class="toc-number">4.2.</span> <span class="toc-text">什么是WAL(write-ahead log)机制, 好处是什么</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#redo-log-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%8F%AF%E4%BB%A5%E4%BF%9D%E8%AF%81crash-safe%E6%9C%BA%E5%88%B6"><span class="toc-number">4.3.</span> <span class="toc-text">redo log 为什么可以保证crash safe机制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#binlog%E7%9A%84%E6%A6%82%E5%BF%B5%E6%98%AF%E4%BB%80%E4%B9%88-%E8%B5%B7%E5%88%B0%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8-%E5%8F%AF%E4%BB%A5%E5%81%9Acrash-safe%E5%90%97"><span class="toc-number">4.4.</span> <span class="toc-text">binlog的概念是什么, 起到什么作用, 可以做crash safe吗?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#binlog%E5%92%8Credolog%E7%9A%84%E4%B8%8D%E5%90%8C%E7%82%B9%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="toc-number">4.5.</span> <span class="toc-text">binlog和redolog的不同点有哪些?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%89%A9%E7%90%86%E4%B8%80%E8%87%B4%E6%80%A7%E5%92%8C%E9%80%BB%E8%BE%91%E4%B8%80%E7%9B%B4%E6%80%A7%E5%90%84%E5%BA%94%E8%AF%A5%E6%80%8E%E4%B9%88%E7%90%86%E8%A7%A3"><span class="toc-number">4.6.</span> <span class="toc-text">物理一致性和逻辑一直性各应该怎么理解?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A7%E8%A1%8C%E5%99%A8%E5%92%8CinnoDB%E5%9C%A8%E6%89%A7%E8%A1%8Cupdate%E8%AF%AD%E5%8F%A5%E6%97%B6%E5%80%99%E7%9A%84%E6%B5%81%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88%E6%A0%B7%E7%9A%84"><span class="toc-number">4.7.</span> <span class="toc-text">执行器和innoDB在执行update语句时候的流程是什么样的?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E6%9E%9C%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AF%AF%E6%93%8D%E4%BD%9C-%E5%A6%82%E4%BD%95%E6%89%A7%E8%A1%8C%E6%95%B0%E6%8D%AE%E6%81%A2%E5%A4%8D"><span class="toc-number">4.8.</span> <span class="toc-text">如果数据库误操作, 如何执行数据恢复?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4-%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4%E6%80%8E%E4%B9%88%E4%BF%9D%E8%AF%81%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E4%B8%A4%E4%BB%BD%E6%97%A5%E5%BF%97%E9%97%B4%E7%9A%84%E9%80%BB%E8%BE%91%E4%B8%80%E8%87%B4%E6%80%A7-%E4%BB%80%E4%B9%88%E5%8F%AB%E9%80%BB%E8%BE%91%E4%B8%80%E8%87%B4%E6%80%A7"><span class="toc-number">4.9.</span> <span class="toc-text">什么是两阶段提交, 为什么需要两阶段提交, 两阶段提交怎么保证数据库中两份日志间的逻辑一致性(什么叫逻辑一致性)?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E6%9E%9C%E4%B8%8D%E6%98%AF%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4-%E5%85%88%E5%86%99redo-log%E5%92%8C%E5%85%88%E5%86%99bin-log%E4%B8%A4%E7%A7%8D%E6%83%85%E5%86%B5%E5%90%84%E4%BC%9A%E9%81%87%E5%88%B0%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98"><span class="toc-number">4.10.</span> <span class="toc-text">如果不是两阶段提交, 先写redo log和先写bin log两种情况各会遇到什么问题?</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%82%E8%80%83-14"><span class="toc-number">5.</span> <span class="toc-text">参考</span></a></li></ol></div></div></div></div></main><footer id="footer" style="background-image: url('/pics/scenery/06.png')"><div id="footer-wrap"><div class="copyright">©2021 - 2022 By huy</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div></div><hr/><div id="local-search-results"></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer="defer" id="ribbon" src="https://jsdelivr.pai233.top/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="false"></script><script id="click-heart" src="https://jsdelivr.pai233.top/npm/butterfly-extsrc@1/dist/click-heart.min.js" async="async" mobile="false"></script></div><!-- hexo injector body_end start --><script async src="//at.alicdn.com/t/font_2032782_8d5kxvn09md.js"></script><!-- hexo injector body_end end --></body></html>