粉丝4469获赞3.4万

你们遇到过这种情况吗?首先看一眼代码视力,这是一个 supreme boot 项目,这个方法用于保存学生信息,在方法中又开启了一个现成,保存另一个学生信息。最后手动抛出一个异常,这里我添加了一个 supreme 的生命式事物, 执行一下会是什么效果呢?咱们来看一眼数据库,现在数据库里面是没有数据的,咱们通过 pos 他们调用一下,看一眼效果, 再刷新一下数据库,发现子线城的数据保存进来了,这是什么原因呢? severin 事务是通过数据库连接来实现的,咱们来看一眼 severin 事务管理员码, 发现数据库链接是放在 thread local 当中的, thread local 本身是线层隔离的, 咱们开启的线程和主线程不是同一个数据库连接,只有同拥有同一个数据库连接,才能同时提交事务和回滚。那这个问题应该怎么解决呢?咱们可以通过编程式事务 加上咱们的加握同步工具来解决,同步工具呢有很多种,这里面咱们用到的是卡当烂尺。来看一眼原码。 首先呢,模拟一些学生数据,创建一个现成池创,按照学生数量创建一个计数器,这里有一个异常标识,默认值为 false, 如果子线程报错,则将这个标识设置为处。按照学生数量,咱们开启现成, 这里通过传赛身 time later 的 x q 的方法来进行插入操作。这里我判断了手动模拟了个 异常,判断学生姓名是否等于礼仪,如果等于礼仪,我则抛出一个自定义异常,接下来呢,捕捉这个异常,如果发现异常呢,则将咱们那个异常标识设置为处, 然后计入器卡档。最后呢,用一个烂尺点儿 awat 等待其他线程执行。最后判断这个异常标识是否为处,如果为处,则进行回滚,再往下呢,那就是拉烂尺点儿 awat 主线程,等待其他子线程执行。 那咱们执行一下,看一眼效果, 看一眼日志,手动异常,说明咱们的异常已经抛出了 这些 circle 呢,里一、里二里三也执行了,咱们看一眼数据进没进来,发现这种方式是可以解决问题的。还有什么解决方法?小伙伴请在评论区里留言。

今天给大家讲解一下买手口事物回滚的 on the log 之字。买手口为了在事物执行过程中出现错误或主动 回滚的时候,将数据恢复到事物执行之前的状态。买收口底层使用了 onlog 机制按存储组成。从小到大,买收口的 onlog 的存储 分别为叶 on the log 日字念表、斯洛特存储槽以及我们的回滚段。这个 on the log 日之夜是 on the log 日字存储的一个最小存储单元,它分为四种类型的一个页,第一种类型是普通的 ins 二的类型存储页, 第二种类型是普通的 update, update 类型纯出页。第三种类型是临时表的尼萨尔类型纯出页。第四种类型是临时 是表啊不对的类类型存储页。这个普通和临时表就是我们麦授课的这个表结构里面的普通表和物理表和临时表。这个 insert 和 update 类型的区别是 ins, insert 就是我们的 insert in two 语句执行过程中形成的 undo log 日字。 update, 这个 undo log 日字是我们在执行 update 语句以及 delete 语句的时候形成的一一个 undo log 日字。其中的里头呢,他在卖售后底层的执执行过程中呢,他是先做标记,最后的话再将我们的数据做一个单除这么一个操作, 如果一个事物执行过程中 它包含两种操作,比如说我们一个事物包含既包含了 insert 类型的操作,又包含了 update 或者 delete 类型的操作,那么它的这个 onlog 日志就会存出两种,另一种是 insert, 另一种是 update。 同时呢,我们 从一个事物的 insert 类型的一个操作和 update 类型的操作,他都会形成一条独立的一个链表,就比如说 insert 类型的形成一个 instrument 列表, upside 类型就是 upstate 类型的链表。最后呢,将我们的链表存储在这个 slote 存储槽里面, 那我们的客户端在执行一个授课语句的时候,他如何找到我们正确的位置来存储我们的昂都那个日志呢?买授课的表空间,他的第五页就会存储我们的一 个 on blog 日子 segment 就是回滚段,回滚段里面呢,就映射了我们的一个我们的这个 on blog slot 存储槽,这个存储槽阴异色的,就是我们的昂多了个日志存储链。我们的客服端通过表空的第五页,然后找到一个空闲的斯洛斯槽,再向我们的这个斯洛斯槽里面去写我们的昂杜洛克日志。 当我们的一个事物执行结束之后,我们的 insert 类型的这个昂读 log 日字用了没?用了,这个时候就可以清理掉了。 但是呢,这个 update 的类型的这种 ontolog 日志的话,他现在还不能清楚,因为我们在这个 m m v c c 机制里面会用到它。这 这个是我下一个视频要讲的一个内容,就是后面这一这一坨,这一期先不讲了,最后我们看一下在这个崩溃恢复过程中他的一个 逻辑,这 macycle 在公崩溃崩溃恢复的过程中,他会去我们的 macycle 表空间第五页去寻找,这个不是困难,是说明他存储了我们的这个 onlog 的日志, 这个 on the log 日字的话,就可以进行一个回滚,找到不有空的这个 slot, 然后再找出这个 on the log 日字列表带,从这个列表的第一个页面的这个一个有一个叫 on the log segment header 里面这么一个 存储单元,再从这个 onlog segment here 里面去找到我们的它有一个 onlog state 的这么一个属性,这个属性就标记了 我们这个事物是活跃的,或者是或者是已经结束的一个事物,他他有一个媒体,如果我们的这个事物是活跃的事物,直接读取我们的一个事物 id, 然后将我们对应的数据进行回滚,如果我们这个数这个事物是已经提交过的,说明他是一个结束事物, 就不要管他,不回本就行了。这就是我们今天要讲的整个 onlog 日志的一个底层存储逻辑,以及一个巡止崩溃恢复的一个过程。 啊。本本期视频就到此为止,下一期呢,我为大家介绍一下 mvcc 的一个机制。

