在今天这篇答疑文章更新前,MySQL 实战这个专栏已经更新了 14 篇。在这些文章中,你们在评论区留下了不少高质量的留言。如今,每篇文章的评论区都有热心的同窗帮忙总结文章知识点,也有很多同窗提出了不少高质量的问题,更有一些同窗帮忙解答其余同窗提出的问题。mysql
在浏览这些留言并回复的过程当中,我倍受鼓舞,也尽我所知地帮助你解决问题、和你讨论。能够说,大家的留言活跃了整个专栏的氛围、提高了整个专栏的质量,谢谢大家。评论区的大多数留言我都直接回复了,对于须要展开说明的问题,我都拿出小本子记了下来。这些被记下来的问题,就是咱们今天这篇答疑文章的素材了。sql
到目前为止,我已经收集了 47 个问题,很难经过今天这一篇文章所有展开。因此,我就先从中找了几个联系很是紧密的问题,串了起来,但愿能够帮你解决关于日志和索引的一些疑惑。而其余问题,咱们就留着后面慢慢展开吧。数据库
我在第 2 篇文章《日志系统:一条 SQL 更新语句是如何执行的?》中,和你讲到binlog(归档日志)和 redo log(重作日志)配合崩溃恢复的时候,用的是反证法,说明
了若是没有两阶段提交,会致使 MySQL 出现主备数据不一致等问题。缓存
在这篇文章下面,不少同窗在问,在两阶段提交的不一样瞬间,MySQL 若是发生异常重启,是怎么保证数据完整性的?bash
如今,咱们就从这个问题开始吧。session
我再放一次两阶段提交的图,方便你学习下面的内容。架构
图 1 两阶段提交示意图并发
这里,我要先和你解释一个误会式的问题。有同窗在评论区问到,这个图不是一个update 语句的执行流程吗,怎么还会调用 commit 语句?数据库设计
他产生这个疑问的缘由,是把两个“commit”的概念混淆了:分布式
而咱们这个例子里面,没有显式地开启事务,所以这个 update 语句本身就是一个事务,在执行完成后提交事务时,就会用到这个“commit 步骤“。
接下来,咱们就一块儿分析一下在两阶段提交的不一样时刻,MySQL 异常重启会出现什么现象
若是在图中时刻 A 的地方,也就是写入 redo log 处于 prepare 阶段以后、写 binlog 以前,发生了崩溃(crash),因为此时 binlog 还没写,redo log 也还没提交,因此崩溃恢复的时候,这个事务会回滚。这时候,binlog 还没写,因此也不会传到备库。到这里,你们均可以理解。
你们出现问题的地方,主要集中在时刻 B,也就是 binlog 写完,redo log 还没 commit前发生 crash,那崩溃恢复的时候 MySQL 会怎么处理?
咱们先来看一下崩溃恢复时的判断规则。
1. 若是 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
2. 若是 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
a. 若是是,则提交事务;
b. 不然,回滚事务。
这里,时刻 B 发生 crash 对应的就是 2(a) 的状况,崩溃恢复过程当中事务会被提交。
如今,咱们继续延展一下这个问题。
追问 1:MySQL 怎么知道 binlog 是完整的?
回答:一个事务的 binlog 是有完整格式的:
statement 格式的 binlog,最后会有 COMMIT; row 格式的 binlog,最后会有一个 XID event。
另外,在 MySQL 5.6.2 版本之后,还引入了 binlog-checksum 参数,用来验证 binlog内容的正确性。对于 binlog 日志因为磁盘缘由,可能会在日志中间出错的状况,MySQL能够经过校验 checksum 的结果来发现。因此,MySQL 仍是有办法验证事务 binlog 的完整性的。
追问2:redo log 和 binlog 是怎么关联起来的?
回答:它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:
追问 3:处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢复,MySQL 为何要这么设计?
回答:其实,两阶段提交是经典的分布式系统问题,并非 MySQL 独有的。若是必需要举一个场景,来讲明这么作的必要性的话,那就是事务的持久性问题。
回答:其实,这个问题仍是跟咱们在反证法中说到的数据与备份的一致性有关。在时刻B,也就是 binlog 写完之后 MySQL 发生崩溃,这时候 binlog 已经写入了,以后就会被从库(或者用这个 binlog 恢复出来的库)使用。
因此,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性
追问 4:若是这样的话,为何还要两阶段提交呢?干脆先 redo log 写完,再写 binlog。崩溃恢复的时候,必须得两个日志都完整才能够。是否是同样的逻辑?
回答:其实,两阶段提交是经典的分布式系统问题,并非 MySQL 独有的。
若是必需要举一个场景,来讲明这么作的必要性的话,那就是事务的持久性问题。
对于 InnoDB 引擎来讲,若是 redo log 提交完成了,事务就不能回滚(若是这还容许回滚,就可能覆盖掉别的事务的更新)。而若是 redo log 直接提交,而后 binlog 写入的
两阶段提交就是为了给全部人一个机会,当每一个人都说“我 ok”的时候,再一块儿提交。
追问 5:不引入两个日志,也就没有两阶段提交的必要了。只用 binlog 来支持崩溃恢复,又能支持归档,不就能够了?
回答:这位同窗的意思是,只保留 binlog,而后能够把提交流程改为这样:… -> “数据更新到内存” -> “写 binlog” -> “提交事务”,是否是也能够提供崩溃恢复的能力?
答案是不能够。
若是说历史缘由的话,那就是 InnoDB 并非 MySQL 的原生存储引擎。MySQL 的原生引擎是 MyISAM,设计之初就有没有支持崩溃恢复。
InnoDB 在做为 MySQL 的插件加入 MySQL 引擎家族以前,就已是一个提供了崩溃恢复和事务支持的引擎了。
InnoDB 接入了 MySQL 后,发现既然 binlog 没有崩溃恢复的能力,那就用 InnoDB 原有的 redo log 好了。
而若是说实现上的缘由的话,就有不少了。就按照问题中说的,只用 binlog 来实现崩溃恢复的流程,我画了一张示意图,这里就没有 redo log 了。
图 2 只用 binlog 支持崩溃恢复
这样的流程下,binlog 仍是不能支持崩溃恢复的。我说一个不支持的点吧:binlog 没有能力恢复“数据页”。
若是在图中标的位置,也就是 binlog2 写完了,可是整个事务尚未 commit 的时候,MySQL 发生了 crash。
重启后,引擎内部事务 2 会回滚,而后应用 binlog2 能够补回来;可是对于事务 1 来讲,系统已经认为提交完成了,不会再应用一次 binlog1。
可是,InnoDB 引擎使用的是 WAL 技术,执行事务的时候,写完内存和日志,事务就算完成了。若是以后崩溃,要依赖于日志来恢复数据页。
也就是说在图中这个位置发生崩溃的话,事务 1 也是可能丢失了的,并且是数据页级的丢失。此时,binlog 里面并无记录数据页的更新细节,是补不回来的。
你若是要说,那我优化一下 binlog 的内容,让它来记录数据页的更改能够吗?但,这其实就是又作了一个 redo log 出来。
因此,至少如今的 binlog 能力,还不能支持崩溃恢复。
追问 6:那能不能反过来,只用 redo log,不要 binlog?
回答:若是只从崩溃恢复的角度来说是能够的。你能够把 binlog 关掉,这样就没有两阶段提交了,但系统依然是 crash-safe 的。
可是,若是你了解一下业界各个公司的使用场景的话,就会发如今正式的生产库上,binlog 都是开着的。由于 binlog 有着 redo log 没法替代的功能。
一个是归档。redo log 是循环写,写到末尾是要回到开头继续写的。这样历史日志无法保留,redo log 也就起不到归档的做用。
一个就是 MySQL 系统依赖于 binlog。binlog 做为 MySQL 一开始就有的功能,被用在了不少地方。其中,MySQL 系统高可用的基础,就是 binlog 复制。
还有不少公司有异构系统(好比一些数据分析系统),这些系统就靠消费 MySQL 的binlog 来更新本身的数据。关掉 binlog 的话,这些下游系统就无法输入了。
总之,因为如今包括 MySQL 高可用在内的不少系统机制都依赖于 binlog,因此“鸠占鹊巢”redo log 还作不到。你看,发展生态是多么重要。
追问 7:redo log 通常设置多大?
回答:redo log 过小的话,会致使很快就被写满,而后不得不强行刷 redo log,这样WAL 机制的能力就发挥不出来了。
因此,若是是如今常见的几个 TB 的磁盘的话,就不要过小气了,直接将 redo log 设置为4 个文件、每一个文件 1GB 吧。
追问 8:正常运行中的实例,数据写入后的最终落盘,是从 redo log 更新过来的仍是从 buffer pool 更新过来的呢?
回答:这个问题其实问得很是好。这里涉及到了,“redo log 里面究竟是什么”的问题。
实际上,redo log 并无记录数据页的完整数据,因此它并无能力本身去更新磁盘数据页,也就不存在“数据最终落盘,是由 redo log 更新过去”的状况。
1. 若是是正常运行的实例的话,数据页被修改之后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与 redo log 毫无关系。
2. 在崩溃恢复场景中,InnoDB 若是判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,而后让 redo log 更新内存内容。更新完成后,内存页变成脏页,就回到了第一种状况的状态。
追问 9:redo log buffer 是什么?是先修改内存,仍是先写 redo log文件?
回答:这两个问题能够一块儿回答。
在一个事务的更新过程当中,日志是要写屡次的。好比下面这个事务:
begin; insert into t1 ... insert into t2 ... commit;
这个事务要往两个表中插入记录,插入数据的过程当中,生成的日志都得先保存起来,但又不能在还没 commit 的时候就直接写到 redo log 文件里。
因此,redo log buffer 就是一块内存,用来先存 redo 日志的。也就是说,在执行第一个insert 的时候,数据的内存被修改了,redo log buffer 也写入了日志。
可是,真正把日志写到 redo log 文件(文件名是 ib_logfile+ 数字),是在执行 commit语句的时候作的。
(这里说的是事务执行过程当中不会“主动去刷盘”,以减小没必要要的 IO 消耗。可是可能会出现“被动写入磁盘”,好比内存不够、其余事务提交等状况。这个问题咱们会在后面第 22 篇文章《MySQL 有哪些“饮鸩止渴”的提升性能的方法?》中再详细展开)。
单独执行一个更新语句的时候,InnoDB 会本身启动一个事务,在语句执行完成的时候提交。过程跟上面是同样的,只不过是“压缩”到了一个语句里面完成。
以上这些问题,就是把你们提过的关于 redo log 和 binlog 的问题串起来,作的一次集中回答。若是你还有问题,能够在评论区继续留言补充。
接下来,我再和你分享 @ithunter 同窗在第 8 篇文章《事务究竟是隔离的仍是不隔离的?》的评论区提到的跟索引相关的一个问题。我以为这个问题挺有趣、也挺实用的,其余同窗也可能会碰上这样的场景,在这里解答和分享一下。
问题是这样的(我文字上稍微作了点修改,方便你们理解):
业务上有这样的需求,A、B 两个用户,若是互相关注,则成为好友。设计上是有两张表,一个是 like 表,一个是 friend 表,like 表有 user_id、liker_id 两个字段,我设置为复合惟一索引即 uk_user_id_liker_id。语句执行逻辑是这样的: 以 A 关注 B 为例: 第一步,先查询对方有没有关注本身(B 有没有关注 A) select * from like where user_id = B and liker_id = A; 若是有,则成为好友 insert into friend; 没有,则只是单向关注关系 insert into like; 可是若是 A、B 同时关注对方,会出现不会成为好友的状况。由于上面第 1步,双方都没关注对方。第 1 步即便使用了排他锁也不行,由于记录不存在,行锁没法生效。请问这种状况,在 MySQL 锁层面有没有办法处理?
首先,我要先赞一下这样的提问方式。虽然极客时间如今的评论区还不能追加评论,但若是你们可以一次留言就把问题讲清楚的话,其实影响也不大。因此,我但愿你在留言提问的时候,也能借鉴这种方式。
接下来,我把 @ithunter 同窗说的表模拟出来,方便咱们讨论。
CREATE TABLE `like` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `liker_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_id_liker_id` (`user_id`,`liker_id`) ) ENGINE=InnoDB; CREATE TABLE `friend` ( id` int(11) NOT NULL AUTO_INCREMENT, `friend_1_id` int(11) NOT NULL, `firned_2_id` int(11) NOT NULL, UNIQUE KEY `uk_friend` (`friend_1_id`,`firned_2_id`) PRIMARY KEY (`id`) ) ENGINE=InnoDB;
虽然这个题干中,并无说到 friend 表的索引结构。但我猜想 friend_1_id 和friend_2_id 也有索引,为便于描述,我给加上惟一索引。
顺便说明一下,“like”是关键字,我通常不建议使用关键字做为库名、表名、字段名或索引名。
我把他的疑问翻译一下,在并发场景下,同时有两我的,设置为关注对方,就可能致使没法成功加为朋友关系。
如今,我用你已经熟悉的时刻顺序表的形式,把这两个事务的执行语句列出来:
图 3 并发“喜欢”逻辑操做顺序
因为一开始 A 和 B 之间没有关注关系,因此两个事务里面的 select 语句查出来的结果都是空。
所以,session 1 的逻辑就是“既然 B 没有关注 A,那就只插入一个单向关注关系”。session 2 也一样是这个逻辑。
这个结果对业务来讲就是 bug 了。由于在业务设定里面,这两个逻辑都执行完成之后,是应该在 friend 表里面插入一行记录的。
如提问里面说的,“第 1 步即便使用了排他锁也不行,由于记录不存在,行锁没法生效”。不过,我想到了另一个方法,来解决这个问题。
首先,要给“like”表增长一个字段,好比叫做 relation_ship,并设为整型,取值 一、二、3。
值是 1 的时候,表示 user_id 关注 liker_id; 值是 2 的时候,表示 liker_id 关注 user_id; 值是 3 的时候,表示互相关注
而后,当 A 关注 B 的时候,逻辑改为以下所示的样子:
应用代码里面,比较 A 和 B 的大小,若是 A<B,就执行下面的逻辑
mysql> begin; /* 启动事务 */ insert into `like`(user_id, liker_id, relation_ship) values(A, B, 1) on duplicate key update relation_ship=relation_ship | 1; select relation_ship from `like` where user_id=A and liker_id=B; /* 代码中判断返回的 relation_ship, 若是是 1,事务结束,执行 commit 若是是 3,则执行下面这两个语句: */ insert ignore into friend(friend_1_id, friend_2_id) values(A,B); commit;
若是 A>B,则执行下面的逻辑
mysql> begin; /* 启动事务 */ insert into `like`(user_id, liker_id, relation_ship) values(B, A, 2) on duplicate key update relation_ship=relation_ship | 2; select relation_ship from `like` where user_id=B and liker_id=A; /* 代码中判断返回的 relation_ship, 若是是 2,事务结束,执行 commit 若是是 3,则执行下面这两个语句: */ insert ignore into friend(friend_1_id, friend_2_id) values(B,A); commit;
这个设计里,让“like”表里的数据保证 user_id < liker_id,这样不管是 A 关注 B,仍是B 关注 A,在操做“like”表的时候,若是反向的关系已经存在,就会出现行锁冲突。
而后,insert … on duplicate 语句,确保了在事务内部,执行了这个 SQL 语句后,就强行占住了这个行锁,以后的 select 判断 relation_ship 这个逻辑时就确保了是在行锁保护下的读操做。
操做符 “|” 是按位或,连同最后一句 insert 语句里的 ignore,是为了保证重复调用时的幂等性。
这样,即便在双方“同时”执行关注操做,最终数据库里的结果,也是 like 表里面有一条关于 A 和 B 的记录,并且 relation_ship 的值是 3, 而且 friend 表里面也有了 A 和 B 的这条记录。
不知道你会不会吐槽:以前明明还说尽可能不要使用惟一索引,结果这个例子一上来我就建立了两个。这里我要再和你说明一下,以前文章咱们讨论的,是在“业务开发保证不会插入重复记录”的状况下,着重要解决性能问题的时候,才建议尽可能使用普通索引。
而像这个例子里,按照这个设计,业务根本就是保证“我必定会插入重复数据,数据库必定要要有惟一性约束”,这时就没啥好说的了,惟一索引建起来吧。
这是专栏的第一篇答疑文章。
我针对前 14 篇文章,你们在评论区中的留言,从中摘取了关于日志和索引的相关问题,串成了今天这篇文章。这里我也要再和你说一声,有些我答应在答疑文章中进行扩展的话题,今天这篇文章没来得及扩展,后续我会再找机会为你解答。因此,篇幅所限,评论区见吧。
最后,虽然这篇是答疑文章,但课后问题仍是要有的。
咱们建立了一个简单的表 t,并插入一行,而后对这一行作修改。
mysql> CREATE TABLE `t` ( `id` int(11) NOT NULL primary key auto_increment, `a` int(11) DEFAULT NULL ) ENGINE=InnoDB; insert into t values(1,2);
这时候,表 t 里有惟一的一行数据 (1,2)。假设,我如今要执行:
mysql> update t set a=2 where id=1;
你会看到这样的结果:
结果显示,匹配 (rows matched) 了一行,修改 (Changed) 了 0 行。
仅从现象上看,MySQL 内部在处理这个命令的时候,能够有如下三种选择:
1. 更新都是先读后写的,MySQL 读出数据,发现 a 的值原本就是 2,不更新,直接返回,执行结束;
2. MySQL 调用了 InnoDB 引擎提供的“修改成 (1,2)”这个接口,可是引擎发现值与原来相同,不更新,直接返回;
3. InnoDB 认真执行了“把这个值修改为 (1,2)"这个操做,该加锁的加锁,该更新的更新。
你以为实际状况会是以上哪一种呢?你能否用构造实验的方式,来证实你的结论?进一步地,能够思考一下,MySQL 为何要选择这种策略呢?
你能够把你的验证方法和思考写在留言区里,我会在下一篇文章的末尾和你讨论这个问题。感谢你的收听,也欢迎你把这篇文章分享给更多的朋友一块儿阅读。
上期的问题是,用一个计数表记录一个业务表的总行数,在往业务表插入数据的时候,须要给计数值加 1。
逻辑实现上是启动一个事务,执行两个语句:
1. insert into 数据表; 2. update 计数表,计数值加 1。
从系统并发能力的角度考虑,怎么安排这两个语句的顺序。
这里,我直接复制 @阿建 的回答过来供你参考:
并发系统性能的角度考虑,应该先插入操做记录,再更新计数表。知识点在《行锁功过:怎么减小行锁对性能的影响?》
由于更新计数表涉及到行锁的竞争,先插入再更新能最大程度地减小事务之间的锁等待,提高并发度。
评论区有同窗说,应该把 update 计数表放后面,由于这个计数表可能保存了多个业务表的计数值。若是把 update 计数表放到事务的第一个语句,多个业务表同时插入数据的话,等待时间会更长。
这个答案的结论是对的,可是理解不太正确。即便咱们用一个计数表记录多个业务表的行数,也确定会给表名字段加惟一索引。相似于下面这样的表结构:
在更新计数表的时候,必定会传入 where table_name=$table_name,使用主键索引,更新加行锁只会锁在一行上。
而在不一样业务表插入数据,是更新不一样的行,不会有行锁。
孔乙己:孔乙己来到酒馆大喊一声老板来二两酒赊着,酒馆生意太好,老板把孔乙己的欠帐记录记到小黑板上并记录了孔乙己点的菜单。
孔乙己跟别人吹了会牛,忘了叫的几两酒了。又给老板说,老板把酒改为二两。
老板也不肯定孔乙己叫没叫酒,就去查菜单,发现孔乙己确实点了酒,可是原本就二两,也就可贵麻烦了,又要修改小黑板,又要改菜单。
直接就给孔乙己说已经改好了
老板看完板:正要告知孔乙己今日总帐是赊帐二两酒,
小二连忙过来拦住:“老板,刚刚孔乙己刚又赊帐了一碟茴香豆。”
老板大惊:“差点亏了我一碟豆子!我怎不知?”
小二道:“老板你方才看板的之时没拿记帐笔,我看记帐笔没人使用,按店规天然可用。老板你本身没看”
老板惊呼,“亏的你当心”。
暗地想店规确有不妥。
因而把店规“变帐须用记帐笔。” 改成
“改账均须动笔。纵为不变之账,仍需覆写之”
看到本身的问题上榜,这是对本身的最大鼓励。
学习专栏以前,本身只是一个 CRUD boy,平时同事间讨论 MySQL 的问题,本身彻底搭不上话,由于对 MySQL 底层原理彻底不懂。对 MySQL 的认知就仅限一点:索引能提升查询效率。可是为何能提升?不知道!!
如今回想,之前犯过不少错误:
1. 主键使用 UUID,非自增主键。
2. 滥用索引,其实能够经过“最左前缀原则”来精减索引。
3. 无论 SQL 语句是否合理,只要能返回结果集就是好 SQL。
4. 建表时字段类型拿捏不许。
如今都会反复学习专栏的每一篇文章,每次学习都有不同的收获。
第一次多是:喔,原来有这么个知识点,但对它的实现原理只知其一;不知其二。
第二次倒是:对它的实现原理有了更深的认识,增强对知识的理解,基本会造成一个比较清晰的逻辑。
第三次是,MySQL 的这种实现原理,是为了解决什么问题等等。
如今感受有点“走火入魔”了,之前执行查询语句,关注的多久能返回结果集。
如今关注的倒是:链接器、分析器、优化器、执行器和 InnoDB 引擎。
链接成功后,获取个人权限,查询缓存,命中缓存直接返回,不然进行后续的操做。(记得老师留言区回复过:链接器取权限,执行器用权限。而编写留言到这产生了一个疑问:查询缓存前,应该会校验权限,因此链接器也会用权限?)
分析器阶段进行词法分析,解析关键字,字段名,表名等。语法分析判断语法是否正确。(记得第一篇《基础架构》留言提到语义分析,今晚要找资料学习下)。
优化器阶段生成执行计划,选择索引(这时会怀疑 MySQL 选择的索引是否最优),可否使用索引下推和覆盖索引减小回表查询,提升性能。
执行器阶段调用引擎接口查询数据,Server 层要啥,引擎给啥,InnoDB 只给必要的值。
查询结束后,返回结果集,并将结果集放入查询缓存。
更新语句的关注点是隔离性,视图,MVCC,回滚日志,redo log,binlog,两阶段提交等。
写业务代码时,会考虑事务内的 SQL 语句,可否调整 SQL 语句的顺序,减小更新表时行锁对性能的影响。
在建表的时,会反复推敲这个索引是否合理。使用普通索引仍是惟一索引更为合适。可否经过“最左前缀原则”来减小建立索引的个数。若是索引字段的类型是字符串并长度太长,如何优化使用前缀索引,减小空间占用,提升查询性能。
学习专栏后,基本上涉及到 MySQL 的内容,这些知识点都会浮如今脑海中。昨天还差点应用这些知识,帮同事优化他的 SQL 语句。昨天跟往常同样,当写代码写累了,就跑到同事那溜达溜达。
他正在线上的备库测试查询百万数据要多久,另外一位同事建议他使用 force index 强制索引,此次执行 5 秒,再执行零点几秒。
他惊乎,为啥此次这么快。我说,此次查了缓存。我还想帮他看看 SQL 语句,是否 MySQL 选择错了索引,致使使用 force index 显式指定索引。说不定使用 order by field 就解决了呢,哈哈哈哈。后面有事,没有继续跟进他这问题了。
很是感恩,跟着老师学习,让我体会到了学习是一件天然而又充满魅力的事情,也让我从一个基础不牢固的小白,一步步地充实了本身的知识库,另外老师很是尽责,常常半夜回复答疑,但愿老师保重身体。谢谢!!
做者回复: “我说,此次查了缓存”
哈哈,这个场景好棒,这个画面感,有一种扫地僧的感受👍🏿
一块儿加油
林老师的每次更新我都会跟着看 跟着学 已经坚持15节课了 受益良多 只是内心有时会反问本身 底层原理有那么重要吗? 会用不就好了吗?
本身不知道该怎么推翻这些想法 加上本身有个很差的习惯 就是容易放弃 但愿本身可以坚持到最后。
做者回复: 加油。
说下我本身的理解。
我在带新人的时候,要求你们在写SQL语句的时候,内心是有数的,知道每一个语句执行的结果,以及这些代码会消耗什么资源、若是慢了会慢在哪里、每一个语句执行会占用哪些锁等等。
有的新人会问“为何须要这么麻烦,我执行一下,看看结果对不对,对了就行,不对就改,是否是也能够?”
我说不能够。由于若是这样,咱们就会受到不少局限,即便咱们定位本身是业务开发人员。
这里我说一个限制:
这会限制基于数据库的业务架构能力。一个语句能够试,一个五个语句的事务分析就要试不少次,一个复杂业务系统的数据库设计,是试不出来的。
原理能够帮咱们剪枝,排除掉那些理论上明显错误的方案,这样才有精力真的去试那些有限的、可能正确的方案。
咱们不须要100%精通MySQL(我本身离这个目标也相去甚远),可是只要多知道一些原理,就能多剪一些枝,架构设计就能少一些错误选项的干扰,设计出来的项目架构正确的可能性更高。
我本身特别喜欢这个剪枝的过程和感受,他表示我用之前学习的时间,来节省了如今工做的时间。
固然,“原理”是一个很大的概念,有的原理更接近实战,有的远一些。这个专栏我挑的是跟平时使用相关的原理,以便你们能够有机会边学边用。
一块儿加油吧