From 33aa32f0f53f1999f16f05aa2d43978a564e4d8d Mon Sep 17 00:00:00 2001 From: SnailClimb Date: Fri, 29 Mar 2019 18:39:21 +0800 Subject: [PATCH] Update filename filecncoding --- ...鏉ql璇彞鍦╩ysql涓浣曟墽琛岀殑.md | 161 ++++++++++-------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/docs/database/涓鏉ql璇彞鍦╩ysql涓浣曟墽琛岀殑.md b/docs/database/涓鏉ql璇彞鍦╩ysql涓浣曟墽琛岀殑.md index 9f4bde24..076f4a26 100644 --- a/docs/database/涓鏉ql璇彞鍦╩ysql涓浣曟墽琛岀殑.md +++ b/docs/database/涓鏉ql璇彞鍦╩ysql涓浣曟墽琛岀殑.md @@ -1,130 +1,147 @@ + -本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。 +- [涓 MySQL 鍩虹鏋舵瀯鍒嗘瀽](#涓-mysql-鍩虹鏋舵瀯鍒嗘瀽) + - [1.1 MySQL 鍩烘湰鏋舵瀯姒傝](#11-mysql-鍩烘湰鏋舵瀯姒傝) + - [1.2 Server 灞傚熀鏈粍浠朵粙缁峕(#12-server-灞傚熀鏈粍浠朵粙缁) + - [1) 杩炴帴鍣╙(#1-杩炴帴鍣) + - [2) 鏌ヨ缂撳瓨(MySQL 8.0 鐗堟湰鍚庣Щ闄)](#2-鏌ヨ缂撳瓨mysql-80-鐗堟湰鍚庣Щ闄) + - [3) 鍒嗘瀽鍣╙(#3-鍒嗘瀽鍣) + - [4) 浼樺寲鍣╙(#4-浼樺寲鍣) + - [5) 鎵ц鍣╙(#5-鎵ц鍣) +- [浜 璇彞鍒嗘瀽](#浜-璇彞鍒嗘瀽) + - [2.1 鏌ヨ璇彞](#21-鏌ヨ璇彞) + - [2.2 鏇存柊璇彞](#22-鏇存柊璇彞) +- [涓 鎬荤粨](#涓-鎬荤粨) +- [鍥 鍙傝僝(#鍥-鍙傝) -在分析之前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成已经这些组件的作用是什么,可以帮助我们理解和解决这些问题。 + -## 一 MySQL 基础架构分析 +鏈瘒鏂囩珷浼氬垎鏋愪笅涓涓 sql 璇彞鍦 MySQL 涓殑鎵ц娴佺▼锛屽寘鎷 sql 鐨勬煡璇㈠湪 MySQL 鍐呴儴浼氭庝箞娴佽浆锛宻ql 璇彞鐨勬洿鏂版槸鎬庝箞瀹屾垚鐨勩 -### 1.1 MySQL 基本架构概览 +鍦ㄥ垎鏋愪箣鍓嶆垜浼氬厛甯︾潃浣犵湅鐪 MySQL 鐨勫熀纭鏋舵瀯锛岀煡閬撲簡 MySQL 鐢遍偅浜涚粍浠剁粍鎴愬凡缁忚繖浜涚粍浠剁殑浣滅敤鏄粈涔堬紝鍙互甯姪鎴戜滑鐞嗚В鍜岃В鍐宠繖浜涢棶棰樸 -下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。 +## 涓 MySQL 鍩虹鏋舵瀯鍒嗘瀽 -先简单介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图,在 1.2 节中会详细介绍到这些组件的作用。 +### 1.1 MySQL 鍩烘湰鏋舵瀯姒傝 -- **连接器:** 身份认证和权限相关(登录 MySQL 的时候)。 -- **查询缓存:** 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。 -- **分析器:** 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。 -- **优化器:** 按照 MySQL 认为最优的方案去执行。 -- **执行器:** 执行语句,然后从存储引擎返回数据。 +涓嬪浘鏄 MySQL 鐨勪竴涓畝瑕佹灦鏋勫浘锛屼粠涓嬪浘浣犲彲浠ュ緢娓呮櫚鐨勭湅鍒扮敤鎴风殑 SQL 璇彞鍦 MySQL 鍐呴儴鏄浣曟墽琛岀殑銆 + +鍏堢畝鍗曚粙缁嶄竴涓嬩笅鍥炬秹鍙婄殑涓浜涚粍浠剁殑鍩烘湰浣滅敤甯姪澶у鐞嗚В杩欏箙鍥撅紝鍦 1.2 鑺備腑浼氳缁嗕粙缁嶅埌杩欎簺缁勪欢鐨勪綔鐢ㄣ + +- **杩炴帴鍣細** 韬唤璁よ瘉鍜屾潈闄愮浉鍏(鐧诲綍 MySQL 鐨勬椂鍊)銆 +- **鏌ヨ缂撳瓨:** 鎵ц鏌ヨ璇彞鐨勬椂鍊欙紝浼氬厛鏌ヨ缂撳瓨锛圡ySQL 8.0 鐗堟湰鍚庣Щ闄わ紝鍥犱负杩欎釜鍔熻兘涓嶅お瀹炵敤锛夈 +- **鍒嗘瀽鍣:** 娌℃湁鍛戒腑缂撳瓨鐨勮瘽锛孲QL 璇彞灏变細缁忚繃鍒嗘瀽鍣紝鍒嗘瀽鍣ㄨ鐧戒簡灏辨槸瑕佸厛鐪嬩綘鐨 SQL 璇彞瑕佸共鍢涳紝鍐嶆鏌ヤ綘鐨 SQL 璇彞璇硶鏄惁姝g‘銆 +- **浼樺寲鍣細** 鎸夌収 MySQL 璁や负鏈浼樼殑鏂规鍘绘墽琛屻 +- **鎵ц鍣:** 鎵ц璇彞锛岀劧鍚庝粠瀛樺偍寮曟搸杩斿洖鏁版嵁銆 ![](https://user-gold-cdn.xitu.io/2019/3/23/169a8bc60a083849?w=950&h=1062&f=jpeg&s=38189) -简单来说 MySQL 主要分为 Server 层和存储引擎层: +绠鍗曟潵璇 MySQL 涓昏鍒嗕负 Server 灞傚拰瀛樺偍寮曟搸灞傦細 -- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。 -- **存储引擎**: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了。** +- **Server 灞**锛氫富瑕佸寘鎷繛鎺ュ櫒銆佹煡璇㈢紦瀛樸佸垎鏋愬櫒銆佷紭鍖栧櫒銆佹墽琛屽櫒绛夛紝鎵鏈夎法瀛樺偍寮曟搸鐨勫姛鑳介兘鍦ㄨ繖涓灞傚疄鐜帮紝姣斿瀛樺偍杩囩▼銆佽Е鍙戝櫒銆佽鍥撅紝鍑芥暟绛夛紝杩樻湁涓涓氱敤鐨勬棩蹇楁ā鍧 binglog 鏃ュ織妯″潡銆 +- **瀛樺偍寮曟搸**锛 涓昏璐熻矗鏁版嵁鐨勫瓨鍌ㄥ拰璇诲彇锛岄噰鐢ㄥ彲浠ユ浛鎹㈢殑鎻掍欢寮忔灦鏋勶紝鏀寔 InnoDB銆丮yISAM銆丮emory 绛夊涓瓨鍌ㄥ紩鎿庯紝鍏朵腑 InnoDB 寮曟搸鏈夎嚜鏈夌殑鏃ュ織妯″潡 redolog 妯″潡銆**鐜板湪鏈甯哥敤鐨勫瓨鍌ㄥ紩鎿庢槸 InnoDB锛屽畠浠 MySQL 5.5.5 鐗堟湰寮濮嬪氨琚綋鍋氶粯璁ゅ瓨鍌ㄥ紩鎿庝簡銆** -### 1.2 Server 层基本组件介绍 +### 1.2 Server 灞傚熀鏈粍浠朵粙缁 -#### 1) 连接器 +#### 1) 杩炴帴鍣 -连接器主要和身份认证和权限相关的功能相关,就好比一个级别很高的门卫一样。 +杩炴帴鍣ㄤ富瑕佸拰韬唤璁よ瘉鍜屾潈闄愮浉鍏崇殑鍔熻兘鐩稿叧锛屽氨濂芥瘮涓涓骇鍒緢楂樼殑闂ㄥ崼涓鏍枫 -主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。 +涓昏璐熻矗鐢ㄦ埛鐧诲綍鏁版嵁搴擄紝杩涜鐢ㄦ埛鐨勮韩浠借璇侊紝鍖呮嫭鏍¢獙璐︽埛瀵嗙爜锛屾潈闄愮瓑鎿嶄綔锛屽鏋滅敤鎴疯处鎴峰瘑鐮佸凡閫氳繃锛岃繛鎺ュ櫒浼氬埌鏉冮檺琛ㄤ腑鏌ヨ璇ョ敤鎴风殑鎵鏈夋潈闄愶紝涔嬪悗鍦ㄨ繖涓繛鎺ラ噷鐨勬潈闄愰昏緫鍒ゆ柇閮芥槸浼氫緷璧栨鏃惰鍙栧埌鐨勬潈闄愭暟鎹紝涔熷氨鏄锛屽悗缁彧瑕佽繖涓繛鎺ヤ笉鏂紑锛屽嵆鏃剁鐞嗗憳淇敼浜嗚鐢ㄦ埛鐨勬潈闄愶紝璇ョ敤鎴蜂篃鏄笉鍙楀奖鍝嶇殑銆 -#### 2) 查询缓存(MySQL 8.0 版本后移除) +#### 2) 鏌ヨ缂撳瓨(MySQL 8.0 鐗堟湰鍚庣Щ闄) -查询缓存主要用来缓存我们所执行的 SELECT 语句以及该语句的结果集。 +鏌ヨ缂撳瓨涓昏鐢ㄦ潵缂撳瓨鎴戜滑鎵鎵ц鐨 SELECT 璇彞浠ュ強璇ヨ鍙ョ殑缁撴灉闆嗐 -连接建立后,执行查询语句的时候,会先查询缓存,MySQL 会先校验这个 sql 是否执行过,以 Key-Value 的形式缓存在内存中,Key 是查询预计,Value 是结果集。如果缓存 key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。 +杩炴帴寤虹珛鍚庯紝鎵ц鏌ヨ璇彞鐨勬椂鍊欙紝浼氬厛鏌ヨ缂撳瓨锛孧ySQL 浼氬厛鏍¢獙杩欎釜 sql 鏄惁鎵ц杩囷紝浠 Key-Value 鐨勫舰寮忕紦瀛樺湪鍐呭瓨涓紝Key 鏄煡璇㈤璁★紝Value 鏄粨鏋滈泦銆傚鏋滅紦瀛 key 琚懡涓紝灏变細鐩存帴杩斿洖缁欏鎴风锛屽鏋滄病鏈夊懡涓紝灏变細鎵ц鍚庣画鐨勬搷浣滐紝瀹屾垚鍚庝篃浼氭妸缁撴灉缂撳瓨璧锋潵锛屾柟渚夸笅涓娆¤皟鐢ㄣ傚綋鐒跺湪鐪熸鎵ц缂撳瓨鏌ヨ鐨勬椂鍊欒繕鏄細鏍¢獙鐢ㄦ埛鐨勬潈闄愶紝鏄惁鏈夎琛ㄧ殑鏌ヨ鏉′欢銆 -MySQL 查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新的数据来说,使用缓存还是可以的。 +MySQL 鏌ヨ涓嶅缓璁娇鐢ㄧ紦瀛橈紝鍥犱负鏌ヨ缂撳瓨澶辨晥鍦ㄥ疄闄呬笟鍔″満鏅腑鍙兘浼氶潪甯搁绻侊紝鍋囧浣犲涓涓〃鏇存柊鐨勮瘽锛岃繖涓〃涓婄殑鎵鏈夌殑鏌ヨ缂撳瓨閮戒細琚竻绌恒傚浜庝笉缁忓父鏇存柊鐨勬暟鎹潵璇达紝浣跨敤缂撳瓨杩樻槸鍙互鐨勩 -所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。 +鎵浠ワ紝涓鑸湪澶у鏁版儏鍐典笅鎴戜滑閮芥槸涓嶆帹鑽愬幓浣跨敤鏌ヨ缂撳瓨鐨勩 -MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。 +MySQL 8.0 鐗堟湰鍚庡垹闄や簡缂撳瓨鐨勫姛鑳斤紝瀹樻柟涔熸槸璁や负璇ュ姛鑳藉湪瀹為檯鐨勫簲鐢ㄥ満鏅瘮杈冨皯锛屾墍浠ュ共鑴嗙洿鎺ュ垹鎺変簡銆 -#### 3) 分析器 +#### 3) 鍒嗘瀽鍣 -MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用来分析 SQL 语句是来干嘛的,分析器也会分为几步: +MySQL 娌℃湁鍛戒腑缂撳瓨锛岄偅涔堝氨浼氳繘鍏ュ垎鏋愬櫒锛屽垎鏋愬櫒涓昏鏄敤鏉ュ垎鏋 SQL 璇彞鏄潵骞插槢鐨勶紝鍒嗘瀽鍣ㄤ篃浼氬垎涓哄嚑姝ワ細 -**第一步,词法分析**,一条 SQL 语句有多个字符串组成,首先要提取关键字,比如 select,提出查询的表,提出字段名,提出查询条件等等。做完这些操作后,就会进入第二步。 +**绗竴姝ワ紝璇嶆硶鍒嗘瀽**锛屼竴鏉 SQL 璇彞鏈夊涓瓧绗︿覆缁勬垚锛岄鍏堣鎻愬彇鍏抽敭瀛楋紝姣斿 select锛屾彁鍑烘煡璇㈢殑琛紝鎻愬嚭瀛楁鍚嶏紝鎻愬嚭鏌ヨ鏉′欢绛夌瓑銆傚仛瀹岃繖浜涙搷浣滃悗锛屽氨浼氳繘鍏ョ浜屾銆 -**第二步,语法分析**,主要就是判断你输入的 sql 是否正确,是否符合 MySQL 的语法。 +**绗簩姝ワ紝璇硶鍒嗘瀽**锛屼富瑕佸氨鏄垽鏂綘杈撳叆鐨 sql 鏄惁姝g‘锛屾槸鍚︾鍚 MySQL 鐨勮娉曘 -完成这 2 步之后,MySQL 就准备开始执行了,但是如何执行,怎么执行是最好的结果呢?这个时候就需要优化器上场了。 +瀹屾垚杩 2 姝ヤ箣鍚庯紝MySQL 灏卞噯澶囧紑濮嬫墽琛屼簡锛屼絾鏄浣曟墽琛岋紝鎬庝箞鎵ц鏄渶濂界殑缁撴灉鍛紵杩欎釜鏃跺欏氨闇瑕佷紭鍖栧櫒涓婂満浜嗐 -#### 4) 优化器 +#### 4) 浼樺寲鍣 -优化器的作用就是它认为的最优的执行方案去执行(有时候可能也不是最优,这篇文章涉及对这部分知识的深入讲解),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。 +浼樺寲鍣ㄧ殑浣滅敤灏辨槸瀹冭涓虹殑鏈浼樼殑鎵ц鏂规鍘绘墽琛岋紙鏈夋椂鍊欏彲鑳戒篃涓嶆槸鏈浼橈紝杩欑瘒鏂囩珷娑夊強瀵硅繖閮ㄥ垎鐭ヨ瘑鐨勬繁鍏ヨ瑙o級锛屾瘮濡傚涓储寮曠殑鏃跺欒濡備綍閫夋嫨绱㈠紩锛屽琛ㄦ煡璇㈢殑鏃跺欏浣曢夋嫨鍏宠仈椤哄簭绛夈 -可以说,经过了优化器之后可以说这个语句具体该如何执行就已经定下来。 +鍙互璇达紝缁忚繃浜嗕紭鍖栧櫒涔嬪悗鍙互璇磋繖涓鍙ュ叿浣撹濡備綍鎵ц灏卞凡缁忓畾涓嬫潵銆 -#### 5) 执行器 +#### 5) 鎵ц鍣 -当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。 +褰撻夋嫨浜嗘墽琛屾柟妗堝悗锛孧ySQL 灏卞噯澶囧紑濮嬫墽琛屼簡锛岄鍏堟墽琛屽墠浼氭牎楠岃鐢ㄦ埛鏈夋病鏈夋潈闄愶紝濡傛灉娌℃湁鏉冮檺锛屽氨浼氳繑鍥為敊璇俊鎭紝濡傛灉鏈夋潈闄愶紝灏变細鍘昏皟鐢ㄥ紩鎿庣殑鎺ュ彛锛岃繑鍥炴帴鍙f墽琛岀殑缁撴灉銆 -## 二 语句分析 +## 浜 璇彞鍒嗘瀽 -### 2.1 查询语句 +### 2.1 鏌ヨ璇彞 -说了以上这么多,那么究竟一条 sql 语句是如何执行的呢?其实我们的 sql 可以分为 2 中,一种是查询,一种是更新(增加,更新,删除)。我们先分析下查询语句,语句如下: +璇翠簡浠ヤ笂杩欎箞澶氾紝閭d箞绌剁珶涓鏉 sql 璇彞鏄浣曟墽琛岀殑鍛紵鍏跺疄鎴戜滑鐨 sql 鍙互鍒嗕负 2 涓紝涓绉嶆槸鏌ヨ锛屼竴绉嶆槸鏇存柊锛堝鍔狅紝鏇存柊锛屽垹闄わ級銆傛垜浠厛鍒嗘瀽涓嬫煡璇㈣鍙ワ紝璇彞濡備笅锛 ```sql -select * from tb_student A where A.age='18' and A.name=' 张三 '; +select * from tb_student A where A.age='18' and A.name=' 寮犱笁 '; ``` -结合上面的说明,我们分析下这个语句的执行流程: +缁撳悎涓婇潰鐨勮鏄庯紝鎴戜滑鍒嗘瀽涓嬭繖涓鍙ョ殑鎵ц娴佺▼锛 -* 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。 -* 通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。 -* 接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案: +* 鍏堟鏌ヨ璇彞鏄惁鏈夋潈闄愶紝濡傛灉娌℃湁鏉冮檺锛岀洿鎺ヨ繑鍥為敊璇俊鎭紝濡傛灉鏈夋潈闄愶紝鍦 MySQL8.0 鐗堟湰浠ュ墠锛屼細鍏堟煡璇㈢紦瀛橈紝浠ヨ繖鏉 sql 璇彞涓 key 鍦ㄥ唴瀛樹腑鏌ヨ鏄惁鏈夌粨鏋滐紝濡傛灉鏈夌洿鎺ョ紦瀛橈紝濡傛灉娌℃湁锛屾墽琛屼笅涓姝ャ +* 閫氳繃鍒嗘瀽鍣ㄨ繘琛岃瘝娉曞垎鏋愶紝鎻愬彇 sql 璇彞鐨勫叧閿厓绱狅紝姣斿鎻愬彇涓婇潰杩欎釜璇彞鏄煡璇 select锛屾彁鍙栭渶瑕佹煡璇㈢殑琛ㄥ悕涓 tb_student,闇瑕佹煡璇㈡墍鏈夌殑鍒楋紝鏌ヨ鏉′欢鏄繖涓〃鐨 id='1'銆傜劧鍚庡垽鏂繖涓 sql 璇彞鏄惁鏈夎娉曢敊璇紝姣斿鍏抽敭璇嶆槸鍚︽纭瓑绛夛紝濡傛灉妫鏌ユ病闂灏辨墽琛屼笅涓姝ャ +* 鎺ヤ笅鏉ュ氨鏄紭鍖栧櫒杩涜纭畾鎵ц鏂规锛屼笂闈㈢殑 sql 璇彞锛屽彲浠ユ湁涓ょ鎵ц鏂规锛 - a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。 - b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。 - 那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。 + a.鍏堟煡璇㈠鐢熻〃涓鍚嶄负鈥滃紶涓夆濈殑瀛︾敓锛岀劧鍚庡垽鏂槸鍚﹀勾榫勬槸 18銆 + b.鍏堟壘鍑哄鐢熶腑骞撮緞 18 宀佺殑瀛︾敓锛岀劧鍚庡啀鏌ヨ濮撳悕涓衡滃紶涓夆濈殑瀛︾敓銆 + 閭d箞浼樺寲鍣ㄦ牴鎹嚜宸辩殑浼樺寲绠楁硶杩涜閫夋嫨鎵ц鏁堢巼鏈濂界殑涓涓柟妗堬紙浼樺寲鍣ㄨ涓猴紝鏈夋椂鍊欎笉涓瀹氭渶濂斤級銆傞偅涔堢‘璁や簡鎵ц璁″垝鍚庡氨鍑嗗寮濮嬫墽琛屼簡銆 -* 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。 +* 杩涜鏉冮檺鏍¢獙锛屽鏋滄病鏈夋潈闄愬氨浼氳繑鍥為敊璇俊鎭紝濡傛灉鏈夋潈闄愬氨浼氳皟鐢ㄦ暟鎹簱寮曟搸鎺ュ彛锛岃繑鍥炲紩鎿庣殑鎵ц缁撴灉銆 -### 2.2 更新语句 +### 2.2 鏇存柊璇彞 -以上就是一条查询 sql 的执行流程,那么接下来我们看看一条更新语句如何执行的呢?sql 语句如下: +浠ヤ笂灏辨槸涓鏉℃煡璇 sql 鐨勬墽琛屾祦绋嬶紝閭d箞鎺ヤ笅鏉ユ垜浠湅鐪嬩竴鏉℃洿鏂拌鍙ュ浣曟墽琛岀殑鍛紵sql 璇彞濡備笅锛 ``` -update tb_student A set A.age='19' where A.name=' 张三 '; +update tb_student A set A.age='19' where A.name=' 寮犱笁 '; ``` -我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 **binlog(归档日志)** ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 **redo log(重做日志)**,我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下: +鎴戜滑鏉ョ粰寮犱笁淇敼涓嬪勾榫勶紝鍦ㄥ疄闄呮暟鎹簱鑲畾涓嶄細璁剧疆骞撮緞杩欎釜瀛楁鐨勶紝涓嶇劧瑕佽鎶鏈礋璐d汉鎵撶殑銆傚叾瀹炴潯璇彞涔熷熀鏈笂浼氭部鐫涓婁竴涓煡璇㈢殑娴佺▼璧帮紝鍙笉杩囨墽琛屾洿鏂扮殑鏃跺欒偗瀹氳璁板綍鏃ュ織鍟︼紝杩欏氨浼氬紩鍏ユ棩蹇楁ā鍧椾簡锛孧ySQL 鑷甫鐨勬棩蹇楁ā鍧楀紡 **binlog锛堝綊妗f棩蹇楋級** 锛屾墍鏈夌殑瀛樺偍寮曟搸閮藉彲浠ヤ娇鐢紝鎴戜滑甯哥敤鐨 InnoDB 寮曟搸杩樿嚜甯︿簡涓涓棩蹇楁ā鍧 **redo log锛堥噸鍋氭棩蹇楋級**锛屾垜浠氨浠 InnoDB 妯″紡涓嬫潵鎺㈣杩欎釜璇彞鐨勬墽琛屾祦绋嬨傛祦绋嬪涓嬶細 -* 先查询到张三这一条数据,如果有缓存,也是会用到缓存。 -* 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。 -* 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。 -* 更新完成。 +* 鍏堟煡璇㈠埌寮犱笁杩欎竴鏉℃暟鎹紝濡傛灉鏈夌紦瀛橈紝涔熸槸浼氱敤鍒扮紦瀛樸 +* 鐒跺悗鎷垮埌鏌ヨ鐨勮鍙ワ紝鎶 age 鏀逛负 19锛岀劧鍚庤皟鐢ㄥ紩鎿 API 鎺ュ彛锛屽啓鍏ヨ繖涓琛屾暟鎹紝InnoDB 寮曟搸鎶婃暟鎹繚瀛樺湪鍐呭瓨涓紝鍚屾椂璁板綍 redo log锛屾鏃 redo log 杩涘叆 prepare 鐘舵侊紝鐒跺悗鍛婅瘔鎵ц鍣紝鎵ц瀹屾垚浜嗭紝闅忔椂鍙互鎻愪氦銆 +* 鎵ц鍣ㄦ敹鍒伴氱煡鍚庤褰 binlog锛岀劧鍚庤皟鐢ㄥ紩鎿庢帴鍙o紝鎻愪氦 redo log 涓烘彁浜ょ姸鎬併 +* 鏇存柊瀹屾垚銆 -这里肯定有同学会问,为什么要用两个日志模块,用一个日志模块不行吗? +杩欓噷鑲畾鏈夊悓瀛︿細闂紝涓轰粈涔堣鐢ㄤ袱涓棩蹇楁ā鍧楋紝鐢ㄤ竴涓棩蹇楁ā鍧椾笉琛屽悧锛 -这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。 +杩欐槸鍥犱负鏈寮濮 MySQL 骞舵病涓 InnoDB 寮曟搸( InnoDB 寮曟搸鏄叾浠栧叕鍙镐互鎻掍欢褰㈠紡鎻掑叆 MySQL 鐨) 锛孧ySQL 鑷甫鐨勫紩鎿庢槸 MyISAM锛屼絾鏄垜浠煡閬 redo log 鏄 InnoDB 寮曟搸鐗规湁鐨勶紝鍏朵粬瀛樺偍寮曟搸閮芥病鏈夛紝杩欏氨瀵艰嚧浼氭病鏈 crash-safe 鐨勮兘鍔(crash-safe 鐨勮兘鍔涘嵆浣挎暟鎹簱鍙戠敓寮傚父閲嶅惎锛屼箣鍓嶆彁浜ょ殑璁板綍閮戒笉浼氫涪澶)锛宐inlog 鏃ュ織鍙兘鐢ㄦ潵褰掓。銆 -并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做? +骞朵笉鏄鍙敤涓涓棩蹇楁ā鍧椾笉鍙互锛屽彧鏄 InnoDB 寮曟搸灏辨槸閫氳繃 redo log 鏉ユ敮鎸佷簨鍔$殑銆傞偅涔堬紝鍙堜細鏈夊悓瀛﹂棶锛屾垜鐢ㄤ袱涓棩蹇楁ā鍧楋紝浣嗘槸涓嶈杩欎箞澶嶆潅琛屼笉琛岋紝涓轰粈涔 redo log 瑕佸紩鍏 prepare 棰勬彁浜ょ姸鎬侊紵杩欓噷鎴戜滑鐢ㄥ弽璇佹硶鏉ヨ鏄庝笅涓轰粈涔堣杩欎箞鍋氾紵 -* **先写 redo log 直接提交,然后写 binlog**,假设写完 redo log 后,机器挂了,binlog 日志没有被写入,那么机器重启后,这台机器会通过 redo log 恢复数据,但是这个时候 bingog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。 -* **先写 binlog,然后写 redo log**,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。 +* **鍏堝啓 redo log 鐩存帴鎻愪氦锛岀劧鍚庡啓 binlog**锛屽亣璁惧啓瀹 redo log 鍚庯紝鏈哄櫒鎸備簡锛宐inlog 鏃ュ織娌℃湁琚啓鍏ワ紝閭d箞鏈哄櫒閲嶅惎鍚庯紝杩欏彴鏈哄櫒浼氶氳繃 redo log 鎭㈠鏁版嵁锛屼絾鏄繖涓椂鍊 bingog 骞舵病鏈夎褰曡鏁版嵁锛屽悗缁繘琛屾満鍣ㄥ浠界殑鏃跺欙紝灏变細涓㈠け杩欎竴鏉℃暟鎹紝鍚屾椂涓讳粠鍚屾涔熶細涓㈠け杩欎竴鏉℃暟鎹 +* **鍏堝啓 binlog锛岀劧鍚庡啓 redo log**锛屽亣璁惧啓瀹屼簡 binlog锛屾満鍣ㄥ紓甯搁噸鍚簡锛岀敱浜庢病鏈 redo log锛屾湰鏈烘槸鏃犳硶鎭㈠杩欎竴鏉¤褰曠殑锛屼絾鏄 binlog 鍙堟湁璁板綍锛岄偅涔堝拰涓婇潰鍚屾牱鐨勯亾鐞嗭紝灏变細浜х敓鏁版嵁涓嶄竴鑷寸殑鎯呭喌銆 -如果采用 redo log 两阶段提交的方式就不一样了,写完 binglog 后,然后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设 redo log 处于预提交状态,binglog 也已经写完了,这个时候发生了异常重启会怎么样呢? -这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程如下: +濡傛灉閲囩敤 redo log 涓ら樁娈垫彁浜ょ殑鏂瑰紡灏变笉涓鏍蜂簡锛屽啓瀹 binglog 鍚庯紝鐒跺悗鍐嶆彁浜 redo log 灏变細闃叉鍑虹幇涓婅堪鐨勯棶棰橈紝浠庤屼繚璇佷簡鏁版嵁鐨勪竴鑷存с傞偅涔堥棶棰樻潵浜嗭紝鏈夋病鏈変竴涓瀬绔殑鎯呭喌鍛紵鍋囪 redo log 澶勪簬棰勬彁浜ょ姸鎬侊紝binglog 涔熷凡缁忓啓瀹屼簡锛岃繖涓椂鍊欏彂鐢熶簡寮傚父閲嶅惎浼氭庝箞鏍峰憿锛 +杩欎釜灏辫渚濊禆浜 MySQL 鐨勫鐞嗘満鍒朵簡锛孧ySQL 鐨勫鐞嗚繃绋嬪涓嬶細 -* 判断 redo log 是否完整,如果判断是完整的,就立即提交。 -* 如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。 +* 鍒ゆ柇 redo log 鏄惁瀹屾暣锛屽鏋滃垽鏂槸瀹屾暣鐨勶紝灏辩珛鍗虫彁浜ゃ +* 濡傛灉 redo log 鍙槸棰勬彁浜や絾涓嶆槸 commit 鐘舵侊紝杩欎釜鏃跺欏氨浼氬幓鍒ゆ柇 binlog 鏄惁瀹屾暣锛屽鏋滃畬鏁村氨鎻愪氦 redo log, 涓嶅畬鏁村氨鍥炴粴浜嬪姟銆 -这样就解决了数据一致性的问题。 +杩欐牱灏辫В鍐充簡鏁版嵁涓鑷存х殑闂銆 -## 三 总结 +## 涓 鎬荤粨 -* MySQL 主要分为 Server 曾和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。 -* 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。 -* SQL 等执行过程分为两类,一类对于查询等过程如下:权限校验---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎 -* 对于更新等语句执行流程如下:分析器----》权限校验----》执行器---》引擎---redo log prepare---》binlog---》redo log commit +* MySQL 涓昏鍒嗕负 Server 鏇惧拰寮曟搸灞傦紝Server 灞備富瑕佸寘鎷繛鎺ュ櫒銆佹煡璇㈢紦瀛樸佸垎鏋愬櫒銆佷紭鍖栧櫒銆佹墽琛屽櫒锛屽悓鏃惰繕鏈変竴涓棩蹇楁ā鍧楋紙binlog锛夛紝杩欎釜鏃ュ織妯″潡鎵鏈夋墽琛屽紩鎿庨兘鍙互鍏辩敤,redolog 鍙湁 InnoDB 鏈夈 +* 寮曟搸灞傛槸鎻掍欢寮忕殑锛岀洰鍓嶄富瑕佸寘鎷紝MyISAM,InnoDB,Memory 绛夈 +* SQL 绛夋墽琛岃繃绋嬪垎涓轰袱绫伙紝涓绫诲浜庢煡璇㈢瓑杩囩▼濡備笅锛氭潈闄愭牎楠---銆嬫煡璇㈢紦瀛---銆嬪垎鏋愬櫒---銆嬩紭鍖栧櫒---銆嬫潈闄愭牎楠---銆嬫墽琛屽櫒---銆嬪紩鎿 +* 瀵逛簬鏇存柊绛夎鍙ユ墽琛屾祦绋嬪涓嬶細鍒嗘瀽鍣----銆嬫潈闄愭牎楠----銆嬫墽琛屽櫒---銆嬪紩鎿---redo log prepare---銆媌inlog---銆媟edo log commit -## 四 参考 +## 鍥 鍙傝 -* 《一起构建 MySQL 知识网络》 -* MySQL 5.6参考手册: \ No newline at end of file +* 銆婁竴璧锋瀯寤 MySQL 鐭ヨ瘑缃戠粶銆 +* MySQL 5.6鍙傝冩墜鍐: \ No newline at end of file