最近我在做 seed 的升级改造,然后在做的过程中间呢发现了一些问题,所以今天来给大家 呃分享一下吧。呃,问题是这样子的,比如说我们的微服里面呢,我们既会去用到 c, 他也会去呃用到下一点 gdp c, 那么如果说我们下顶我们配了主重的话,可能就算我们主库和重库用的是同一个数据库,其实也会有问题。呃,这是原因呢,就是我这个视频要讲的,那我来分析一下, 比如说我现在直接启动我们的呃看胸膜应用,那么在我应用里面呢,我到时候会来请求我们这个摄影师,这个摄影师呢,他其实就会去开启一个全局失误嘛,但是我在最后我却抛了一个控制针,所以说讲道理呢, 呃,相当于这个全局事务只有一个分支事务,那么我这里跑了异常的话呢,就会导致这个全局事务,也就是说这个分支事务应该是要回滚的啊,这应该也是 c 他,他正常来说他也是能够支持的, 但是就是因为我用的虾顶 gdpc 之后啊,像他就分支事物就会回滚失败。我们来试一下,比如说我来访问这个泰式的 啊,比如说我们看日志的话呢,确实是开启了一个拳击事物,并且呢这里也会开始去回滚我们的分支事物,但是很明显他一直没有成功啊,我们稍微等一会, 好,我们发现呢,等了很久,他确实 就一直卡在这里呢,也没有什么日志,他等一下应该是会抛一段日出来的。所以从这个现象啊,这里抛了一个控制针啊,这这个控制针是我们自己抛的,但是在最后呢,他又继续去回滚了,所以相当于他肯定是前面分支事物。 呃,他就一直是没有回滚成功的,那原因在哪里呢啊?其实我真正自己在排查的时候呢,也是排查了一段时间的 啊,最终呢是从数据库这一边找到了一些。呃,切入口吧,比如说我去查看当前有哪些进程啊,但我们可以多查几次啊,因为他他有的在做一些别的,比如说我们发现 像这个 delete, 它其实是执行了,呃,挺长时间的,你看它就一直,其实一个 delete 来说,它应该是能够执行很快的,我这里面其实就一两条数据,所以它为什么一直在这里卡住了呢?它为什么一 一直就没有执行完呢?啊?从我们秀 processes 的我们是看不出来什么现象的,我们直接来执行。呃,这个语句吧,就是直接查看印度 dv 里面所相关的信息。好,我们一执行我会发现,哎,确实还是刚刚这个 delete 啊,他应该是在等待锁啊。那么这些字段呢,我大家可以说一下,比如说啊,这个是哪个对应的哪个表?这个是 锁的类型,是行锁,对吧?然后这些呢,就是跟这个社会有关的,表示是当前哪个失误在等待这把锁,那么这把锁到底是被谁占用了呢?哎,这里就显示出来了,比如说这个 pid, 就表示是买手里面的这个进程,他占用的当前这把锁,那么四七九,我们就可以直接再根据这个四七九去查这个表就可以。 你查到当前这个进程里面的线程啊,只有一个就是五幺九,我们再根据这个五幺九去看这个线程,他到底在干嘛?哎,我们发现 通过这个验证我们可以发现啊,他是根据这个英文的 id 来就可以了。九嘛,那他是查查这个,然后查这里,反正就是一系列的信息,但是我们发现这里就跟我们的 myatt 有关系,并且这里用的呢 for update, 所以从这里我们大概就能够看出来,他确实啊,好像是由于这个 circle 他先发布 data 了,所以说就去加了锁, 从而呢就导致我们这里的 delete, 因为 id 都等于二嘛, delete, 它在 delete 的时候呢,它就一直加不到锁,所以它就一直主设在这个地方,所以,呃我当我们这里只是看到了表面的原因。那为什么呃 呃要去加那个 forthat 呢?对,我们的业务逻辑里面,我们一开始是插入 anti 里嘛,那你要回滚的话呢?啊就是迪丽特,所以说从这里我们也可以放心。确实呢,迪丽特是正常的,也是表示在进行分支事物的回滚,但是就是一直回滚不了嘛, 那呃 insect 也好,呃我们的 delete 也好,这都是我们能够理解的。那为什么这里还实现了一个 select 的 for update 呢? 这就是问题所在。那么这里呢,就需要去分析一下我们 c 塔的原理啊。我这边呢也可以给大家呃大概看一下他对应的源码啊,其实就在呃这一块给大家找一下 啊,就在这里啊,比如说在真正进行分支事物回滚的时候啊,比如说这里这个方法是在最外 外层调过来的昂度,昂度调过来的昂度调过来之后呢,他就会继续执行,就会执行到这个方法。而真正在进行分支事务回滚的时候,或者说在一开始的时候呢,会进行一个验证,呃,比如说会去查询我们通常说的 呃,就 be for image, 哎,包括 of the image 啊,我这里举一个例子啊,比如说在我们用 seed 的时候,我现在假设有一个这段 a, 我本来呢 a 是等于一的,然后我的业务逻辑里面呢,我就把 a 改成了二,好,那现在我这个分支式我肯定要回滚啊,正常来说呢,我肯定就是 把二又修改为一嘛啊,所以说这里的 b for 就是 ef 的,就是二,好。那么但是呢,我在呃回滚的时候,我可能就需要去判断一下,如果说我发现当前的 a 就是已经变成了三,那其实是有问题的。 就是说我分支事务提交之后,然后在全局事务回滚的时,中间这段时间啊,已经有其他的事务把我的那个 a 给改掉了。所以说我们分支事务在回滚的时候呢,我就要去查当前的 这个数据到底是什么样子的。然后我可能就需要去比较一下你当前的数据啊,应该在下面就是这里会去查当前的数据,当前的 record, 然后再和我们的 after record 去进行比较,就是当前表里面的数据和我前面记录的 那个数据是不是是相等的,如果不相等,那就表示是被改了的,那我就不能回滚了啊,别,就比如说被改成了三,那我就不能把三直接改为一,我正常来说是应该要把二改为一的,现在你是三了,那我就不能回滚了,就说这里就需要去查 当前的数据,并且你想嘛,查出来之后,那你你你这里还下面还有逻辑啊,那么我在执行这些逻辑的时候呢,你肯定也不能让其他事物去把你 a 字段去进行修改,所以说他在执行 这个查询的时候呢,就会用到 for update 啊,这里呢,我可能就一时半会就不带。呃,就这里,对,就是去查,并且运用到了 for update 就加锁,加了锁之后我就把当前的数据查出来,然后再和 up 的一面积进行比较,如果说发现两者一致,那么我才会去真正进行回滚。 而我们这里的回文呢,其实就是迪丽塔嘛,就是去执行迪丽的操作,所以这就是一个正常的 c 塔的分支事物,他回滚的逻辑就是先查并且发布的,查出来之后就进行比较比较,如果发现数据没有改变,我就去迪丽特, 就是删除掉我前面插入的那条数据。好,这是正常的,似乎是没有什么问题的,但为什么到我们这里就有问题呢?啊?就这个就跟我们瞎点 gdp c, 他确确实实就是有关系的,因为在不管是 c 他也好,还是瞎点 gdp c 也好,他们都会有各自的 呃 data source 啊,比如说 c 场里面,它叫做 data source proxy, 然后相对于 t v s 里面,如果说我们用的组成就是 master slave data 硕士,如果我们单独用他们两个的话呢,他们两个都会去代理,比如说德鲁伊的 data 硕士,或者说其他的更底层的 data 硕士。但是如果说我们 呃在一起使用的话,就会是 ceta 的去代理,下面一个 d b c 的就是 ceta 的这个作词,下面是 mas 的 snave 的作词,然后再下面才是德鲁伊的,这个是呃的 c 的硕士,那么这就会有问题,比如说呃 c 塔嘛, c 塔上现在需要去执行 c 口,那肯定就得去建立连接,那么它一建立连接呢?其实最终就就是利用的我们相连 g、 d、 b、 c 去建立数据会连接,而我这个 master snap 的硕士,它最终就 就建立出来,就是 mass 的 sniver connect 型,这个数据会连接,最终 c, 他其实用的是这个连接,而这个连接他在执行社会的时候,他就根据当前要执行的社会来判断。比如说你执行的如果说是 select, 那我就会和 重库去建立数据库连接,如果说你执行的是 inside 这些,那么他就会和主库去建立数据库连接,那这就不就有问题了吗?就是我分支数在回滚的时候,我一开始是查询嘛,对,这个时候我获取的其实是重库的数据库连接好,然后获取完之后呢,你 就继续去 delete, 而 delete 的时候,你又获取的是主库的数据会连接,就变成了两个连接,而前面这个连接它并没有提交,因为按照 c 塔正常的逻辑来说,它应该在 delete 之后才才去提交的。 说对于前面这个事物而言,他加了锁,但是呢锁又一直没有释放,而后面这个事物他想要 delete 的时候呢,他就想要去加锁,但是又加不到锁,因为他们不是同一个数据库连接,更加不是同一个事物,所以后面这个事物他加不到锁,从而就卡住了, 从而就导致我们分子事物就回滚不成功,所以这是呃最根本的原因,所以,呃,就如果说我们要解决这个问题的话呢?啊,就当我们可以扩展的。先想一下,就是对于斯伊塔的这个呃呃数据员而言, 那大家说的是 proxy 而言,他就建议不要去代理相连接的 b c 的,因为你一旦代理了,那么就可能会导致 c 塔内部他在执行一些逻辑的时候,他可能原本要执行很多很多 ceo 嘛,这些 ceo 呢,原本是要在一个事物里面去执行的, 但是如果说你是 c 塔代理的相连接 dbc, 那么很有可能就导致这多个操作被分散到多个数据库连接中间去了,那很有可能就会有问题,对不对?所以我们改造的方式呢,也就是去 呃就换一个顺序,让虾顶的去代理 cat 的啊,最终呢,其实是没有探到问题的,包括虾顶内部,他后面也支持 cat, 他也是这么做的,所以,呃,这是这个问题的解决方式,但具体是怎么改的,我这个视频可能就不太方便继续上台去讲了,后面再分析 啊。然后这里顺便提一准,就如果说我们,嗯,前面我们说组成是同一个库吗?或那就算你组成是不同的数据库,其实也可能也会有问题。比如说 c 塔,它提交分支事务的时候会插入 on the log, 这个时候插入肯定是主库, 但是在回滚的时候呢,他需要去查出 undolog 嘛,然后查 off 的 emai 加 e 呃 b for email 家会查安 dolog, 而查的时候是重库,所以如果你储存延迟比较大的话,那很有可能就查不到,那不照样会 会很失败吗?所以这也是问题,所以呢这是,呃,就还是前面说的,就如果说 c 塔整合下定决定 d b c 的时候呢?你可能需要自己做一些改造。好,所以这是就今天给大家分享一下我的这一块的理解吧。

然后呢是提亮插入,提亮插入其实很多人都知道,对吧?就是当我们比如说用 excel 或者之类的,我们一次性,比如说导入个一万条, 那假如说你直接用循环一条一条插入的话,他的性能是非常低的,对吧?因为你每次掉一次音色的,相当于呢,你得去连接一个数据库的口 nax, 然后呢你还得去插入一条数据,这样的性能肯定是非常低的。那么怎么优化呢? 我们可以用批量插入的这种方式来进行优化,那么其实我们的 circle 语句它是支持批量插入的,我们可以通过 inside in two, 然后 values, 我们直接插入多个值,用逗号去分开,每一个括号就代 表一行的数据。那么通过这种方式呢?就比如说我们用 my bettis, 我们可以通过动态 cycle 的方式, 对吧?比如说我们用 my betice, 我们通过 for each 动态 co 的方式呢,去拼接我们的这个要插入的数据,然后来完成这种批量插入的方式。当然假如说你用的是 买 babys 的话,你还可以去设置那个 executor type, 设置为 bench, 那这种方式呢,它对批量插入的性能也是能够提高的, 所以说你看我在这里呢,测试一千条数据,循环插入的话是要一千多毫秒,但是我们通过这个 fore 一直去拼接我们批量插入的 sql 语句,它的性能是最高的, 只要四百多毫秒。所以说批量插入,假如说我们遇到了这种场景,千万不要用 for for 循环一条一条插入,好吧,当然我们虽然通过这种啊我们的 动态 c 口的方式去拼接成批量插入的 c 口语句的话,我们也不建议每次插入太多啊,尽量控制呢,一次五百。所以说你假如说你用这种啊 批量插入拼接 cycle 的方式,我们其实也可以用循环,但是呢你可以五百一次循环,五百一次,好吧,这样呢啊就不会造成比如说内存溢出啊或者死锁之类的问题,好吧 nice。

啊,我们来说一下这个思索啊,这个你面试的时候可能经常会呃,人家会问你什么叫思索?就是思索啊,就相当于是,嗯嗯,互相持有对方的锁,谁也不释放, 那么这时候呢,就会产生思索了啊,按照那个生活的案例呢,就相当于两个人这个互殴呢, 双方都抓着对方那个脖领子啊,你说你松手,我说我松手谁,结果谁也不松,哎,这就是这叫思索,那么这里呢,就是你有没有想过这个问题,就思索的话, 那出现回滚,是回滚是十五一啊?还是回滚十五二,这个你有想过这个问题吗? 那这里呢,我们就通过这个案例啊,给你演示一下,咱到底回滚哪?是回滚这一条还是回滚这一条啊?我们来看一眼啊,我,我开两个绘画啊, 然后我数亿执行 啊,数二,紧接的啊,直行 i d 等于二, 这里我都没有提交,那也就是说我这两条啊都会加锁 id 等于一会加锁 id 等于二,也会加锁,然后这个时候呢,我把这改一下, 这时候呢,在十五一上,哎,我更新一个范围,这回大概是更改九十八条吧, 这时候呢,我一回车会出现所等待,因为我这条记录没有提交呢,对吧?看见没有,我们一回车,这时候会出现所等待,那紧接着 我在四五二里头,嗯,再执行一条记录,还是把 id 等于一这个给改了, 这时候就出现思索了,看到没有?这就是思索,双方俩人互相互殴嘛,谁也不松不松手,哎,这就是 就是那个出现思思索,然后呢,我们看这个问题,到底是回滚他,还回滚他还是回滚他 啊?我们看啊,这是思,这是打印出那个思索信息来啊?我们看这个,嗯,看十五一啊,这是,这是十五十五二 啊,看这个事物一啊,是吧?这是相当于一个范围,是吧?然后呢?下面呢?是事物二只更改一条, 对吧?然后我们就看最后一个,我们回滚的是 w 二,对吧?也就是回滚了, 影响最小的行数是被优先被回滚掉的。啊?这个呢? 如果你面试的时候呢?你跟面试官说啊,回滚这个因为他影响行数最小,这个影响行数最大的他不会被回滚,这是 mythical 内部的一个机制。

您是否遇到过需要更新包含数百万行的表,但又不想等待数小时才能完成的情况?在此视频中,我将演示如何更快的更新数据。本视频中,我们把 starcover four 中的部分数据转处到了埋色口中,但这些方法也适用于其他数据库数据。如画面所示, 这是来自 star overflow 的评论表。我们对表进行快速选择技术,发现他有大约两百万条记录。接下来要更新什么呢? 假设我们要进行一些字符串替换,用单引号替换任何出现的双引号。可以运行一个快速的 slick 语句来查看将要更新的记录数。 这里大约是二十五万七千行,让我们开始吧。第一种方法是编写一个简单的更新语句。我们将更新表中的所有行,这样就不用添加威尔子句。 可以这样做,如果在字符串中找不到双引号,则此处的替换函数不会进行任何更改。运行这个语句并在底部查看结果。我们看到它显示已更新了二十五万七千九百二十五行数据,这与我们操作的数据及相符,所用时间为六点九秒。 方法二是相同的,只是添加了一个 word 子句。因为我们只想更新具有双引号的记录,所以也许可以通过添加一个 word 子句来排除其他行来加快速度。可以使用 inster 函数来实现此目的。如果返回的值大于零,则表示已找到该字符串。 运行查询并检查输出面板。它显示更新了二十五万七千九百二十五行,运行时间为八点五秒。可以看到它实际上比方法一慢一点。如果您 在自己的数据机上尝试此方法,有可能会得到不同的运行时间,并且此方法甚至可能比方法一更快,所以暂时不要低估这个方法。如果采用批量更新数据的方法呢? 我们可以一次更新一千条记录,而不是一次更新所有二十五万七千条记录。这是在麦色口中执行此操作的代码涉及几个步骤。我们首先创建一个表, 其中包含所有要更新的记录的 id 及符合条件的二十五万七千行 id。 再创建一个表来存储当前批次中的记录,目前这是一张空表,然后在这些表上创建,所以 以便后面的语句可以快速找到数据。接下来我们向当前批处理表中插入一千条记录,通过使用那么一千子句来做到这一点。此语法在其他供应商中有所不同,但是用相同的概念。 到目前为止,每个步骤都花费了不到一秒钟的时间。紧接着我们有一个更新语句,他在评论表中执行更新,但是 我们连接到当前批处理表,以便只有当前批处理表中的 id 受到更新语句的影响,此更新语句仅更新一千行,运行时间为二点九秒。然后从 ids took play 表中删除当前批次中已更新的 id, 因为我们刚刚对他们进行了更新,该批数据已不再需要 此步骤的运行时间不到一秒。最后,我们从当前批处理表中删除所有数据。多次重复这些步骤, 从插入当前批次到删除该数据,以便最终更新所有数据。选择这些语句运行看们我们可以看到用时大约三秒。需要重复此操作 直到更新所有记录。其时间成本取决于设置的批量大小和表中的记录数。我们可以根据数据量估计所需的时间。要更新批量大小为一千和二十五万七千条记录。每个批量运行需要三秒,估计需要七百七十一秒或大约十三分钟。这比其他两种方法慢的多。 您可以通过将其包含在循环中来检查是否还有剩余记录来改进这一点。这样只需执行一次,他就会自动循环。还可以调整批量大小以查看他是否运行的更快。但这是另一种适合您的方法。方法四与方法三类四,但均在一个批次中进行。 这涉及创建一个临时表来保存我们想要更新的所有 id, 因为它是临时表,所以在绘画结束时会自动删除。我们可以运行这个语句,花费时间为四点九秒。 接下来在 id 列上添加锁影会让我们的更新语句运行的更快。用时零点五秒,然后更新评论表并连接到这个临时表。这意味着只有符合条件的 id 才会被更新。 运行这个语句花费了六点七秒,因此该方法总共需要十二点一秒。与我们的其他方法相比,他有点慢。 最后一种方法是使用 create table 语句。我们将运行一个创建表语句。该语句使用选择查询的结果创建一个新表。选择查询将从评论表中获取数据, 但是选择查询返回的是更新后的数据,而不是原始数据。在此视力中,我们将返回的文本值替换为函数的结果,而不是文本值本身。运行词语句可看到运行时间为十三点一秒, 并且包含了更多的行,也就是原始表中的每一行。该方法比其他方法稍微慢一些。让我们比较一下这五种方法。此视频中使用了五种不同的方法,以下是每种方法的运行时间,他们的范围从六点九秒的基本更新,一直到需要七百七十一秒或大约十三分钟的批量更新。 该视频的目的是向您展示更新数百万行数据的不同方法。如果您使用不同的数据库表和更新语句,将获得不同的运行时间。您甚至可能会发现某种方法比我们的经验更快。如果您需要在大型表上运行更新语句,可尝试其中的一种方法,看看哪种方法更适合您。 好了,这就是本期视频的主要内容。如果本视频对您有帮助,还请关注我们,感谢您的观看。

现在我有一个需求是要求提防数据库中带有文件地址的 ip, 然后变更成另外一个 ip, 也就是说我们的文件服务器可能 有牵引,然后需要更换我们的地址。但是说有些小伙伴可能就会有疑问,为什么我们的地址存的不是后面的 uri 地址?把前面这节放到我们的加号配置项目里面去进行动态的配置。这个问题问的好, 但是现在的问题就是说我们数据库中存了一个完整的链接地址,要求我们去替换 ip 或者域名,这个时候我们应该怎么搞呢? 如果要解决这个问题,我们就应该清楚哪些库里面的哪些表以及哪些字段需要我们进行相关的一个替换。比如说现在在我这张测试表里面的字段叫 url, 这个字段是带含文件地址的,需要我们进行替换,但是有可能其他的表里面叫尹曼吉或者其他的名字,这时候我们如果说去一个一个去查找表 你的这段经纪录,然后再一个一个去替换就很麻烦。第二个我们要知道替换语法是什么样的,比如说我整理的 update 的更新语句,我们更新这张表里面的 url 地址,然后我们可以通过这个 replace 替换这个 url 字段中包含幺二七的这个内容, 将它替换成我们这个三 w 这个地址。我们先来看下效果,先执行一下,执行完之后你会发现这里有三条的一个变更语句,也就是我们这张测试表,我们刷新下可以发现就成功替换了,这是我们的一个替换效果。 然后回到第一个问题,如果说我要替换到本次业务相关的所有库下面的所有表,再有这个内容的相关制段信息,那么这时候我们就需要知道数据库里面有这么一个 information schem, 在这个表里面有这么一个制段表,这个制段表里面 记录了我们数据库中的一个数据库名以及相关的表以及这段信息。知道这里面记录了表这段信息之后,我们就可以通过去手动的去拼接出我们要更新的一个语法, 比如说我们可以先去查询到这张表里面的一个质量信息,说这里的是 demo 数据库,然后我可以去动态的去拼接我这里的一个更新语句,比如说这里更新的是这张表里面的一个表明,以及我们设计一个值,然后设置更新我们的一个质段名,然后等于我们的替换语句, 然后就是我们最后加了一个未了条件,通过这个函数我们就可以判断到这个字段里面是否带有这个关键内容,如果说有的话我们就进行提款,没有的话我们就不处理。当然我们也可以使用耐克模糊查询去判断有没有包含这个内容。 然后我们可以来执行下这个 c 口,在下面我们就可以拿到相关的所有的一个表字段的一个替换语句,我们将这个表字段的替换语句全部 ctrl c, 然后到这个粘贴板里面来 ctrl v 一下,这里就给出了所有的一个表字段的一个更新操作语句,然后我们统一的去执行一下, 然后就可以得到我们最终的一个处理结果,我这里是将这个库下面的所有字来进行的替换,实际的时候我们可能只需要替换掉我们的一个挖叉类型, 也就是说这不算类型,像其他的硬头类型我们就根本不需要替换。所以说我们可以通过这个 detap 去判断一下,去过滤一下我们的需要替换的一个数据再走。如果说我们的表数据呢?已经上千万级了,这样直接去替换肯定会出现新的问题的。 所以说在业务前期我们就应该只存下 ui 地址,涉及到前面的域名,我们就应该做成一个配置选项,这样可以防止我们服务器域名变更的时候可以快速替换。

你有没有这样的经历,就是自己在写代码的时候呢,感觉缩口执行非常快,然后程序的一个性能呢,也是杠杠的,但是没过几天发布到线上客户端呢,频繁报错,然后一看这个报错信息居然是后端的这个接口超时了啊,于是你就一脸问号,当时我查数据库明明挺快的呀, ok, 哈喽,大家好,我是于峰,欢迎大家看明视频。那么今天呢,我们就一起来分享几个 sucle 性能优化的小技巧啊,我今天所提到的这些小技巧呢,都是非常的简单,但是呢,经常会被忽略啊,如果你按照今天的这些改进的啊,这些方式去做的话呢,你后续的这个 sucle 的性能优化一定是非常有效果的, ok 啊,那么首先呢,我们来看第一段 soco 啊,就是斯莱克的希望 from restaurant 啊,大家现在可以暂停一下你的这个视频啊,然后我们弹幕一起交流一下他所存在的问题啊,其实很简单,就是咱 咱们使用了这个新号去查询对吧?啊,在我们公司啊,我不知道你们公司怎么样,我们公司的 dba 是明确规定的,数据库查询的时候不能够使用新号, 一定要写清楚你当前这个查询要返回哪些字段。那这个时候呢,可能有的同学就会啊疑问啊,不就是查询了所有字段吗?难道会有什么其他非常严重的问题吗? 首先呢,就是你在使用 slog 的星的时候呢,数据库需要去解析更多的对象自断权限属性,那么在 soco 一句复杂应解析比较多的这种情况下呢,会对数据库造成非常沉重的这个负担,也就是他的这个计算的负担,对吧? 那么另外一点呢,就是这个星号会带上所有的资段,如果说你的这个啊表当中某些资段,比如说是这个 blob 和 taxt 啊这种类型啊,那么你数据传输的这个 size 呢,就会啊非常的大啊。 另外呢,就是咱们现在这个微服务啊, db 和应用程序是分开的,对吧?所以说通过网络传输啊,这种数据库结果的时候呢,这个开销是非常非常大的 啊。另外一点呢,可能是一个面试题相关的,就是斯莱克的心,他失去了覆盖所引的这个可能性啊啊,比如说现在我们有一个表啊,他是 t, a, b, c, d, e, f 啊,其中呢,这个 a 是主见, b 呢是我们人为给他添加了一个缩影, 那这个时候呢,在磁盘上实际上是有两颗必加数的啊,也就是聚集所以和咱们这个辅助,所以啊分别保存的是 abcdef 和咱们的这个 ab 啊,这个地方可能有点云里雾里啊。我们说简单一点,全部的数据呢,我们给大家叫 data, 然后所以我们叫 index, 如果说查询条件中, where 条件可以通过 b 啊这个 b 列的缩影过滤掉一部分数据,那么这个时候呢,他的这个查询是优先走了 index, 那么如果说用户 只需要返回 a 和 b 啊这两列数据的话呢,那么直接通过 index 就可以查询并返回用户的这个数据,这个时候呢是非常非常快的啊。但是如果现在用户使用了这个斯莱克的星啊,他需要获取一些非 index 的数据,那么他的一个查询过程其实是这样的, 首先呢通过 index 过滤掉数据,然后再通过 data 啊去查询得到你其他的这些列的数据,对吧?那么这个时候呢,就无形当中增加了一次 b 加数的这个查询,相对于只查 ab 来讲会啊慢很多。 好的,那么这个斯莱克的心我们讲的有点多啊,接下来我们来讲一下这个小数据及驱动大数据级啊,其实就是讲一个啊,使用 in, 不要使用这个 exist 啊,怎么说呢,比如说现在我们有两张表,一个是 goods 啊,一个是这个 restaurants 啊,我们这个 restaurants 可能在数据库里面只有八千多条数据啊,但是这个 goods 它非常多, 有一百多万条啊,所以说他们两个的数量级是完全不一样的。当你去使用了这个 exist 的时候呢,他首先是会去查所有的这个故字,然后来过滤啊,这个 exist 里面的这些子句啊是否有包含,所以说他是先查大表再去过滤, 就是不满足小表里面的这些条件的这样的一些数据,对吧?啊?但是呢,我们如果使用了这个硬的话呢,他的一个查询正好是反过来的,首先呢他会去执行硬里面的这个查询语句,然后得到了一个条件啊,得到了一个数据集,比如说 id 的这个集合。然后呢我们再查询这个 裤子的时候啊,他会去走这样的一个比较小范围的查询啊,那么这个呢,就是小数据级驱动大数据级的一个策略啊,大家以后在写 sogo 的时候呢,都要尽量往这个方面去靠啊。另外这个地方我们还需要补充一点,就是啊,你需要考虑 考虑一下硬字段的这个内容会不会很多,如果很多的话呢,你需要去分批查询,然后避免这个返回数据量过大,造成程序的这个内存溢出啊。然后另外一点呢,就是我们公司实际上是不推崇像这样的一种写法,就是一条 so 口,但是里面操作了两张表。 我们通常的一个正确的做法是把这个 restaurant 啊这个里面的子句,首先在程序里面查出来,然后得到一个 id 的列表,然后呢再作为参数传给固子这张表的查询啊,这样就啊,一条 soco 只会去操作一张表啊, ok。 那么第三点呢,我们讲到的是批量插入代替这个循环插入啊,这个非常的简单,比如说现在我们有很多的 oder, 然后我们这样循环去操作,实际上性能是很低的, ok, 然后会多次去和数据库做一个交互,对吧?啊?我们其实正确的一个做法是使用这个买 is 他的一个 p 处理啊,就是 insert batch 啊,大概他最后执行的一个 circle 是这个样子, ok。 然后第四点呢,其实就是用 limit 限制返回的一个条数啊,因为我们公司有一个硬性的规定,就是你所有的这个 list 接口和 querry 接口呢,必须要做分页啊,因为有 size 才可以去避免内存溢出啊。 所以说大家在写色口的时候呢,也尽量能够去啊,做一下厘米茶,比如说你现在提供的是一个内部接口,他虽然说没有一个明确的 size, 但是你也可以去强制要求一下,比如说最多不能返回两千条啊,最多不能返回一千条等等,这样我觉得这个做了之后呢,会让你的这个程序有非常好的这个 稳定性啊,就不至于说啊,突然有一天某一个条件出发了一个非常大的查询,然后整个你的这个程序就挂了,这个之前我们也是有过线上故障复盘的一个经历的, ok, ok, 那么接下来呢,我们看一下第五点啊,就是同步数据的时候,我们去使用一个 update at 来检索出增量的数据啊,然后定时任务再去跑的时候呢,是可以以非常小的一个时间频率去跑这个增量数据的一个更新的,然后它也避免了全表的一个扫描啊,这个性能就非常的好,对吧? 啊?实际应用的一个例子呢,就是我们公司会有一个 es 的锁引构建的定时任务,然后呢他再去跑这个 restaurant 的时候呢,只会去跑最近时间往前推两分钟的啊,这样的一些增量的数据啊, 我觉得这个是大家也可以去学习和借鉴一下。然后第六点呢,就是我们的一个分页优化啊,分页的查询优化,对吧?比如说我们这个地方啊,很简单啊,我们去查这个固字表,然后首先第一页是呃一,然后三十这样的一个 size, 对吧?但是呢它随着这个佩奇那么不断的增大分页往后啊,它的这个效率 率是非常低的, ok, 那么这个时候呢,我们其实可以去借助上一次查询的那个最大 id 啊,或者说上次查询的那个啊,欧德拜的那个阶段的一个最大值或者最小值来做一个啊条件的这个过滤啊,比如说这个地方我们就按最大的那个 id 吧,比如说前段这个时候呢,就可以把列表当中最后一条数据 返给我们,然后我们来作为一个条件的拼接,然后我们再去做一个分页的查询啊,这个时候就非常不错了, ok 啊,那么最后一点呢,就是缩小数据级的这个条件要放到前面啊,其实这个我觉得大家应该都能理解啊,就是比如说我们现在去查这个 restaurant 这张表, 然后现在呢,我们这个地方有一个 exdelyt 的等于零啊,就是所有没有删除的这个餐厅啊,我们把它查出来,但是呢其实这个地方并不是很好,如果说让我去改写的话,我会把 这个 wechat id 放到前面去啊,因为这个 wechat id, 比如说某一个大区,他可能只有三十多条数据,可能只有四十来条数据,对吧?然后我们再在这三十四十条数据当中呢,去过滤没有删除的,并且他的这个折扣率是大于二十的啊,那这个就啊比你 当前写的这个 east delete 等于零,然后再去过滤这个啊,它的这个数据过滤的这个数据级是要小很多的, ok, 那么所以说大家在啊写 cico 的时候呢,也尽量去往这方面来考虑一下啊,尽量能够让第一个查询条件就过滤掉非常多的数据, 然后减小你的这个数据集,然后不断你再往下走的时候呢,他的这个效率,是啊会越来越明显的。 ok, 好的,那以上呢就是本期视频的一个全部内容了,如果大家觉得这个视频做的还不错的话呢,请帮忙点赞、投币分享,一键三连支持我一下。那么如果你还没有关注我的这个频道啊,也请你点下关注,我的这个频道呢,是专注于程序员干货视频教程分享,关于程序员身心健康的这样一个频道。 ok, 我们下个视频再见吧。

今天呢,咱们来聊一聊麦思口主从的一个系列面试题啊。那么麦思口主从呢,一直是我们面试当中的一个常客,里面的知识点虽然比较基础,也比较简单,但是能回答全的同学呢,并不多,因为他不可能只问你这么一个点的。之前呢,我们有一个学员,他去面试我们阿里的时候,他就被问到了这个面试题啊。 那首先他问了问我们主从复制的原理,然后我们发生了主从实言,他的一个解决方案是怎么样的。那么正在屏幕前的你有没有遇到过哪些 masco 主从的问题呢?那么也可以打在我们的评论区啊。好,首先我们来看我们的第一道面试题, 在项目当中你们的数据库是怎样部署的呢?这个呢是他的一道开胃菜啊,一般来说他都会通过这么一个影子来去问你啊,他看看你们项目当中到底有没有用过 mic 过主虫,然后呢才会去问你主虫那些原理,包括解决方案等等啊。 那么你在回答的时候呢,你也不能直接说我们的数据库呢,都是我们运维部署的,我们的数据库呢,是部署在阿里云的,那么阿里云呢,已经帮我们做好了主从。那么这样回答呢,基本上已经没戏了啊。所以如果说我们要保证一个高可用,那么保证一个高性能的话,那么基本上你的架构一定是一个主从架构的啊。 如果你回答了我们搭建的是一个主从架构,那么他就会又来问你,我们为什么要去使用这个 mask 主从呢? 那么这道面试题呢,他实际上是想考察你对这个 miceco 主从到底有没有过了解,你不能光会使用,你还需要了解他的所以然对吧,你不能平白无故的去部署一个主从上去吧。所以这一题呢,实际上也不难啊。 我们在回答这道题的时候,我们可以通过这几个维度去答。首先第一点,对于我们数据库单机部署,假设我们是一个四和八级的机器,我们在里面呢是运行了一个五点七的版本啊, 那么他大概能够支撑的一个 tps 和 qps 呢,在五百和一万左右。如果说我们遇到一些促销活动,那么这个时候我们的查询流量他会斗争,对吧?所以我们就需要去进行一个储存分离啊。这样呢,我们能够去缓解我们系统的一个压力,提高他的一个信念。这个呢是第一点。 那么第二点,我们大部分的系统,他的一个访问模型,他是一个读多写少的场景啊,一般来说都是这样的。而我们读写请求呢,他们之间的一个差异呢,能够达到几个数量级的。 所以我们去部署一个一主多重的方式。我们的主库呢,他只负责去写入,而我们其他的重库呢,他呢只负责查询。这样我们既提高了我们查询的性能, 然后呢,我们又降低了我们主库的一个压力啊。好。那么第三点,当我们主机档机的时候,我们的重库他能够去切成我们的主机,这一点他保证了我们服务的一个高可用,尽管我们在主机上 面他发生了档机,那么也不会影响我们正常的业务啊。然后我们主机呢,他也能够做到我们数据的一个容灾备份。假设我们主库上面磁盘已经坏掉了,那么也没有关系,我们的数据呢,在我们的存库上面也是有的。所以关于这一点,我们通过这三个维度回答,基本上就已经 ok 了。这两道是我们的一个开胃菜啊,当他确定呢,你是了解麦这个主从的, 那么这个时候他就会去考察你的深度了。他会问你,我们主从复制的一个原理是什么?这道题的难度呢,是稍微上来了一点啊。那么这道题我们该怎么去答呢?我们接着往下来看啊。 我们可以看到下面这张图啊,我们 my circle 里面它的一个主重复制,它主要依赖的呢是我们 my circle 里面的 binog 日志啊。那么 binog 日志里面呢,它是记录了我们 my circle 上所有的变化,同样它是以二进制的方式,它是保存在我们磁盘上面的。而我们主重复制呢,它实际上就是将我们 bino 里面的日志传输到我们从库里面去。我们的主库呢,他的操作他不会去,等我们 bnog 同步完成,他就会去返回。那么这个过程呢,一般来说他是个义部的,他就请求我们主库更新之后的一个 bnog, 并不是全量啊, 并且他把我们接收的 bnog 写到我们这个叫做 vlog 里面去。这个时候我们的重库呢,他再去做一个回放,他会在重接点里面去创建一个思考线程, 它去读取我们这个 vlog 里面的内容啊,并且它在从库里面去做一个回放,这样呢它就能够做到我们主库和从库之间它的数据的一致性啊。这个呢是我们 micicle 主重复制的一个原理。 那么我们在回答完这道题的时候,面试官呢,他又抛出了一个问题,那么你怎么去保证我们主从的一个一致性呢?好,这个时候呢,就又来到了我们这个新的题啊。那么面对这种情况怎么解决呢?我们来跟大家去举个例子。假设我们碰到了下面 这种情况啊,我们在主库里面呢,我们去执行了一条 delete 的语句。我们有两个条件啊,我们的 a 呢是大于一百的,并且我们的创建时间小于我们二零二三年三月二十四。那么 mimit 呢是一。 那么在这儿呢,有两个锁引啊,一个是我们的 a 锁引,一个是我们的创建时间锁引。我们选择了 a 锁引和选择了我们这个创建时间锁引。那么最后我们零 meet one 就是我们厘米的一条数据,它出来的结果呢?一般它不是一样的, 所以它会存在这么一种情况,我们 bino 给它等于我们 statement 格式的时候。那么我们主库它在执行这条 circle 的时候,它使用的呢是我们的 a。 所以 而从库它在执行这条 circle 的时候呢,它使用了我们 great time 的缩影。那么最后我们组成的数据是不是不一样了,对不对?那么怎么解决呢?我们可以把我们 bnog 的一个格式改成我们行格式,那么行格式呢?它其实里面记录的 bnog 日志呢,它 并不是我们搜口的原文,而是两个事件啊,一个是我们的这个表的 map, 还有一个呢是我们 delete 的一个行,这样呢我们就能够保证在我们主库和存库之间,他们不会导致数据不一致的情况。 所以这个呢是我们保证我们储存一致的一个方案啊。当我们回答完如何去保证我们储存一致性的时候,那么他接下来又要问一个问题了,因为网络他不仅可能会发生我们储存不一致,那么也有可能会发生储存延迟对吧?因为你之前讲到了我们是采用的是毒血分离的架构, 假设我们主库同步到我们重库的时候,他的一个网络速度呢比较慢,那么这个时候你去查重库,你是查不到最新数据的。那 那么这个时候怎么解决呢?他首先呢会问你一个场景啊,你到底有没有遇到过我们主从岩石的一个问题,那么在什么场景下面遇到的?那么在这里呢,我为大家去整理出来的几个点啊。首先第一个我们从库的机器性能呢,他比我们主户 会差一些,所以我们只需要去选择和我们主重规格一样的机器,那么从机器配置这一点呢,我们就能规避这个问题啊。那么第二点,我们重库的压力比较大,这个时候我们可以去搞一组多重架构, 是我们毒的压力扛不住了,那么这个时候我们再加几台机器,比如说我有两台,我有三台,我去扛这个毒的压力,那么这样呢他也能缓解我们重复的压力啊。然后呢,我们也可以把我们 bno 的日志接到我们哈豆腐里面,让他们去提供我们查询的一个性能啊。 好。那么第三点,我们重库过多了,我们前面呢跟大家说可以采用一组多重的价格对吧?一旦我们重库过多的话,我们要知道我们的数据都是从主库复制出去的, 如果说我们重复节点过多,那么我们的网络 i o 呢?也会变得更高,所以我们要去避免我们复制的重节点数量过多啊,一般来说我们有三到五个就已经可以了。好。那么第四个我们的大失误,假设我们 十五在我们的主库里面要执行十分钟对吧?那么我们主库执行完成之后,他给到从库,那么我们的从库是不是也要执行十分钟呢,对吧,这个时候是不是有可能会导致我们主从的食盐他会有十分钟的时差对吧?那么在我们日常开发中啊,我们不要一次性的去删除太多的一些 sk 啊,我们尽可能的分批去进行。 还有一个是我们大表的一个 ddl 语句啊,他呢也会导致一个大失误,所以对于这些点呢,我们要考虑到。好。那么下一个我们的网络延迟问题啊,一般来说我们组成的机器他都在不同的节点对吧?我们尽可能的去选择局域网啊,我们尽可能的选择在一个机房里面, 我们选择局域网,不要去选择互联网,因为互联网的速度呢,实际上他实际上是比较慢的。如果我们要做到跨期访的话,那么我们呢也尽量的把贷款呢去提升到最高。好。那么最后一点,我们低版本的一个 myself 呢,他实际上是只支持我们单线程复制的,如果我们主户的一个并发量比较高的 话,那么他有可能呢来不及同步到我们重库里面,这样呢也会导致个延时。所以我们可以去换用一个更高版本的麦 c 口,让他呢去支持我们多线程的复制啊。那么这几点呢?就是我们去解决我们主从实验问题的一些方案啊。基本上我们回答到这里呢,就已经答的已经很 ok 了。 当他问完你有没有遇到过主从延迟问题的时候,当我们答完了这道题的时候,那么面试官呢?他会接着继续往下去问呢?他不会就此打住的, 他会问你们当时遇到祖宗延迟,你们是怎么解决的?我们在这里呢是跟大家去讲了几种避免我们祖宗延迟对吧?但是不可能完全避免。所以说一旦当我们遇到这些问题,我们怎么去解决呢? 首先第一个我们可以去使用缓存啊,一般来说我们同步去写我们数据库的同时,我们呢也可以把我们的数据呢去写到我们 reds 里面,那么也可以写到我们本期缓存里面啊,当我们 去查询数据的时候,我们会先查缓存,不过这种情况呢,他也会带来我们 master 和 reds 之间数据,怎么去保证一致性。关于这一点呢,我以前也讲过,那么大家有兴趣的话,那么也可以去看相关的视频啊。 第二种方式,我们直接去查询主库,那么这种方式啊,他确实能够完全避免我们主从实验的问题啊,但是这种情况呢,他会给我们主库很大的一个压力,所以这种方案呢,是不太建议的,只有到万不得已,我们在业务上面,我们必须要保证完全一致的情况。那么这样呢,我们可以去选择这种方案 好。那么最后一种,我们的数据入鱼,那么对于一些易不出列的场景啊,如果只认数据 id, 那么消费数据的时候,他需要去查询重库对吧?我们可以把数据全部都丢给我们消息对列啊, 这样我们的消费者呢,他就不用去再去查一遍存库了。不过一般来说这种情况呢,应该也不大可能出现啊,因为我们数据转了一圈对吧。我们讲完了主从延时的一些解决方 之后,我们来看看我们最后一道面试题啊。如果我们主节点发生了故障,那么这时应该怎么处理呢? 首先第一种情况,我们是一主一从啊,假设没有两台机器啊,机器 a 和机器 b, 那么 a 为主库,那么他那负责写,同时呢,他也提供一小部分的独特服务啊。 那么 b 呢,他会重库,他呢是负责读数据的。如果当我们 a 库发生了故障之后,我们 b 库呢,他就会自动成为我们主库,他会负责读写。当我们修复故障之后,那么我们的 a 呢,他就成为了我们的重库,我们的主库 b 呢,他就会把数据同步给我们的重库。 a。 这个呢,就是我们通过一组一重的方式来去解决我们这个故障问题啊。 它的一个优点呢是我们重库呢,这是毒,那么它呢是分担了我们主库的压力对吧,也提升了并发度。而且一个机器故障呢,也可以自动切换,那么它操作呢,也是比较简单的。那么我们的重库呢,它 能够充当我们数据备份的一个角色。那么他缺点呢是我们只有一台从库,所以他的一个并发知识度呢,还是不太够,而且加起来也就两台机器对吧。那么还是存在了我们同时故障的一个几率啊,还是不够高可用这种方案呢,一般的小公司他会这么用啊。 不过在这种模式里面,我们的主从分离,他协商意义不是太大的,因为小公司流量本身呢,就不高。他主要的一个目的呢,是为他的一个可用性,还有一个数据备份。而我们的大公司呢,他一般采用的是我们一族多重的方式啊。 那么 a 为主库,假设我们有 b、 c、 d 三台重录啊,这三台呢,是负责读,我们主库呢,负责读写。假设我们主库 a, 它发生了故障对吧? 那么我们的 b 呢,它就自动成为我们的主库啊,它呢就继续提供我们读写的服务。而我们的 cd 呢,它就负责读。当我们故障修复之后,那么我们的主库呢,还是 b 不变,那么我们的 a 呢,它就自动成为了我们的存库啊。一组多存的好处呢,是 我们有多个重库去进行读对吧?那么他分担了我们主库的压力,同时呢,他也能够明显的提高我们的 qps。 但他的一个缺点呢,是我们只有一台主机在写,所以说写的并发度不是很高啊。那么一般比较大的公司呢,比如说百度地跌,都是这种一组多重的方式啊, 因为他本身查询流量太高了,所以说他需要进行读写分离,同时呢,他们也加了缓存的。那么关于今天的面试题呢,我们聊到这就结束了,如果大家有需要我们这个思维导图,包括我们今天的笔记的话,那么大家可以评论区扣一领取啊,咱们下期再见。

大家好,后台有小伙伴问我说自己面试被问到了什么是买 sq 的回表,为什么加了锁引查询就变快了?你要是想回答好这道题,首先你要知道什么是回表,然后如何避免回表的出现,下面我来分一下。 首先要知道回表是出现在 innodb 的存储引擎下,所以这道面试题也是在考你对 innodb 引擎的礼节。 我们都知道 innodb 引擎下有聚促锁引和非聚促锁引,聚促锁引也叫逐渐锁引,非聚促锁引也可以叫二级锁引。 咱们先来看看主见锁引,在主见锁引数中,非叶子节点存储的是主见和指向下级节点的指针,而叶子节点存储的是你真实的行数据。二级锁引在所引数中, 非叶子节点存储的是缩影值和指向下级节点的指针,而叶子节点存储的是对应的主线值。这里有一个关键点,你要记住,就是你给一个字段建立了一个缩影,那么 introdb 就会为你维护一棵缩影树。 ok, 要弄清什么是 mythical 的回表,这里我们先创建一张表,叫 user 表,它有哪些字段呢?有 id, 有 name, 有 age, 其中 id 是逐渐锁引。另外呢,我又在 age 上建了一个二级锁引,那么在 innodb 引擎下会创建一个作用于 五件 id 的锁引术一、一个是作用于 age 锁引的二级锁引术。假如我们有一条 so 语句是 select name age from user wear age 笔吞三 and 五。我们来看看这条 secure 语句在 in o d b 引擎中是如何运行的。由于 ver 条件是 age, 所以会先去 age 的二级缩影书去查询,找到 age 等于三的记录,接着获得对应的 id 等于三百, 再到 id 锁引书上,找到 id 等于三百的对应的记录是 r 三,然后将 r 三加入到结果机中,接着再回到 age 锁引书中,查找下一条记录。找到 age 等于四的记录, 接着获取对应的 id 是五百,按到 id 锁引数上,找到 id 等于五百的对应记录是 r 四。将 r 四加入到结果机中, 接着再回到 a 值锁引数中,查找下一条记录。找到 a 值等于六的记录,发现已经不满足查询条件了,那么整个查询过程结束。在这个过程中,从二级锁引数回到主见锁引数搜索的过程就叫做 回表。在这里的查询过程中,读了 age 锁引书三条记录回表两次,那为什么会回表呢?主要是我们查询的字段内幕在逐渐锁引书上,所以不得不回表进行查询。那么怎么才能够避免回表呢?其实就是覆盖锁引了,这里我再来举一个例子, 我们用 age 和 name 建了一个联合,所以 index age name。 如果我们再用之前的 circle 语句 select name age from user, where age between 三 and 五,那么会发生什么呢? 这里会去 h 内幕锁印书上去查询,找到 h 等于三的记录,同时发现锁印书上已经有内幕了,那么就将这条记录放入到结果记录,接着查找下一条记录,找到 h 等于四的记录,直接将记录放到结果记录,直到找到不满足条件的记录, 查询就结束了。这里是不需要回表的,这里的联合锁银 a 内幕已经覆盖了我的查询条件和查询字段,所以称之为覆盖锁银。由于使用覆盖锁银可以减少回表的次数,显著提升性能,所以使用覆盖锁银是一个常用的 circle 性能优化手段。