作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding: 0px 10px;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">前言</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span style="letter-spacing: 0px;">今天跟大家聊聊什么是观察者模式,如何应用到工作实践中,以及如何抽取一个观察者模板。</span><br></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: decimal;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 观察者模式定义 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 观察者模式的应用场景 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如何实现一个简单的观察者模式 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 工作中,如何使用观察者模式的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> Spring 观察者模式原理 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 基于spring观察者模式,抽取一个通用模板 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 唠叨几句,总结一下 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">1. 观察者模式定义</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">观察者模式,也可以称之为<strong style="font-weight: bold;color: black;">发布订阅模式</strong>,它在GoF 的《设计模式》中,是这么定义的:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;">Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically。</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">翻译过来就是:<strong style="font-weight: bold;color: black;">观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被完成业务的更新</strong>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">观察者模式属于<strong style="font-weight: bold;color: black;">行为模式</strong>,一个对象(<strong style="font-weight: bold;color: black;">被观察者</strong>)的状态发生改变,所有的依赖对象(<strong style="font-weight: bold;color: black;">观察者对象</strong>)都将得到通知,进行广播通知。它的主要成员就是<strong style="font-weight: bold;color: black;">观察者和被观察者</strong>。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 被观察者(Observerable):目标对象,状态发生变化时,将通知所有的观察者。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 观察者(observer):接受被观察者的状态变化通知,执行预先定义的业务。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">2. 观察者模式的应用场景</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">哪些场景我们可以考虑使用观察者模式呢?</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><strong style="font-weight: bold;color: black;">我们日常生活中</strong>,其实就有观察者模式类似的例子。比如,我们订阅了报社一年的报纸。每天报社印刷好报纸,就送到我们手中。我们就是观察者,报社就是被观察者。</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">而日常开发中,观察者模式的使用场景主要表现在:完成一件事情后,通知处理某个逻辑。如,<strong style="font-weight: bold;color: black;">登陆成功发个IM消息</strong>,<strong style="font-weight: bold;color: black;">支付成功发个邮件消息或者发个抽奖消息</strong>,<strong style="font-weight: bold;color: black;">用户评论成功给他发个积分</strong>等等。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">举个详细点的例子吧,<strong style="font-weight: bold;color: black;">登陆注册</strong>应该是最常见的业务场景了,我们就拿注册来说事,大家经常会遇到类似的场景,就是用户注册成功后,我们给用户发一条<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">IM</code>消息,又或者发个邮件等等,因此经常有如下的代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">void register(User user){<br> insertRegisterUser(user);<br> sendIMMessage();<br> sendEmail();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">这块代码会有什么问题呢?如果产品又加需求:现在注册成功的用户,再给用户发一条短信通知。于是你又得改<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">register</code>方法的代码了。。。这是不是违反了<strong style="font-weight: bold;color: black;">开闭原则</strong>啦。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">void register(User user){<br> insertRegisterUser(user);<br> sendIMMessage();<br> sendMobileMessage();<br> sendEmail();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">并且,如果调发短信的接口失败了,是不是又影响到用户注册了?!这时候,是不是得加个<strong style="font-weight: bold;color: black;">异步方法</strong>,异步发通知消息才好??其实这种场景,我们可以使用<strong style="font-weight: bold;color: black;">异步非阻塞的观察者模式</strong>优化的。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">3. 如何实现一个简单的观察者模式</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">我们先来看下,简单的观察者模式如何实现。可以这么定义</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 一个主题接口 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Subject</code>(声明添加、删除、通知观察者方法) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 一个 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Observer</code>观察者接口 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 一个创建主题的类 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ObserverableImpl</code>(即被观察者),实现了 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Subject</code>接口 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 各个观察者的差异化实现 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">为了通俗易懂,可以这样理解观察者模式:就是被观察者(<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ObserverableImpl</code>)做了一件事情,或者说发布了一个主题(<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Subject</code>),然后这件事情通知到各个相关的不同的人(不同的观察者,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Observer</code>的差异化实现者)。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4513742071881607" src="/upload/ce645860f1b64a9d93e349931c64abe9.png" data-type="png" data-w="946" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;">一个主题接口</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">public interface Subject {<br><br> /**<br> * 添加观察者<br> * @param observer<br> */<br> void addServer(Observer observer);<br><br> /**<br> * 移除观察者<br> * @param observer<br> */<br> void removeServer(Observer observer);<br><br> /**<br> * 通知观察者<br> * @param msg<br> */<br> void notifyAllObservers(String msg);<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Observer</code>接口</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">/**<br> * 观察者<br> *<br> */<br>public interface Observer {<br> /**<br> * 更新消息<br> * @param msg<br> */<br> void update(String msg);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">一个创建主题的类<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ObserverableImpl</code>(即被观察者),同时有观察者列表的属性(其实就是说观察者要事先注册到被观察者)</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">public class ObserverableImpl implements Subject {<br><br> /**<br> * 存储被观察者<br> */<br> private List<Observer> observers = new ArrayList<Observer>();<br><br> @Override<br> public void addServer(Observer observer) {<br> observers.add(observer);<br> }<br><br> @Override<br> public void removeServer(Observer observer) {<br> observers.remove(observer);<br> }<br><br> @Override<br> public void notifyAllObservers(String msg) {<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">for</span> (Observer observer : observers) {<br> observer.update(msg);<br> }<br> }<br>}<br><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;">观察者的差异化</strong>实现,以及使用</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">public class ObserverOneImpl implements Observer {<br> @Override<br> public void update(String msg) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"ObserverOne is notified,"</span>+msg);<br> }<br>}<br><br>public class ObserverTwoImpl implements Observer {<br><br> @Override<br> public void update(String msg) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"ObserverTwo is notified,"</span>+msg);<br> }<br>}<br><br>public class ObserverDemoTest {<br> public static void main(String[] args) {<br> Subject subject = new ObserverableImpl();<br> //添加观察者<br> subject.addObserver(new ObserverOneImpl());<br> subject.addObserver(new ObserverTwoImpl());<br> //通知<br> subject.notifyAllObservers(<span style="color: #a6e22e;line-height: 26px;">"关注公众号:捡田螺的小男孩"</span>);<br> }<br>}<br>//输出<br>ObserverOne is notified,关注公众号:捡田螺的小男孩<br>ObserverTwo is notified,关注公众号:捡田螺的小男孩<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">就这样,我们实现了观察者模式啦,是不是很简单?不过上面的代码,只能算是观察者模式的<strong style="font-weight: bold;color: black;">模板代码</strong>,只能反映大体的设计思路。接下来,我们看下在工作中,是如何使用观察者模式的。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">4. 工作中,如何使用观察者模式的</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">观察者模式的实现有两种方式,同步阻塞方式和异步非阻塞方式。第3小节就是一个<strong style="font-weight: bold;color: black;">同步阻塞方式</strong>的观察者模式。我们来看下,日常工作的例子:<strong style="font-weight: bold;color: black;">用户注册成功发消息的例子,如何实现</strong>。本小节分同步阻塞、异步阻塞、spring观察者模式三个方向探讨。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 同步阻塞方式的观察模式 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 异步非阻塞方式的观察者模式 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> spring观察者模式应用 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>4.1 同步阻塞方式的观察模式<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">我们可以把<strong style="font-weight: bold;color: black;">用户注册</strong>,当做<strong style="font-weight: bold;color: black;">被观察者</strong>实现的逻辑,然后<strong style="font-weight: bold;color: black;">发消息</strong>就是<strong style="font-weight: bold;color: black;">观察者的实现逻辑</strong>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">假设有两个观察者,分 别是发QQ消息和手机消息,于是有以下代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">public interface RegisterObserver {<br> void sendMsg(String msg);<br>}<br>@Service<br>public class ObserverMobileImpl implements RegisterObserver {<br> @Override<br> public void sendMsg(String msg) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"发送手机短信消息"</span>+msg);<br> }<br>}<br>@Service<br>public class ObserverQQImpl implements RegisterObserver {<br> @Override<br> public void sendMsg(String msg) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"发送QQ消息"</span>+msg);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">直接可以通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">spring</code>的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationContextAware</code>,初始化观察者列表,然后用户注册成功,通知观察者即可。代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">@RestController<br>public class UserController implements ApplicationContextAware{<br><br> @Autowired<br> private UserService userService;<br><br> //观察者列表<br> private Collection<RegisterObserver> regObservers;<br><br> @RequestMapping(<span style="color: #a6e22e;line-height: 26px;">"register"</span>)<br> public String register(UserParam userParam) {<br> //注册成功过(类似于被观察者,做了某件事)<br> userService.addUser(userParam);<br> //然后就开始通知各个观察者。<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">for</span>(RegisterObserver temp:regObservers){<br> temp.sendMsg(<span style="color: #a6e22e;line-height: 26px;">"注冊成功"</span>);<br> }<br> <span style="color: #a6e22e;line-height: 26px;">return</span> <span style="color: #a6e22e;line-height: 26px;">"SUCCESS"</span>;<br> }<br><br> //利用spring的ApplicationContextAware,初始化所有观察者<br> @Override<br> public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {<br> regObservers = new ArrayList<>(applicationContext.getBeansOfType(RegisterObserver.class).values());<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">可以发现,观察者模式,就是将不同的行为代码解耦,也就是说<strong style="font-weight: bold;color: black;">将观察者和被观察者代码解耦</strong>。但是这里大家会发现,这是<strong style="font-weight: bold;color: black;">同步阻塞式的观察者模式</strong>,是有缺点的,比如发QQ消息异常,就会影响用户注册,或者发消息因为某些原因耗时,就影响了用户注册,所以可以考虑<strong style="font-weight: bold;color: black;">异步非阻塞</strong>的观察者模式。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>4.2 异步非阻塞方式的观察者模式<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">如何实现异步非阻塞,最简单就是另开个线程嘛,即<strong style="font-weight: bold;color: black;">新开个线程或者线程池异步跑观察者通知</strong>。代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">@RestController<br>public class UserController implements ApplicationContextAware{<br><br> @Autowired<br> private UserService userService;<br><br> private Collection<RegisterObserver> regObservers;<br><br> private Executor executor = Executors.newFixedThreadPool(10);<br><br> @RequestMapping(<span style="color: #a6e22e;line-height: 26px;">"register"</span>)<br> public String register(UserParam userParam) {<br> userService.addUser(userParam);<br> //异步通知每个观察者<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">for</span> (RegisterObserver temp : regObservers) {<br> executor.execute(() -> {<br> temp.sendMsg(<span style="color: #a6e22e;line-height: 26px;">"注冊成功"</span>);<br> });<br> }<br><br> <span style="color: #a6e22e;line-height: 26px;">return</span> <span style="color: #a6e22e;line-height: 26px;">"SUCCESS"</span>;<br> }<br><br> @Override<br> public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {<br> regObservers = new ArrayList<>(applicationContext.getBeansOfType(RegisterObserver.class).values());<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">线程池实现的异步非阻塞方式,还是可以的,但是异步执行逻辑都耦合在了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">register()函数</code>中,不是很优雅,也增加了这部分业务代码的维护成本。一般日常工作中,我们会用<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">spring</code>那一套观察者模式等</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>4.3 spring观察者模式应用<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">spring的观察者模式使用也是比较简单的,就是先定义个事件,继承于<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationEvent</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">public class MessageEvent extends ApplicationEvent {<br><br> public MessageEvent(Object <span style="color: #a6e22e;line-height: 26px;">source</span>) {<br> super(<span style="color: #a6e22e;line-height: 26px;">source</span>);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">然后定义一个事件监听器<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">MessageListener</code>,类似于<strong style="font-weight: bold;color: black;">观察者</strong>,它实现<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationListener</code>接口</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">@Component<br>public class MessageListener implements ApplicationListener<MessageEvent> {<br> @Override<br> public void onApplicationEvent(MessageEvent messageEvent) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"用户注册成功,执行监听事件"</span>+messageEvent.getSource());<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">用户注册成功后,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">applicationEventPublisher</code>(<strong style="font-weight: bold;color: black;">类似于被观察者</strong>)发布事件即可,代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">@RestController<br>public class UserController implements ApplicationContextAware{<br><br> @Autowired<br> private UserService userService;<br> <br> @Autowired<br> private ApplicationEventPublisher applicationEventPublisher;<br><br> @RequestMapping(<span style="color: #a6e22e;line-height: 26px;">"springListenRegister"</span>)<br> public String springListenRegister(UserParam userParam) {<br> System.out.println(<span style="color: #a6e22e;line-height: 26px;">"开始注册"</span>);<br> userService.addUser(userParam);<br> //用户注册成功,发布事件<br> applicationEventPublisher.publishEvent(new MessageEvent(<span style="color: #a6e22e;line-height: 26px;">"666"</span>));<br> <span style="color: #a6e22e;line-height: 26px;">return</span> <span style="color: #a6e22e;line-height: 26px;">"SUCCESS"</span>;<br> }<br> <br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">运行结果:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">开始注册<br>用户注册成功,执行监听事件666<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">这个也是<strong style="font-weight: bold;color: black;">同步阻塞</strong>的方式实现的,等下下个小节先介绍完<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">spring</code>观察者模式的原理,田螺哥再来教大家如何抽取一个通用的异步非阻塞观察者模式哈。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">5. Spring观察者模式原理</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">Spring 中实现的观察者模式包含三部分:分别是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Event</code>事件(相当于消息)、<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Listener</code>监听者(相当于观察者)、<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Publisher</code>发送者(相当于被观察者)。用个图表示就是这样:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.30844793713163066" src="/upload/4aefc4029cd1b28432c105b3e7a50af6.png" data-type="png" data-w="1018" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">这个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationEvent</code>是放到哪里的,监听者<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">AppliactionListener</code>是如何监听到的。接下来,我们来看下spring框架的观察者原理是怎样哈~</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">我们先来看下<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationEventPublisher</code>源代码(<strong style="font-weight: bold;color: black;">被观察者/发布者</strong>)</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">@FunctionalInterface<br>public interface ApplicationEventPublisher {<br><br> default void publishEvent(ApplicationEvent event) {<br> publishEvent((Object) event);<br> }<br><br> void publishEvent(Object event);<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ApplicationEventPublisher</code>它只是一个函数式接口,我们再看下它接口方法的实现。它的具体实现类是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">AbstractApplicationContext</code>,这个类代码有点多,我把关键部分代码贴出来了:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkoibl85XeZqwTZZftaxfKvKOP2T74atqBrj8klsss280iaxibCTDqkR2xVNoNf53zicF3hiceYtrAksGObEzlThPpMuS/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><br>public abstract class AbstractApplicationContext extends ... {<br> //监听者(观察者列表)<br> private final Set<ApplicationListener<?>> applicationListeners;<br> <br> //构造器,初始化观察者列表<br> public <span style="line-height: 26px;"><span style="color: #a6e22e;font-weight: bold;line-height: 26px;">AbstractApplicationContext</span></span>() {<br> this.applicationListeners = new LinkedHashSet();<br> //...<br> }<br> <br> //发布事件<br> public void publishEvent(ApplicationEvent event) {<br> this.publishEvent(event, (ResolvableType)null);<br> }<br><br> public void publishEvent(Object event) {<br> this.publishEvent(event, (ResolvableType)null);<br> }<br><br> //发布事件接口实现<br> protected void publishEvent(Object event, ResolvableType eventType) {<br> //...<br> Object applicationEvent;<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (event instanceof ApplicationEvent) {<br> //如果event是ApplicationEvent对象,或者是它的子类<br> applicationEvent = (ApplicationEvent)event;<br> } <span style="color: #f92672;font-weight: bold;line-height: 26px;">else</span> {<br> // 如果不是ApplicationEvent对象或者它的子类,则将其包装成PayloadApplicationEvent事件,并获取对应的事件类型<br> applicationEvent = new PayloadApplicationEvent(this, event);<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (eventType == null) {<br> eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();<br> }<br> }<br><br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (this.earlyApplicationEvents != null) {<br> this.earlyApplicationEvents.add(applicationEvent);<br> } <span style="color: #f92672;font-weight: bold;line-height: 26px;">else</span> {<br> //真正的消息发送,是通过它。获取ApplicationEventMulticaster,调用multicastEvent方法广播事件<br> this.getApplicationEventMulticaster().multicastEvent(<br> (ApplicationEvent)applicationEvent, eventType);<br> }<br><br> //如果当前命名空间还有父亲节点,也需要给父亲推送该消息<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (this.parent != null) {<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (this.parent instanceof AbstractApplicationContext) {<br> ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);<br> } <span style="color: #f92672;font-weight: bold;line-height: 26px;">else</span> {<br> this.parent.publishEvent(event);<br> }<br> }<br> }<br> <br> //添加观察者(监听者)<br> public void addApplicationListener(ApplicationListener<?> listener) {<br> Assert.notNull(listener, <span style="color: #a6e22e;line-height: 26px;">"ApplicationListener must not be null"</span>);<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (this.applicationEventMulticaster != null) {<br> this.applicationEventMulticaster.addApplicationListener(listener);<br> } <span style="color: #f92672;font-weight: bold;line-height: 26px;">else</span> {<br> this.applicationListeners.add(listener);<br> } <br> }<br> <br> //观察者列表<br> public Collection<ApplicationListener<?>> <span style="line-height: 26px;"><span style="color: #a6e22e;font-weight: bold;line-height: 26px;">getApplicationListeners</span></span>() {<br> <span style="color: #a6e22e;line-height: 26px;">return</span> this.applicationListeners;<br> }<br> <br> // 注册监听器<br> &nb
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">今天来分享一下Nacos注册中心的底层原理,从服务注册到服务发现,非常细致</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">1. Nacos介绍</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">再讲Nacos之前,先来讲一下服务注册和发现。我们知道,现在微服务架构是目前开发的一个趋势。服务消费者要去调用多个服务提供者组成的集群。这里需要做到以下几点:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: decimal;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务消费者 <strong style="font-weight: 700;color: rgb(248, 57, 41);">需要在本地配置文件中维护服务提供者集群的每个节点的请求地址</strong>。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务提供者集群中如果某个节点宕机, <strong style="font-weight: 700;color: rgb(248, 57, 41);">服务消费者的本地配置中需要同步删除这个节点的请求地址</strong>,防止请求发送到已经宕机的节点上造成请求失败。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">因此需要引入服务注册中心,它具有以下几个功能:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: decimal;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务地址的管理。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务注册。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务动态感知。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">而Nacos致力于解决微服务中的统一配置,服务注册和发现等问题。<strong style="font-weight: 700;color: rgb(248, 57, 41);">Nacos集成了注册中心和配置中心</strong>。其相关特性包括:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">1.服务发现和服务健康监测</strong></p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;display: block;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;font-style: normal;padding: 15px 10px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: #353535;font-size: 16px;margin: 0 10px;display: block;">Nacos支持基于DNS和RPC的服务发现,即<strong style="font-weight: 700;color: rgb(248, 57, 41);">服务消费者可以使用DNS或者HTTP的方式来查找和发现服务</strong>。Nacos提供对服务的实时的健康检查,阻止向不健康的主机或者服务实例发送请求。<strong style="font-weight: 700;color: rgb(248, 57, 41);">Nacos支持传输层(Ping/TCP)、应用层(HTTP、Mysql)的健康检查。</strong></p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">2.动态配置服务</strong></p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;display: block;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;font-style: normal;padding: 15px 10px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: #353535;font-size: 16px;margin: 0 10px;display: block;">动态配置服务可以<strong style="font-weight: 700;color: rgb(248, 57, 41);">以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。</strong></p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">3.动态DNS服务</strong></p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;display: block;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;font-style: normal;padding: 15px 10px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: #353535;font-size: 16px;margin: 0 10px;display: block;">支持权重路由,让开发者更容易的实现中间层的负载均衡、更灵活的路由策略、流量控制以及DNS解析服务。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">4.服务和元数据管理</strong></p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;display: block;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;font-style: normal;padding: 15px 10px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: #353535;font-size: 16px;margin: 0 10px;display: block;">Nacos允许开发者从微服务平台建设的视角来管理数据中心的所有服务和元数据。如:服务的生命周期、静态依赖分析、服务的健康状态、服务的流量管理、路由和安全策略等。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">2. Nacos注册中心实现原理分析</strong></span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">2.1 Nacos架构图</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">以下是Nacos的架构图:<img class="rich_pages wxw-img" data-ratio="0.4444444444444444" src="/upload/4e6f39c0262f884bf49a9a422015ccf0.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;">其中分为这么几个模块:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Provider APP</strong>:服务提供者。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Consumer APP</strong>:服务消费者。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Name Server</strong>:通过Virtual IP或者DNS的方式实现Nacos高可用集群的服务路由。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Nacos Server</strong>:Nacos服务提供者。</p> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: #f83929;font-size: 16px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> OpenAPI:功能访问入口。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Config Service、Naming Service:Nacos提供的配置服务、名字服务模块。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Consistency Protocol:一致性协议,用来实现Nacos集群节点的数据同步,使用Raft算法实现。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 其中包含: </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Nacos Console</strong>:Nacos控制台。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">小总结</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务提供者通过VIP(Virtual IP)访问Nacos Server高可用集群,基于OpenAPI完成服务的注册和服务的查询。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Nacos Server的底层则通过数据一致性算法(Raft)来完成节点的数据同步。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">2.2 注册中心的原理</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这里对其原理做一个大致的介绍,在后文则从源码角度进行分析。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">首先,服务注册的功能体现在:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务实例启动时注册到服务注册表、关闭时则注销( <strong style="font-weight: 700;color: rgb(248, 57, 41);">服务注册</strong>)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务消费者可以通过查询服务注册表来获得可用的实例( <strong style="font-weight: 700;color: rgb(248, 57, 41);">服务发现</strong>)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务注册中心需要调用服务实例的健康检查API来验证其是否可以正确的处理请求( <strong style="font-weight: 700;color: rgb(248, 57, 41);">健康检查</strong>)。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Nacos服务注册和发现的实现原理的图如下:<img class="rich_pages wxw-img" data-ratio="0.4148148148148148" src="/upload/7f29cfa289e5901db5cf4a15a46e2818.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">3. Nacos源码分析</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">前提(在本地或者虚机上先启动好Nacos) 这一部分从2个角度来讲Nacos是如何实现的:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务注册。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 服务发现 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">3.1 Nacos服务注册</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">首先看下一个包:<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">spring-cloud-commons</code><img class="rich_pages wxw-img" data-ratio="0.6675925925925926" src="/upload/1ca67a36a3be7c8cd623b9e04078d1ac.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">这个ServiceRegistry接口是SpringCloud提供的服务注册的标准,集成到SpringCloud中实现服务注册的组件,都需要实现这个接口。</strong>来看下它的结构:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQZiaRFGPBjTiaPbiaMtKY2tbS8aicuQMBnVoAksWbxsYoyYQPqiaFHq57JaJh7A2ZTFFdwt4dTE9wNekm/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">ServiceRegistry</span><<span style="color: #c18401;line-height: 26px;">R</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">Registration</span>> </span>{<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">register</span><span style="line-height: 26px;">(R registration)</span></span>;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">deregister</span><span style="line-height: 26px;">(R registration)</span></span>;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">close</span><span style="line-height: 26px;">()</span></span>;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">setStatus</span><span style="line-height: 26px;">(R registration, String status)</span></span>;<br><br> <T> <span style="line-height: 26px;">T <span style="color: #4078f2;line-height: 26px;">getStatus</span><span style="line-height: 26px;">(R registration)</span></span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">那么对于Nacos而言,该接口的实现类是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">NacosServiceRegistry</code>,该类在这个pom包下:<img class="rich_pages wxw-img" data-ratio="0.31296296296296294" src="/upload/4a9159d2a05350a593784f825e38ca6e.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">再回过头来看<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">spring-cloud-commons</code>包:<img class="rich_pages wxw-img" data-ratio="0.34029227557411273" src="/upload/6d246eb5877f8aca305c112d2c7b3e9.png" data-w="958" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">spring.factories</code><strong style="font-weight: 700;color: rgb(248, 57, 41);">主要是包含了自动装配的配置信息</strong>,如图:<img class="rich_pages wxw-img" data-ratio="0.49444444444444446" src="/upload/356148d50f818d7a57bc8d20b5bbe1c9.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;">在我之前的文章里我有提到过,在spring.factories中配置EnableAutoConfiguration的内容后,项目在启动的时候,会导入相应的自动配置类,那么也就允许对该类的相关属性进行一个自动装配。那么显然,在这里导入了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">AutoServiceRegistrationAutoConfiguration</code>这个类,而这个类顾名思义是<strong style="font-weight: 700;color: rgb(248, 57, 41);">服务注册相关的配置类</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">该类的完整代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQZiaRFGPBjTiaPbiaMtKY2tbS8aicuQMBnVoAksWbxsYoyYQPqiaFHq57JaJh7A2ZTFFdwt4dTE9wNekm/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Configuration</span>(<br> proxyBeanMethods = <span style="color: #a626a4;line-height: 26px;">false</span><br>)<br><span style="color: #4078f2;line-height: 26px;">@Import</span>({AutoServiceRegistrationConfiguration<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>})<br>@<span style="color: #c18401;line-height: 26px;">ConditionalOnProperty</span>(<br> <span style="color: #c18401;line-height: 26px;">value</span> </span>= {<span style="color: #50a14f;line-height: 26px;">"spring.cloud.service-registry.auto-registration.enabled"</span>},<br> matchIfMissing = <span style="color: #a626a4;line-height: 26px;">true</span><br>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">AutoServiceRegistrationAutoConfiguration</span> </span>{<br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span>(<br> required = <span style="color: #a626a4;line-height: 26px;">false</span><br> )<br> <span style="color: #a626a4;line-height: 26px;">private</span> AutoServiceRegistration autoServiceRegistration;<br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> <span style="color: #a626a4;line-height: 26px;">private</span> AutoServiceRegistrationProperties properties;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">AutoServiceRegistrationAutoConfiguration</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #4078f2;line-height: 26px;">@PostConstruct</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">protected</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">init</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.autoServiceRegistration == <span style="color: #a626a4;line-height: 26px;">null</span> && <span style="color: #a626a4;line-height: 26px;">this</span>.properties.isFailFast()) {<br> <span style="color: #a626a4;line-height: 26px;">throw</span> <span style="color: #a626a4;line-height: 26px;">new</span> IllegalStateException(<span style="color: #50a14f;line-height: 26px;">"Auto Service Registration has been requested, but there is no AutoServiceRegistration bean"</span>);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这里做一个分析,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">AutoServiceRegistrationAutoConfiguration</code>中注入了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">AutoServiceRegistration</code>实例,该类的关系图如下:<img class="rich_pages wxw-img" data-ratio="0.3925925925925926" src="/upload/eea2f3f41c3bdd531eb3a3e247b6f932.png" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;">我们先来看一下这个抽象类<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">AbstractAutoServiceRegistration</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQZiaRFGPBjTiaPbiaMtKY2tbS8aicuQMBnVoAksWbxsYoyYQPqiaFHq57JaJh7A2ZTFFdwt4dTE9wNekm/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">abstract</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">AbstractAutoServiceRegistration</span><<span style="color: #c18401;line-height: 26px;">R</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">Registration</span>> <span style="color: #a626a4;line-height: 26px;">implements</span> <span style="color: #c18401;line-height: 26px;">AutoServiceRegistration</span>, <br><span style="color: #c18401;line-height: 26px;">ApplicationContextAware</span>, <br><span style="color: #c18401;line-height: 26px;">ApplicationListener</span><<span style="color: #c18401;line-height: 26px;">WebServerInitializedEvent</span>> </span>{<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">onApplicationEvent</span><span style="line-height: 26px;">(WebServerInitializedEvent event)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.bind(event);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这里实现了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">ApplicationListener</code>接口,并且传入了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">WebServerInitializedEvent</code>作为泛型,啥意思嘞,意思是:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">NacosAutoServiceRegistration</code>监听 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">WebServerInitializedEvent</code>事件。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">也就是WebServer初始化完成后</strong>,会调用对应的事件绑定方法,调用 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">onApplicationEvent()</code>,该方法最终调用 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">NacosServiceRegistry</code>的 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">register()</code>方法( <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">NacosServiceRegistry</code>实现了Spring的一个服务注册标准接口)。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">对于<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">register()</code>方法,主要调用的是Nacos Client SDK中的<strong style="font-weight: 700;color: rgb(248, 57, 41);">NamingService下的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">registerInstance()</code>方法完成服务的注册</strong>。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQZiaRFGPBjTiaPbiaMtKY2tbS8aicuQMBnVoAksWbxsYoyYQPqiaFHq57JaJh7A2ZTFFdwt4dTE9wNekm/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">register</span><span style="line-height: 26px;">(Registration registration)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (StringUtils.isEmpty(registration.getServiceId())) {<br> log.warn(<span style="color: #50a14f;line-height: 26px;">"No service to register for nacos client..."</span>);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> String serviceId = registration.getServiceId();<br> String group = <span style="color: #a626a4;line-height: 26px;">this</span>.nacosDiscoveryProperties.getGroup();<br> Instance instance = <span style="color: #a626a4;line-height: 26px;">this</span>.getNacosInstanceFromRegistration(registration);<br><br> <span style="color: #a626a4;line-height: 26px;">try</span> {<br> <span style="color: #a626a4;line-height: 26px;">this</span>.namingService.registerInstance(serviceId, group, instance);<br> log.info(<span style="color: #50a14f;line-height: 26px;">"nacos registry, {} {} {}:{} register finished"</span>, <span style="color: #a626a4;line-height: 26px;">new</span> Object[]{group, serviceId, instance.getIp(), instance.getPort()});<br> } <span style="color: #a626a4;line-height: 26px;">catch</span> (Exception var6) {<br> log.error(<span style="color: #50a14f;line-height: 26px;">"nacos registry, {} register failed...{},"</span>, <span style="color: #a626a4;line-height: 26px;">new</span> Object[]{serviceId, registration.toString(), var6});<br> ReflectionUtils.rethrowRuntimeException(var6);<br> }<br><br> }<br>}<br><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">registerInstance</span><span style="line-height: 26px;">(String serviceName, String groupName, Instance instance)</span> <span style="color: #a626a4;line-height: 26px;">throws</span> NacosException </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (instance.isEphemeral()) {<br> BeatInfo beatInfo = <span style="color: #a626a4;line-height: 26px;">new</span> BeatInfo();<br> beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));<br> beatInfo.setIp(instance.getIp());<br> beatInfo.setPort(instance.getPort());<br> beatInfo.setCluster(instance.getClusterName());<br> beatInfo.setWeight(instance.getWeight());<br> beatInfo.setMetadata(instance.getMetadata());<br> beatInfo.setScheduled(<span style="color: #a626a4;line-height: 26px;">false</span>);<br> <span style="color: #a626a4;line-height: 26px;">long</span> instanceInterval = instance.getInstanceHeartBeatInterval();<br> beatInfo.setPeriod(instanceInterval == <span style="color: #986801;line-height: 26px;">0L</span> ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 1.addBeatInfo()负责创建心跳信息实现健康监测。因为Nacos Server必须要确保注册的服务实例是健康的。</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 而心跳监测就是服务健康监测的一种手段。</span><br> <span style="color: #a626a4;line-height: 26px;">this</span>.beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);<br> }<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 2.registerService()实现服务的注册</span><br> <span style="color: #a626a4;line-height: 26px;">this</span>.serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">再来看一下心跳监测的方法<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">addBeatInfo()</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQZiaRFGPBjTiaPbiaMtKY2tbS8aicuQMBnVoAksWbxsYoyYQPqiaFHq57JaJh7A2ZTFFdwt4dTE9wNekm/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">addBeatInfo</span><span style="line-height: 26px;">(String serviceName, BeatInfo beatInfo)</span> </span>{<br> LogUtils.NAMING_LOGGER.info(<span style="color: #50a14f;line-height: 26px;">"[BEAT] adding beat: {} to beat map."</span>, beatInfo);<br> String key = <span style="color: #a626a4;line-height: 26px;">this</span>.buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());<br> BeatInfo existBeat = <span style="color: #a626a4;line-height: 26px;">null</span>;<br> <span style="color: #a626a4;line-height: 26px;">if</span> ((existBeat = (BeatInfo)<span style="color: #a626a4;line-height: 26px;">this</span>.dom2Beat.remove(key)) != <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> existBeat.setStopped(<span style="color: #a626a4;line-height: 26px;">true</span>);<br> }<br><br> <span style="color: #a626a4;line-height: 26px;">this</span>.dom2Beat.put(key, beatInfo);<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 通过schedule()方法,定时的向服务端发送一个数据包,然后启动一个线程不断地检测服务端的回应。</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果在指定的时间内没有收到服务端的回应,那么认为服务器出现了故障。</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 参数1:可以说是这个实例的相关信息。</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 参数2:一个long类型的时间,代表从现在开始推迟执行的时间,默认是5000</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 参数3:时间的单位,默认是毫秒,结合5000即代表每5秒发送一次心跳数据包</span><br> <span style="color: #a626a4;line-height: 26px;">this</span>.executorService.schedule(<span style="color: #a626a4;line-height: 26px;">new</span> BeatReactor.BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);<br> MetricsMonitor.getDom2BeatSizeMonitor().set((<span style="color: #a626a4;line-height: 26px;">double</span>)<span style="color: #a626a4;line-height: 26px;">this</span>.dom2Beat.size());<br>}<br></code></pre> <p data-tool
作者:微信小助手
<p data-lake-id="48c32a09c7423bb870d4f72cece59db7" data-wording="true" style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"><strong style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.05em;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">前言:</span></strong></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.05em;"></span><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">数据库审计功能主要将用户对数据库的各类操作行为记录审计日志,以便日后进行跟踪、查询、分析,以实现对用户操作的监控和审计。审计是一项非常重要的工作,也是企业数据安全体系的重要组成部分,等保评测中也要求有审计日志。对于 DBA 而言,数据库审计也极其重要,特别是发生人为事故后,审计日志便于我们进行责任追溯,问题查找。</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <h4 style="font-size: 16px;line-height: 24px;margin: 10px 0 5px 0;"><span style="background-color: rgb(255, 104, 39);"><strong><span style="font-size: 17px;"> </span></strong></span><strong><span style="font-size: 17px;"> 1. MySQL 社区版审计日志现状</span></strong></h4> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">如果你用的是 MySQL 社区版的话,你会发现 MySQL 官方并没有提供严格意义上的审计日志。虽然 MySQL 提供有 binlog 及 general log ,这二者虽然具备部分审计功能,但一般不当做审计日志来看待。</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">binlog 即二进制日志文件,它记录了数据库所有执行的 DDL 和 DML 语句(除了数据查询语句select、show等),以事件形式记录并保存在二进制文件中。虽然能查到具体 SQL 的执行记录,但其作用主要是主从复制,并不能当做是审计日志。</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">general log 是全量日志,开启后将会记录所有到达 MySQL Server 的SQL语句。一般不会开启此日志,因为 log 的量会非常庞大,对数据库性能有影响,并且 general log 会记录大量无用信息,当做审计日志的话,后期筛选有难度。</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">那么 MySQL 社区版应该怎样进行审计呢?查阅资料我们发现通过安装审计插件可实现 MySQL 的审计功能,常见的审计插件有 MariaDB Audit Plugin、Percona Audit Log Plugin、McAfee MySQL Audit Plugin 三种,MariaDB 自带的审计插件比较适合用于 MySQL 社区版,下面我们来学习下如何使用审计插件来实现审计功能。</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <h4 style="font-size: 16px;line-height: 24px;margin: 10px 0 5px 0;"><span style="background-color: rgb(255, 104, 39);"><strong><span style="font-size: 17px;"> </span></strong></span><strong><span style="font-size: 17px;"> 2. 审计插件使用教程</span></strong></h4> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">首先我们要做的是从 MariaDB 安装包中拷贝出来审计插件,需要注意的是操作系统要选择一致,比如说你的 MySQL 安装在 CentOS 系统中,那就要下载 CentOS 系统的 MariaDB 安装包并从中拷贝,Windows 系统则需要下载对应系统的审计插件。</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">MariaDB 审计插件的名称是 server_audit.so(Windows系统下是 server_audit.dll ),要注意的是,审计插件一直在更新,不同版本的审计插件功能也不同,推荐使用 >= 1.4.4 版本的插件,新版本的插件可以排除掉 select 语句。不同版本的审计插件支持的审计事件如下图:</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.4642857142857143" data-type="png" data-w="1568" width="784" style="height: auto !important;" src="/upload/1a050930430e9baa9a0fc8ccded1109f.png"></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">审计插件版本与 MariaDB 版本对应图如下:</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.7368421052631579" src="/upload/a80fae98e89ee9e3c70c30d937b0a62a.png" data-type="png" data-w="1520" style="height: auto !important;" width="760"></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">MySQL 5.7 一般可对应 MariaDB 10.2 版本,我们以 CentOS 系统 MySQL 5.7 版本为例来安装下审计插件。我这里选择下载的是 MariaDB 10.2.38 版本的安装包(审计插件版本 1.4.13),下载地址:</span><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://downloads.mariadb.com/MariaDB/mariadb-10.2.38/bintar-linux-x86_64/mariadb-10.2.38-linux-x86_64.tar.gz</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">下载完成之后,解压安装包,然后到 mariadb-10.2.38-linux-x86_64/lib/plugin/ 路径下复制出来 server_audit.so 文件,将其拷贝到 MySQL 服务器上,具体步骤如下:</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 14px;padding: 10px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #007400;line-height: 26px;"># 查看 MySQL 插件存放路径</span><br>mysql> <span style="color: #aa0d91;line-height: 26px;">show</span> <span style="color: #aa0d91;line-height: 26px;">variables</span> <span style="color: #aa0d91;line-height: 26px;">like</span> <span style="color: #c41a16;line-height: 26px;">'plugin_dir'</span>;<br>+<span style="color: #007400;line-height: 26px;">---------------+------------------------------+</span><br>| Variable_name | Value |<br>+<span style="color: #007400;line-height: 26px;">---------------+------------------------------+</span><br>| plugin_dir | /usr/local/mysql/lib/plugin/ |<br>+<span style="color: #007400;line-height: 26px;">---------------+------------------------------+</span><br><br><span style="color: #007400;line-height: 26px;"># 将审计插件 server_audit.so 存放到该路径下</span><br>[root@localhost plugin]<span style="color: #007400;line-height: 26px;"># ls -lh server_audit.so </span><br>-rw-r<span style="color: #007400;line-height: 26px;">--r--. 1 root root 191K May 4 2021 server_audit.so</span><br><br><span style="color: #007400;line-height: 26px;"># 更改插件属主及权限</span><br>[root@localhost plugin]<span style="color: #007400;line-height: 26px;"># chown mysql:mysql server_audit.so</span><br>[root@localhost plugin]<span style="color: #007400;line-height: 26px;"># chmod 755 server_audit.so</span><br>[root@localhost plugin]<span style="color: #007400;line-height: 26px;"># ls -lh server_audit.so </span><br>-rwxr-xr-x. 1 mysql mysql 191K May 4 2021 server_audit.so</code></pre> </section> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">以上均为准备内容,为方便各位小伙伴,点击下面链接即可单独下载 Linux 64 位系统的 1.4.13 版本的审计插件:</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">云盘链接: </span><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://pan.baidu.com/s/1HO5sjKb5zpj3CiyRulV5bw?pwd=r85k</span><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 提取码: r85k 。下面我们来开始正式安装。</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><span style="background-color: rgb(249, 249, 249);color: rgb(89, 89, 89);font-size: 13px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"></span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 14px;padding: 10px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Sqqm3oJYw2jMpRkN3ulUx4s9iasIcEhnSlh8JZhrOKoCJKvmFXRUkwU3jFbSwUsfiblgsP1WTMz1Xt7LnpAicA9MkPJVdklpXoo/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #007400;line-height: 26px;"># 进入数据库安装审计插件</span><br>mysql> <span style="color: #aa0d91;line-height: 26px;">INSTALL</span> <span style="color: #aa0d91;line-height: 26px;">PLUGIN</span> server_audit <span style="color: #aa0d91;line-height: 26px;">SONAME</span> <span style="color: #c41a16;line-height: 26px;">'server_audit.so'</span>;<br>Query OK, 0 rows affected (0.07 sec)<br><br>mysql> <span style="color: #aa0d91;line-height: 26px;">show</span> plugins;<br>+<span style="color: #007400;line-height: 26px;">----------------------------+--------+--------------------+-----------------+---------+</span><br>| Name | Status | Type | Library | License |<br>+<span style="color: #007400;line-height: 26px;">----------------------------+--------+--------------------+-----------------+---------+</span><br>...<br>| SERVER_AUDIT | ACTIVE | AUDIT | server_audit.so | GPL |<br>+<span style="color: #007400;line-height: 26px;">----------------------------+--------+--------------------+-----------------+---------+</span><br><br><span style="color: #007400;line-height: 26px;"># 查看 audit 初始参数配置</span><br>mysql> <span style="color: #aa0d91;line-height: 26px;">show</span> <span style="color: #aa0d91;line-height: 26px;">variables</span> <span style="color: #aa0d91;line-height: 26px;">like</span> <span style="color: #c41a16;line-height: 26px;">'%audit%'</span>;<br>+<span style="color: #007400;line-height: 26px;">-------------------------------+-----------------------+</span><br>| Variable_name | Value |<br>+<span style="color: #007400;line-height: 26px;">-------------------------------+-----------------------+</span><br>| server_audit_events | |<br>| server_audit_excl_users | |<br>| server_audit_file_path | server_audit.log |<br>| server_audit_file_rotate_now | OFF |<br>| server_audit_file_rotate_size | 1000000 |<br>| server_audit_file_rotations | 9 |<br>| server_audit_incl_users | |<br>| server_audit_loc_info | |<br>| server_audit_logging | OFF |<br>| server_audit_mode | 1 |<br>| server_audit_output_type | file |<br>| server_audit_query_log_limit | 1024 |<br>| server_audit_syslog_facility | LOG_USER |<br>| server_audit_syslog_ident | mysql-server_auditing |<br>| server_audit_syslog_info | |<br>| server_audit_syslog_priority | LOG_INFO |<br>+<span style="color: #007400;line-height: 26px;">-------------------------------+-----------------------+</span><br><br><span style="color: #007400;line-height: 26px;"># 在线开启审计</span><br>mysql> <span style="color: #aa0d91;line-height: 26px;">set</span> <span style="color: #aa0d91;line-height: 26px;">global</span> server_audit_logging=<span style="color: #aa0d91;line-height: 26px;">on</span>;<br>Query OK, 0 rows affected (0.00 sec)<br><br>mysql> <span style="color: #aa0d91;line-height: 26px;">set</span> <span style="color: #aa0d91;line-height: 26px;">global</span> server_audit_events=<span style="color: #c41a16;line-height: 26px;">'connect,table,query_ddl,query_dcl,query_dml_no_select'</span>;<br>Query OK, 0 rows affected (0.00 sec)<br><br>mysql> <span style="color: #aa0d91;line-height: 26px;">set</span> <span style="color: #aa0d91;line-height: 26px;">global</span> server_audit_file_path =<span style="color: #c41a16;line-height: 26px;">'/data/mysql/logs/server_audit.log'</span>;<br>Query OK, 0 rows affected (0.00 sec)<br><br>mysql> <span style="color: #aa0d91;line-height: 26px;">set</span> <span style="color: #aa0d91;line-height: 26px;">global</span> server_audit_file_rotate_size=<span style="color: #1c00cf;line-height: 26px;">104857600</span>;<br>Query OK, 0 rows affected (0.01 sec)<br><br><span style="color: #007400;line-height: 26px;"># [mysqld]下添加以下配置 使得永久生效</span><br>server_audit=FORCE_PLUS_PERMANENT<br>server_audit_logging=ON<br>server_audit_file_path=/data/mysql/logs/server_audit.log <br>server_audit_events=connect,table,query_ddl,query_dcl,query_dml_no_select<br>server_audit_file_rotate_size=104857600<span style="background-color: rgb(249, 249, 249);color: rgb(89, 89, 89);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;text-align: justify;"></span></code></pre> </section> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">通过以上步骤,我们已经完成审计插件的安装与配置,参照官方文档,我们来了解下主要配置参数的作用:</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.611969111969112" src="/upload/89fec18e9dbb5ba4d44e132d34c1e7ff.png" data-type="png" data-w="2072" style="height: auto !important;" width="1036"></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">以上参数还是很容易理解的,下面我们进行增删改查测试,看下审计日志具体记录的内容:</span></p> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 14px;padding: 10px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Sqqm3oJYw2jMpRkN3ulUx4s9iasIcEhnSlh8JZhrOKoCJKvmFXRUkwU3jFbSwUsfiblgsP1WTMz1Xt7LnpAicA9MkPJVdklpXoo/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #007400;line-height: 26px;"># 进行操作后 查看审计日志内容</span><br>20220612 15:17:17,mysqlhost2,test_user,10.30.21.95,118,0,FAILED_CONNECT,,,1045<br>20220612 15:17:30,mysqlhost2,test_user,10.30.21.95,119,0,FAILED_CONNECT,,,1045<br>20220612 15:20:26,mysqlhost2,test_user,10.30.21.95,124,0,CONNECT,,,0<br>20220612 15:20:49,mysqlhost2,test_user,10.30.21.95,124,395,QUERY,,'<span style="color: #aa0d91;line-height: 26px;">create</span> <span style="color: #aa0d91;line-height: 26px;">database</span> testdb<span style="color: #c41a16;line-height: 26px;">',0<br>20220612 15:22:06,mysqlhost2,test_user,10.30.21.95,129,419,QUERY,testdb,'</span><span style="color: #aa0d91;line-height: 26px;">CREATE</span> <span style="color: #aa0d91;line-height: 26px;">TABLE</span> <span style="color: #aa0d91;line-height: 26px;">if</span> <span style="color: #aa0d91;line-height: 26px;">not</span> <span style="color: #aa0d91;line-height: 26px;">exists</span> <span style="color: #c41a16;line-height: 26px;">`test_tb0`</span> (\r\n <span style="color: #c41a16;line-height: 26px;">`increment_id`</span> <span style="color: #5c2699;line-height: 26px;">int</span>(<span style="color: #1c00cf;line-height: 26px;">11</span>) <span style="color: #aa0d91;line-height: 26px;">NOT</span> <span style="color: #aa0d91;line-height: 26px;">NULL</span> AUTO_INCREMENT <span style="color: #aa0d91;line-height: 26px;">COMMENT</span> \<span style="color: #c41a16;line-height: 26px;">'自增主键\'</span>,\r\n <span style="color: #c41a16;line-height: 26px;">`test_id`</span> <span style="color: #5c2699;line-height: 26px;">int</span>(<span style="color: #1c00cf;line-height: 26px;">11</span>) <span style="color: #aa0d91;line-height: 26px;">NOT</span> <br><span style="color: #aa0d91;line-height: 26px;">NULL</span> ,\r\n <span style="color: #c41a16;line-height: 26px;">`test_name`</span> <span style="color: #5c2699;line-height: 26px;">varchar</span>(<span style="color: #1c00cf;line-height: 26px;">20</span>) <span style="color: #aa0d91;line-height: 26px;">DEFAULT</span> <span style="color: #aa0d91;line-height: 26px;">NULL</span>,\r\n <span style="color: #c41a16;line-height: 26px;">`create_time`</span> <span style="color: #5c2699;line-height: 26px;">timestamp</span> <span style="color: #aa0d91;line-height: 26px;">NOT</span> <span style="color: #aa0d91;line-height: 26px;">NULL</span> <span style="color: #aa0d91;line-height: 26px;">DEFAULT</span> <span style="color: #aa0d91;line-height: 26px;">CURRENT_TIMESTAMP</span> <span style="color: #aa0d91;line-height: 26px;">COMMENT</span> \<span style="color: #c41a16;line-height: 26px;">'创建时间\'</span>,\r\n <span style="color: #c41a16;line-height: 26px;">`update_time`</span> <span style="color: #5c2699;line-height: 26px;">timestamp</span> <span style="color: #aa0d91;line-height: 26px;">NOT</span> <span style="color: #aa0d91;line-height: 26px;">NULL</span> <span style="color: #aa0d91;line-height: 26px;">DEFAULT</span> <span style="color: #aa0d91;line-height: 26px;">CURRENT_TIMESTAMP</span> <span style="color: #aa0d91;line-height: 26px;">ON</span> <span style="color: #aa0d91;line-height: 26px;">UPDATE</span> C<br>URRENT_TIMESTAMP <span style="color: #aa0d91;line-height: 26px;">COMMENT</span> \<span style="color: #c41a16;line-height: 26px;">'修改时间\'</span>,\r\n PRIMARY <span style="color: #aa0d91;line-height: 26px;">KEY</span> (<span style="color: #c41a16;line-height: 26px;">`increment_id`</span>)\r\n) <span style="color: #aa0d91;line-height: 26px;">ENGINE</span>=<span style="color: #aa0d91;line-height: 26px;">InnoDB</span> <span style="color: #aa0d91;line-height: 26px;">DEFAULT</span> <span style="color: #aa0d91;line-height: 26px;">CHARSET</span>=utf8 <span style="color: #aa0d91;line-height: 26px;">COMMENT</span>=\<span style="color: #c41a16;line-height: 26px;">'测试table\'',0<br>20220612 15:23:09,mysqlhost2,test_user,10.30.21.95,129,426,QUERY,testdb,'</span><span style="color: #aa0d91;line-height: 26px;">insert</span> <span style="color: #aa0d91;line-height: 26px;">into</span> test_tb0 (test_id,test_name) <span style="color: #aa0d91;line-height: 26px;">values</span> (<span style="color: #1c00cf;line-height: 26px;">1001</span>,\<span style="color: #c41a16;line-height: 26px;">'4343df\'</span>),(<span style="color: #1c00cf;line-height: 26px;">1002</span>,\<span style="color: #c41a16;line-height: 26px;">'dfd\'</span>)<span style="color: #c41a16;line-height: 26px;">',0<br>20220612 15:23:22,mysqlhost2,test_user,10.30.21.95,129,433,QUERY,testdb,'</span><span style="color: #aa0d91;line-height: 26px;">delete</span> <span style="color: #aa0d91;line-height: 26px;">from</span> test_tb0<span style="color: #c41a16;line-height: 26px;">',0<br>20220612 15:24:14,mysqlhost2,test_user,10.30.21.95,129,448,QUERY,testdb,'</span><span style="color: #aa0d91;line-height: 26px;">create</span> <span style="color: #aa0d91;line-height: 26px;">table</span> test_tb0 (<span style="color: #aa0d91;line-height: 26px;">id</span> <span style="color: #5c2699;line-height: 26px;">int</span>)<span style="color: #c41a16;line-height: 26px;">',1050<br>20220612 15:24:25,mysqlhost2,test_user,10.30.21.95,129,452,QUERY,testdb,'</span><span style="color: #aa0d91;line-height: 26px;">drop</span> <span style="color: #aa0d91;line-height: 26px;">table</span> test_tb0<span style="color: #c41a16;line-height: 26px;">',0<br>20220612 15:25:13,mysqlhost2,test_user,10.30.21.95,126,0,DISCONNECT,testdb,,0<br><br># 连接审计主要审计连接数据库、断开连接、连接失败等操作,其日志格式如下:<br>[timestamp],[serverhost],[username],[host],[connectionid],0,CONNECT,[database],,0<br>[timestamp],[serverhost],[username],[host],[connectionid],0,DISCONNECT,,,0<br>[timestamp],[serverhost],[username],[host],[connectionid],0,FAILED_CONNECT,,,[retcode]<br><br># QUERY审计各种数据库变更事件,执行失败也会记录,其日志记录格式如下:<br>[timestamp],[serverhost],[username],[host],[connectionid],[queryid],QUERY,[database],[object], [retcode]</span></code></pre> </section> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">至此,我们基本上完成审计插件的初步使用,从审计日志内容中我们可以看出,记录的格式还是很清晰详细的,每列内容都是需要的,根据日志很容易查到对应的操作。使用下来,笔者觉得 server_audit 审计插件基本能满足审计需求,不过审计插件也是有优缺点的,优劣势整理如下:</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">server_audit 审计插件优势:</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <ul class="list-paddingleft-1" style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">丰富的审计内容:包括用户连接,关闭,DML操作,存储过程,触发器,事件等。</span></p></li> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">灵活的审计策略:可以自定义审计事件,例如过滤掉select查询,或者排除审计某个用户等。</span></p></li> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">灵活方便:免费使用且安装方便,可以在线开启和停用审计功能。</span></p></li> </ul> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">server_audit 审计插件劣势:</span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <ul class="list-paddingleft-1" style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">开启审计会增加数据库的性能开销,并占用磁盘空间。</span></p></li> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">日志格式不够丰富,不能自定义输出格式。</span></p></li> </ul> <p style="margin: 0;padding: 0;min-height: 24px;"><br></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">参考:</span></strong><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <ul class="list-paddingleft-1" style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://www.cnblogs.com/lijiaman/p/14257861.html</span></p></li> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://www.jianshu.com/p/45b37a73e286</span></p></li> <li style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-right: 8px;margin-left: 8px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;text-size-adjust: auto;font-size: 14px;color: rgb(38, 38, 38);letter-spacing: 0.05em;outline-style: none;line-height: 2em;margin-bottom: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 15px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://mariadb.com/kb/en/mariadb-audit-plugin-options-and-system-variables/</span></p></li> </ul>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0px 10px;word-spacing: 0px;overflow-wrap: break-word;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="margin: 0px 0px 0em;padding: 1em 0px 8px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);color: rgb(74, 74, 74);line-height: 1.75em;visibility: visible;"><img class="rich_pages wxw-img" data-ratio="0.7620370370370371" src="/upload/d11d6c94aefbfd22b359d625a2bc04b7.png" data-type="png" data-w="1080" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;vertical-align: bottom;height: auto !important;visibility: visible !important;width: 677px !important;"></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: right;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 13px;visibility: visible;">作者:小林coding</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: right;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 13px;visibility: visible;">八股文网站:</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 13px;color: rgb(0, 82, 255);visibility: visible;">xiaolincoding.com</span></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">大家好,我是小林。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">之前分享过 <a href="https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&mid=2247502456&idx=1&sn=ea41522693661a4ec9eee923b492a35a&scene=21#wechat_redirect" style="word-wrap: break-word;font-weight: bold;color: #304FFE;text-decoration: none;border-bottom: 1px solid #304FFE;" data-linktype="2">MySQL 死锁</a>的文章,然后很多读者对「插入意向锁」认识很迷糊。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">大家误以为「插入意向锁」是意向锁,也就是表锁,确实这个名字很让人误解。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">但是,<strong style="font-weight: bold;line-height: 1.75em;color: #304FFE;">实际上「插入意向锁」不是意向锁,而是特殊的间隙锁,属于行级锁,注意是「特殊」的间隙锁,并不是我们常说的间隙锁</strong>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">所以,我在原来文章的基础上补充了两个知识点:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 什么是插入意向锁? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> insert 语句是怎么加锁的? </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">提纲如下:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.93359375" src="/upload/e9d12f1cf562e530d98c85d9afd4b597.png" data-type="png" data-w="512" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <h1 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 24px;text-align: center;background-position: center top;background-repeat: no-repeat;background-size: 75px;line-height: 95px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="font-size: 20px;color: #6789c9;border-bottom: 2px solid #8fa3c9;">正文</span></h1> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">有个业务主要逻辑就是新增订单、修改订单、查询订单等操作。然后因为订单是不能重复的,所以当时在新增订单的时候做了幂等性校验,做法就是在新增订单记录之前,先通过 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(100,149,237);">select ... for update</code> 语句查询订单是否存在,如果不存在才插入订单记录。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">而正是因为这样的操作,当业务量很大的时候,就可能会出现死锁。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">接下来跟大家聊下<strong style="font-weight: bold;line-height: 1.75em;color: #304FFE;">为什么会发生死锁,以及怎么避免死锁</strong>。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(65,105,225);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;font-weight: bold;background: rgb(65,105,225);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">死锁的发生</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">本次案例使用存储引擎 Innodb,隔离级别为可重复读(RR)。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">接下来,我用实战的方式来带大家看看死锁是怎么发生的。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">我建了一张订单表,其中 id 字段为主键索引,order_no 字段普通索引,也就是非唯一索引:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/AYfn0IKjIIMadcaOhy591b7YB4eUf5gQ0lQvggJCetcDB1W9vKToyLzMmnmFLryyu2n8zeibaUYPwIwicTjpOnkcRaib9E5Pesf/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">CREATE</span> <span style="color: #c678dd;line-height: 26px;">TABLE</span> <span style="color: #98c379;line-height: 26px;">`t_order`</span> (<br> <span style="color: #98c379;line-height: 26px;">`id`</span> <span style="color: #e6c07b;line-height: 26px;">int</span> <span style="color: #c678dd;line-height: 26px;">NOT</span> <span style="color: #56b6c2;line-height: 26px;">NULL</span> AUTO_INCREMENT,<br> <span style="color: #98c379;line-height: 26px;">`order_no`</span> <span style="color: #e6c07b;line-height: 26px;">int</span> <span style="color: #c678dd;line-height: 26px;">DEFAULT</span> <span style="color: #56b6c2;line-height: 26px;">NULL</span>,<br> <span style="color: #98c379;line-height: 26px;">`create_date`</span> datetime <span style="color: #c678dd;line-height: 26px;">DEFAULT</span> <span style="color: #56b6c2;line-height: 26px;">NULL</span>,<br> PRIMARY <span style="color: #c678dd;line-height: 26px;">KEY</span> (<span style="color: #98c379;line-height: 26px;">`id`</span>),<br> <span style="color: #c678dd;line-height: 26px;">KEY</span> <span style="color: #98c379;line-height: 26px;">`index_order`</span> (<span style="color: #98c379;line-height: 26px;">`order_no`</span>) <span style="color: #c678dd;line-height: 26px;">USING</span> BTREE<br>) <span style="color: #c678dd;line-height: 26px;">ENGINE</span>=<span style="color: #c678dd;line-height: 26px;">InnoDB</span> ;<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">然后,先 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(100,149,237);">t_order</code> 表里现在已经有了 6 条记录:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4646153846153846" src="/upload/3c2b256d04ca7463700f7fa323627d9f.png" data-type="png" data-w="650" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;display: block;font-size: 12px;font-family: PingFangSC-Light;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">假设这时有两事务,一个事务要插入订单 1007 ,另外一个事务要插入订单 1008,因为需要对订单做幂等性校验,所以两个事务先要查询该订单是否存在,不存在才插入记录,过程如下:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4601851851851852" src="/upload/1b39cdeeb66686d51f7582a9be4d37b5.png" data-type="png" data-w="1080" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;display: block;font-size: 12px;font-family: PingFangSC-Light;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">可以看到,两个事务都陷入了等待状态(前提没有打开死锁检测),也就是发生了死锁,因为都在相互等待对方释放锁。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">这里在查询记录是否存在的时候,使用了 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(100,149,237);">select ... for update</code> 语句,目的为了防止事务执行的过程中,有其他事务插入了记录,而出现幻读的问题。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 1em;color: rgb(74,74,74);line-height: 1.75em;">如果没有使用 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,
作者:微信小助手
<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzkzMDI1NjcyOQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/EoJib2tNvVtf7icAmS0BQH6oDVG37Q8NzcfdguS5qAqOhfxvZyIKqmuX5BbnDjynrBbZzktp1EiaeFLzapp1nHysw/0?wx_fmt=png" data-nickname="码哥字节" data-alias="MageByte" data-signature="拥抱硬核技术和对象,面向人民币编程。" data-from="0"></mpprofile> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">我是码哥,可以叫我靓仔。今天码哥手把手带大家摸索下 Redo Log。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><span style="color: rgb(255, 76, 0);"><strong>硬核警告!加粗加红。</strong></span><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><span style="letter-spacing: 0px;">本来 InnoDB 接收到插入、修改、删除这样的 DML 语句,以及创建表 & 索引、修改表结构这样的 DDL 语句,修改 Buffer Pool 中的数据页之后就完事了。</span><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">因为要保证数据不丢失,事情就变的复杂了,修改了数据页不算完,还要生成 Redo 日志,生成了也不算完,还要把它写入 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">Redo 日志文件</code>。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">为了方便描述,本文后面会把 Redo 日志文件简称为<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">日志文件</code>。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">通过以上描述,相信大家能够发现,生成 Redo 日志并写入日志文件,显然是额外操作,会额外消耗资源。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">不惜额外消耗宝贵的服务器资源都要保存下来的东西,肯定不能是个绣花枕头,那这个有用的枕头什么时候能派上用场呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">当然是服务器累了想<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">小憩一下(突然崩溃)</code>的时候了 ^_^。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">服务器也不容易,谁还没有个突然崩溃的时候呢?</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">说了这么多,是时候确定 Redo 日志的历史地位了:<em>Redo 日志,在太平日子里,不但是个鸡肋,更是个累赘,但是,<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">别把它不当英雄,关键时刻还得靠它拯救数据库</code></em>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">饭前甜点到此为止,接下来是正餐。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">本文内容基于 MySQL 8.0.29 源码。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><strong style="font-weight: bold;color: #ff3502;line-height: 1.5;font-size: 16px;">目录</strong></p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li><p>1. 概述</p></li> <li><p>2. Redo 日志产生</p></li> <li><p>3. 写入 log buffer</p></li> <li><p>4. 写入日志文件</p></li> <li><p>5. 日志文件刷盘</p></li> <li><p>6. 总结<strong style="letter-spacing: 0px;color: rgb(255, 53, 2);line-height: 1.5;"></strong></p></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;margin: 80px 10px 40px 10px;text-align: center;font-weight: normal;color: #3f3f3f;font-size: 140%;"><span style="display: none;"></span>1. 概述</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">MySQL 8.0 以前,Redo 日志是<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">串行</code>写入 log buffer 的,多个用户线程想要同时往 log buffer 里写日志,那是不行的,必须<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">排队等待</code>(获取互斥锁),拿到互斥锁之后,才能往 log buffer 里写日志。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">MySQL 8.0 中,串行写入变为<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">并行</code>写入,log buffer 由乡间小道变成了单向 8 车道的高速公路,多个用户线程可以同时往 log buffer 里写入 Redo 日志,效率大大提升。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">Redo 日志从产生到刷盘,一共会经历 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">4 个阶段</code>(产生、写 log buffer、写日志文件、刷盘),本文会用 4 个小节分别介绍这 4 个阶段。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;margin: 80px 10px 40px 10px;text-align: center;font-weight: normal;color: #3f3f3f;font-size: 140%;"><span style="display: none;"></span>2. Redo 日志产生</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">以一条非常简单的<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">插入语句</code>为例,这个语句包含自增列,并且只插入一条记录,我们假设插入过程中不会造成索引页分裂,也不会产生溢出页。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">不考虑 Undo 日志产生的 Redo 日志,这样一条 SQL 语句会包含 2 条 Redo 日志(这 2 条日志会形成一个日志组):</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 一条日志中保存着表中自增列的最大值(MySQL 8.0 把自增列的值持久化了)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 另一条日志中保存着插入记录各字段的值。 </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">每条日志中还有可能会包含 InnoDB 需要的其它信息。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">插入记录的过程中,会先产生一条 Redo 日志用于记录表中自增列的最大值,然后插入记录,再产生另一条 Redo 日志。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">Redo 日志并不会每产生一条就马上写入 log buffer,而是一组 Redo 日志攒到一起往 log buffer 里写。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">问题来了,产生了一条 Redo 日志不能马上写入 log buffer,那怎么办?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">那就需要有一个地方临时存放日志组中不同时间点产生的日志了,这个地方就是 mtr 中的 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">m_log 链表</code>。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2034548944337812" src="/upload/c67d6fe74a46c690b0edfd3f86e0eda8.png" data-type="png" data-w="521" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">m_log 链表是由一个一个 block 组成的链表,block 大小为 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">512 字节</code>,每产生一条日志,就追加到 m_log 的 block 中,如果一个 block 写满了,就再申请一个 block 接着写。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">那 mtr 又是个啥?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">mtr 是 Mini-Transaction 的缩写,是一组不可分隔的操作组成的一个整体,就像前面插入语句的例子中,<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">保存表中自增列的最大值</code>和<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">插入记录</code>就是一组不可分隔的操作,必须放入一个 mtr。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">两个操作放入一个 mtr,它们的日志也就放在同一个 mtr 中了。这样就能保证两个操作产生的 Redo 日志一起写入 log buffer 和日志文件中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">mtr 的用途可不止打包一组 Redo 日志这么简单,它还会对 SQL 执行过程中 mtr 需要访问的 Buffer Pool 中的页加锁、修改页中的数据、释放锁,本文我们只介绍 Redo 日志,对于 mtr 就不再展开了。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">还有一个概念需要解释一下,<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">日志组</code>就是一个 mtr 中的所有日志。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;margin: 80px 10px 40px 10px;text-align: center;font-weight: normal;color: #3f3f3f;font-size: 140%;"><span style="display: none;"></span>3. 写入 log buffer</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">mtr 中一组不可分隔的操作都完成之后,就该提交了,mtr 提交过程中要干的第一件事就是把它里面临时存放的一组 Redo 日志写入到 log buffer 中。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">一个事务中可能会包含多个 mtr,mtr 的提交和事务的提交不是一个概念,不要混淆。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">前面说到在 MySQL 8.0 中,往 log buffer 里写日志不需要排队等待(获取互斥锁),多个用户线程可以同时写入。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">这个无锁化设计是通过在 log buffer 中为每个 mtr 中的 Redo 日志<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">预留空间</code>实现的,每个 mtr 都有一段属于自己的空间,各自往自己专属的空间内写入日志,相互之间就不影响了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">用户线程的 mtr 往 log buffer 写 Redo 日志前,会先获取一段序列号。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">以当前系统中已产生的最大序列号(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">SN</code>)作为 start_sn,加上本次要往 log buffer 中写入的 Redo 日志的字节数(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">len</code>),得到 end_sn(end_sn = start_sn + len)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">start_sn ~ end_sn</code> 就是本次要写入 log buffer 的 Redo 日志的序列号区间。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">获取 start_sn、end_sn 的过程是原子操作,多个线程之间不会出现冲突,不会获取到有交叉的序列号区间。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">拿到 start_sn ~ end_sn 只是第一步,还需要进行一次转换,把序列号(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">SN</code>)转换为日志序列号(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">LSN</code>),得到一个 LSN 的范围:<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">start_lsn ~ end_lsn</code>,这个范围对应着 log_buffer 中为 mtr 即将写入的 Redo 日志预留的空间。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">SN 是截止某个时刻,InnoDB 中实际产生的 Redo 日志字节数。</p> </blockquote> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">SN 按照 496 字节拆分,拆分后<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">每 496 字节</code>,加上 12 字节的头信息、4 字节尾部检验码,得到 512 字节的 block,经过这样的转换之后,得到的数字就是 LSN。</p> </blockquote> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5781637717121588" src="/upload/9bc7ffb426719706d724b2a3576301dd.png" data-type="png" data-w="806" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">至此,写入日志到 log buffer 的准备工作又往前推进了一步。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">但是,别着急,<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">也许</code>还要再等等,如果 log buffer 中剩余空间不够写入当前 mtr 的 Redo 日志,那就需要等到 log buffer 中的 Redo 日志被写入日志文件,为当前 mtr 的 Redo 日志腾出空间才行。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">这里的<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">写入日志文件</code>,只是调用了操作系统的写文件方法,把 Redo 日志写入日志文件的操作系统缓冲区中,日志文件暂时还不会刷新到磁盘上。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">那怎么判断 log buffer 中是否有空间呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">要回答这个问题,我们需要先介绍一个属性 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log_sys.write_lsn</code>,表示 LSN <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">小于</code> log_sys.writen_lsn 的日志都已经写入到日志文件缓冲区中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">end_sn <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;"><=</code> log_sys.write_lsn + <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">innodb_log_buffer_size</code>(默认 16M),就表示 log buffer 中有空间写入当前 mtr 的 Redo 日志。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">如果要等,总不能一直等吧,等到什么时候是个头呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">如果需要等待,用户线程会监听 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log.write_events</code> 事件,log buffer 中有空间写入 Redo 日志之后,当前用户线程会收到事件通知。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">谁会给这些等待的用户线程发送事件通知呢?后面会有介绍,请继续往下看。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">等到 log buffer 中有空间之后,往里面写入日志就很简单了,直接把 mtr 中的 Redo 日志拷贝到 log buffer 中就完事了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">写完之后,还需要根据 mtr 的 start_lsn 在 recent_written.m_links 中找到对应的 SLOT,然后把 mtr 的 end_lsn 写入这个 SLOT,表示这个 mtr 已经把它的全部 Redo 日志写入 log buffer 了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">如果根据 start_lsn 在 recent_written.m_links 中找到的 SLOT 正在被其它 mtr 使用,当前这个用户线程会采用<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">循环 + 间隔休眠 20 毫秒</code>的方式,直到 SLOT 可以使用。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">前面两段涉及到 recent_written 的介绍,大家看了可能会觉得一头雾水,先不要着急,有个模糊印象就行。<br>因为这两段逻辑是在<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">写日志到 log buffer</code> 这个阶段发生的,所以这里必须要提一下露个脸,相当于占个位,但是详细介绍放到 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">4. 写入日志文件</code>小节更合适。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">说完了写入 Redo 日志到 log buffer,我们回到用户线程<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">等待</code> log buffer 中有空间写入它的 Redo 日志,这个等待过程是个躺平的过程,在这个过程中,用户线程除了等待事件通知,其它事情啥也不干。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">在用户线程看来,等待的过程中岁月静好,但是,世上本来没有岁月静好,它感受到的岁月静好,无非是因为有人替它负重前行。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">谁在负重前行?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">那是另一个默默工作的线程,它的名字叫作 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log_writer</code>,它是一个搬运工,一个专门把 log buffer 中的 Redo 日志写入到日志文件的线程。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">log_writer 线程只调用操作系统写文件方法,把 Redo 日志写入日志文件,不会刷新到磁盘上,此时,Redo 日志还在日志文件的操作系统缓冲区中。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">接下来,就是 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log_writer</code> 线程的主场了。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;margin: 80px 10px 40px 10px;text-align: center;font-weight: normal;color: #3f3f3f;font-size: 140%;"><span style="display: none;"></span>4. 写入日志文件</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">log writer 线程把 log buffer 中的 Redo 日志写入日志文件缓冲区,写入的这一段 Redo 日志必须是连续的,中间不能出现空洞。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2459396751740139" src="/upload/89cb417c54b8ed1f1ef4d3c8c52d8f8f.png" data-type="png" data-w="431" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">上一个步骤中,不同用户线程可以并行把各自 mtr 中的 Redo 日志写入 log buffer 中,解决了写入速度慢的问题,同时也带来了新问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">不同用户线程的 Redo 日志量可能不一样,有的线程会先写完,有的线程后写完,如果某一个范围内,头部的日志写完了,尾部的日志也写完了,中间的日志还没写完,这就出现了空洞。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><strong style="font-weight: bold;color: #ff3502;line-height: 1.5;font-size: 16px;">举个例子</strong>,假设有 3 个不同的用户线程,各有一个 mtr 要提交,我们把这 3 个用户线程的 mtr 分别叫作 mtr 10、mtr 11、mtr 12。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">mtr 10</code> 的 Redo 日志占用 200 字节,LSN 范围是 start_lsn(2097252) ~ end_lsn(2097452)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">mtr 11</code> 的 Redo 日志占用 12045 字节,LSN 范围是 start_lsn(2097452) ~ end_lsn(2109497)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">mtr 12</code> 的 Redo 日志占用 300 字节,LSN 范围是 start_lsn(2109497) ~ end_lsn(2109797)。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left: 3px solid rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding: 1px 0px 1px 10px;margin: 20px 0px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: #3f3f3f;line-height: 1.5;font-size: 16px;margin: 10px;padding: 0px;">每一个 mtr 的 end_lsn 其实是不属于它的,而是属于下一个 mtr,是下一个 mtr 的 start_lsn。所以,每个 mtr 的 LSN 范围是一个<span style="color: rgb(255, 53, 2);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 14.4px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(248, 245, 236);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"><span style="color: rgb(255, 53, 2);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 14.4px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(248, 245, 236);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">左闭右开</span></span>区间,例如:mtr 10 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">[2097252, 2097452)</code>。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">mtr 10、mtr 12 的日志比较小,mtr 11 的日志比较大,可能会存在这样的情况,mtr 10、mtr 12 的日志都已经全部写入 log buffer,mtr 11 的日志只有一部分写入了 log buffer,中间是存在空洞的。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.44511278195488724" src="/upload/6a3b1fe83c3f6964fe879bdcc0361fae.png" data-type="png" data-w="665" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">因为存在空洞,log_writer 线程<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">不能</code>把 mtr 10 ~ 12 的 Redo 日志都写入日志文件,只能把 mtr 10 的 Redo 日志写入日志文件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">等到 mtr 11 的 Redo 日志全部写入 log buffer 之后,才能把 mtr 11 ~ 12 的 Redo 日志一起写入日志文件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">那它怎么知道截止到哪个位置的日志是连续的,可以写入日志文件的呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">也许我们都能很快想到用一个变量把这个位置记录下来就好了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">没错,InnoDB 也是这么干的,全局日志对象(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log_sys</code>)中,有一个 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">recent_written</code> 属性,这个属性也是个对象,它有一个属性 m_tail(<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">log_sys.recent_written.m_tail</code>),用来记录 log buffer 中<code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">小于</code>哪个 LSN 的日志都是连续的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;">知道了用什么记,现在有个关键问题,那就是怎么记?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: #3f3f3f;font-size: 16px;margin: 10px 0px;"><code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;background: #f8f5ec;color: #ff3502;line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">recent_written</code> 对象,有个属性 <code style="word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);
作者:微信小助手
<section style="letter-spacing: 0.5px;line-height: 1.5;box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;margin-bottom: 24px;" data-mpa-powered-by="yiban.io"> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;vertical-align: top;padding-left: 10px;box-sizing: border-box;"> <section style="border-bottom: 1px dashed rgb(0, 0, 0);padding-left: 5px;font-size: 20px;color: rgb(12, 182, 242);box-sizing: border-box;"> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzAwMjI0ODk0NA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/SfAHMuUxqJ2JicQpKzblLHz64qoibVa3ATNA4rH8mIYXAF3OErAzxFKHzf5qiaiblb4rAMuAXXMJHEcKcvaHv4ia9rA/0?wx_fmt=png" data-nickname="悟空聊架构" data-alias="PassJava666" data-signature="用故事讲解分布式、架构。 《 JVM 性能调优实战》专栏作者, 《Spring Cloud 实战 PassJava》开源作者, 自主开发了 PMP 刷题小程序。" data-from="0"></mpprofile> </section> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;text-align: left;"><strong style="box-sizing: border-box;">你好,我是悟空。</strong></p> <p style="white-space: normal;margin: 0px 0px 24px;padding: 0px;box-sizing: border-box;letter-spacing: 0.5px;line-height: 1.5;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;">还记得去年这个时候,B 站崩了吗?</p> <p style="white-space: normal;margin: 0px 0px 24px;padding: 0px;box-sizing: border-box;letter-spacing: 0.5px;line-height: 1.5;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;">如果不记得,请回看我这篇:<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451956637&idx=1&sn=faf59d9804f8fc49045cf9916e2a613a&chksm=8d1c1a02ba6b93143bd4f12af37a8650160c5d070cd688b64fa2b4b0271c23dc871c0e1b0280&scene=21#wechat_redirect" textvalue="B 站崩了,总结下「高可用」和「异地多活」" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">B 站崩了,总结下「高可用」和「异地多活」</a>。(只用看文中的背景内容就可以了,其他内容不用看,和 B 站的复盘差远了)</p> <p style="white-space: normal;margin: 0px 0px 24px;padding: 0px;box-sizing: border-box;letter-spacing: 0.5px;line-height: 1.5;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;">下图是当时手机 APP 端 B 站崩了的截图:</p> <p style="white-space: normal;margin: 0px 0px 24px;padding: 0px;box-sizing: border-box;letter-spacing: 0.5px;line-height: 1.5;font-style: normal;font-weight: 400;text-align: center;font-size: 16px;"><img class="rich_pages wxw-img" data-ratio="0.9813953488372092" src="/upload/4eb8b752fb682f1a7ad501c767d4e1fc.png" data-w="645" style="width: 278px;height: 273px;"></p> <p style="white-space: normal;margin: 0px 0px 24px;padding: 0px;box-sizing: border-box;letter-spacing: 0.5px;line-height: 1.5;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;">现在 B 站的大佬来复盘了,而且他们还对此事故做了很多改进,我看完后,直呼牛�
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">前面两篇文章介绍了Nacos的基础、注册中心的原理,如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&mid=2247493854&idx=1&sn=4b3fb7f7e17a76000733899f511ef915&chksm=fcf73713cb80be05fe4473390f946dfbaf77848d7041c30f069bcb5a3629be782f4b1121bd6a&scene=21&cur_album_id=2042874937312346114#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: rg
作者:微信小助手
<section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzkyNzExODM3OA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/FibLXRKztelWZcHdUd1gtkSCYxXrd3Hj45GpTLdljGiaykvo8dyHibqO0hIfiad3lqFo7wmoamWfRCPat8HLicm87kw/0?wx_fmt=png" data-nickname="Java仓库" data-alias="" data-signature="专注Java全栈开发,分享实用技术干货。" data-from="0"></mpprofile> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <section data-class="_mbEditor" data-id="132148" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> <section data-tools-id="46476" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Arial;border-width: 0px 0px 2px;border-style: none none solid;color: rgb(49, 147, 105);border-color: rgb(239, 112, 96);visibility: visible;"> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 32px;display: inline-block;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(239, 112, 96);visibility: visible;">基础</p> </section> </section> </section> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">1、说明:创建数据库</span></strong> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">CREATE DATABASE database-name</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">2、说明:删除数据库</span></strong> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">drop database dbname</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">3、说明:备份sql server</span></strong> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;visibility: visible;"> <pre style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;background: none;visibility: visible;"><code style="margin: 0px 0.15em;padding: 5.95px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;border-radius: 4px;font-size: 0.85em;background: rgb(35, 36, 31);color: rgb(248, 248, 242);display: block;overflow-x: auto;white-space: nowrap;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 12px;font-family: CourierNewPS-ItalicMT;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(117, 113, 94);background: rgba(0, 0, 0, 0);display: inline;width: 163px;text-decoration: none solid rgb(117, 113, 94);font-weight: 400;font-style: normal;visibility: visible;">--- 创建 备份数据的 device</span><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">USE</span> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">master</span><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;">EXEC sp_addumpdevice <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(230, 219, 116);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(230, 219, 116);font-weight: 400;font-style: normal;visibility: visible;">'disk'</span>, <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(230, 219, 116);background: rgba(0, 0, 0, 0);display: inline;width: 66px;text-decoration: none solid rgb(230, 219, 116);font-weight: 400;font-style: normal;visibility: visible;">'testBack'</span>, <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(230, 219, 116);background: rgba(0, 0, 0, 0);display: inline;width: 205px;text-decoration: none solid rgb(230, 219, 116);font-weight: 400;font-style: normal;visibility: visible;">'c:\mssql7backup\MyNwind_1.dat'</span><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(117, 113, 94);background: rgba(0, 0, 0, 0);display: inline;width: 81px;text-decoration: none solid rgb(117, 113, 94);font-weight: 400;font-style: normal;">--- 开始 备份</span><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">BACKUP</span> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 53px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">DATABASE</span> pubs <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">TO</span> testBack</span></code></pre> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">4、说明:创建新表</span></strong> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;"></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">根据已有的表创建新表: </span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">A:create table tab_new like tab_old (使用旧表创建新表)</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">B:create table tab_new as select col1,col2… from tab_old definition only</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">5、说明:删除新表</span></strong> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;"></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">drop table tabname </span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);text-align: left;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">6、说明:增加一个列</span></strong> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;"></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0 10px;word-spacing: 0px;word-wrap: break-word;text-align: left;line-height: 1.6;letter-spacing: .034em;color: rgb(63, 63, 63);font-size: 16px;word-break: all;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">spring batch是spring提供的一个数据处理框架。企业域中的许多应用程序需要批量处理才能在关键任务环境中执行业务操作。这些业务运营包括:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 无需用户交互即可最有效地处理大量信息的自动化,复杂处理。这些操作通常包括基于时间的事件(例如月末计算,通知或通信)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 在非常大的数据集中重复处理复杂业务规则的定期应用(例如,保险利益确定或费率调整)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 集成从内部和外部系统接收的信息,这些信息通常需要以事务方式格式化,验证和处理到记录系统中。批处理用于每天为企业处理数十亿的交易。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Spring Batch是一个轻量级,全面的批处理框架,旨在开发对企业系统日常运营至关重要的强大批处理应用程序。Spring Batch构建了人们期望的Spring Framework特性(生产力,基于POJO的开发方法和一般易用性),同时使开发人员可以在必要时轻松访问和利用更高级的企业服务。Spring Batch不是一个schuedling的框架。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Spring Batch提供了可重用的功能,这些功能对于处理大量的数据至关重要,包括记录/跟踪,事务管理,作业处理统计,作业重启,跳过和资源管理。它还提供更高级的技术服务和功能,通过优化和分区技术实现极高容量和高性能的批处理作业。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Spring Batch可用于两种简单的用例(例如将文件读入数据库或运行存储过程)以及复杂的大量用例(例如在数据库之间移动大量数据,转换它等等) 上)。大批量批处理作业可以高度可扩展的方式利用该框架来处理大量信息。</p> <h2 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 22px;display: block;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mSUh3DeHsewe3SF1JB4fNJXPtCZwGlcQQ5AxjIDg5jhAQ8fI7Hvr7Dg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="text-align: center;display: inline-block;height: 38px;line-height: 42px;color: rgb(60, 112, 198);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">- Spring Batch架构介绍</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">一个典型的批处理应用程序大致如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 从数据库,文件或队列中读取大量记录。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 以某种方式处理数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 以修改之后的形式写回数据。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">其对应的示意图如下:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.19736842105263158" src="/upload/3560a4adc99a8a183119b45cf4a92421.png" data-type="png" data-w="912" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">spring batch的一个总体的架构如下:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3983739837398374" src="/upload/dd003ae5a774a08080560744e1de9d0b.png" data-type="png" data-w="738" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">在spring batch中一个job可以定义很多的步骤step,在每一个step里面可以定义其专属的ItemReader用于读取数据,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">ItemProcesseor</code>用于处理数据,ItemWriter用于写数据,而每一个定义的job则都在<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobRepository</code>里面,我们可以通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobLauncher</code>来启动某一个job。<span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">关注公众号:</span><span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">“码猿技术专栏</span><span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">”,回复关键词:</span><span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">“081</span><span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">“ 获取阿里内部的Spring Cloud Alibaba进阶教程</span><span style="color: rgb(74, 74, 74);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">!</span></p> <h2 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 22px;display: block;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mSUh3DeHsewe3SF1JB4fNJXPtCZwGlcQQ5AxjIDg5jhAQ8fI7Hvr7Dg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="text-align: center;display: inline-block;height: 38px;line-height: 42px;color: rgb(60, 112, 198);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">Spring Batch核心概念介绍</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">下面是一些概念是Spring batch框架中的核心概念。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mY0zXqiaNicvhQrACwrSV4QJboic56ib07hulzuPCewn30ctFQicHs0rcd0A/640?wx_fmt=png");background-size: 15px 15px;display: inline-block;width: 15px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 16px;font-weight: bold;display: inline-block;margin-left: 8px;color: rgb(60,112,198);">什么是Job</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Job和Step是spring batch执行批处理任务最为核心的两个概念。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">其中Job是一个封装整个批处理过程的一个概念。Job在spring batch的体系当中只是一个最顶层的一个抽象概念,体现在代码当中则它只是一个最上层的接口,其代码如下: </p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * Batch domain object representing a job. Job is an explicit abstraction<br> * representing the configuration of a job specified by a developer. It should<br> * be noted that restart policy is applied to the job as a whole and not to a<br> * step.<br> */</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">Job</span> </span>{<br> <br> <span style="line-height: 26px;">String <span style="color: #4078f2;line-height: 26px;">getName</span><span style="line-height: 26px;">()</span></span>;<br> <br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">isRestartable</span><span style="line-height: 26px;">()</span></span>;<br> <br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">execute</span><span style="line-height: 26px;">(JobExecution execution)</span></span>;<br> <br> <br> <span style="line-height: 26px;">JobParametersIncrementer <span style="color: #4078f2;line-height: 26px;">getJobParametersIncrementer</span><span style="line-height: 26px;">()</span></span>;<br> <br> <br> <span style="line-height: 26px;">JobParametersValidator <span style="color: #4078f2;line-height: 26px;">getJobParametersValidator</span><span style="line-height: 26px;">()</span></span>;<br> <br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">在Job这个接口当中定义了五个方法,它的实现类主要有两种类型的job,一个是simplejob,另一个是flowjob。在spring batch当中,job是最顶层的抽象,除job之外我们还有<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>以及<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobExecution</code>这两个更加底层的抽象。推荐Java之家:www.java-fmaily.cn</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">一个job是我们运行的基本单位,它内部由step组成。job本质上可以看成step的一个容器。一个job可以按照指定的逻辑顺序组合step,并提供了我们给所有step设置相同属性的方法,例如一些事件监听,跳过策略。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Spring Batch以<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">SimpleJob</code>类的形式提供了Job接口的默认简单实现,它在Job之上创建了一些标准功能。一个使用<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">java config</code>的例子代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Bean</span><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Job <span style="color: #4078f2;line-height: 26px;">footballJob</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.jobBuilderFactory.get(<span style="color: #50a14f;line-height: 26px;">"footballJob"</span>)<br> .start(playerLoad())<br> .next(gameLoad())<br> .next(playerSummarization())<br> .end()<br> .build();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">这个配置的意思是:首先给这个job起了一个名字叫<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">footballJob</code>,接着指定了这个job的三个step,他们分别由方法,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">playerLoad,gameLoad, playerSummarization</code>实现。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mY0zXqiaNicvhQrACwrSV4QJboic56ib07hulzuPCewn30ctFQicHs0rcd0A/640?wx_fmt=png");background-size: 15px 15px;display: inline-block;width: 15px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 16px;font-weight: bold;display: inline-block;margin-left: 8px;color: rgb(60,112,198);">什么是JobInstance</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">我们在上文已经提到了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>,他是Job的更加底层的一个抽象,他的定义如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">JobInstance</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * Get unique id for this JobInstance.<br> * <span style="color: #a626a4;line-height: 26px;">@return</span> instance id<br> */</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">long</span> <span style="color: #4078f2;line-height: 26px;">getInstanceId</span><span style="line-height: 26px;">()</span></span>;<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * Get job name.<br> * <span style="color: #a626a4;line-height: 26px;">@return</span> value of 'id' attribute from <job><br> */</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> String <span style="color: #4078f2;line-height: 26px;">getJobName</span><span style="line-height: 26px;">()</span></span>; <br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">他的方法很简单,一个是返回Job的id,另一个是返回Job的名字。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>指的是job运行当中,作业执行过程当中的概念。Instance本就是实例的意思。关注公众号:“码猿技术专栏”,回复关键词:“081“ 获取阿里内部的Spring Cloud Alibaba进阶教程!</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">比如说现在有一个批处理的job,它的功能是在一天结束时执行行一次。我们假定这个批处理job的名字为'<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">EndOfDay</code>'。在这个情况下,那么每天就会有一个逻辑意义上的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>, 而我们必须记录job的每次运行的情况。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mY0zXqiaNicvhQrACwrSV4QJboic56ib07hulzuPCewn30ctFQicHs0rcd0A/640?wx_fmt=png");background-size: 15px 15px;display: inline-block;width: 15px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 16px;font-weight: bold;display: inline-block;margin-left: 8px;color: rgb(60,112,198);">什么是JobParameters</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">在上文当中我们提到了,同一个job每天运行一次的话,那么每天都有一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">jobIntsance</code>,但他们的job定义都是一样的,那么我们怎么来区别一个job的不同<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">jobinstance</code>了。不妨先做个猜想,虽然<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">jobinstance</code>的job定义一样,但是他们有的东西就不一样,例如运行时间。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">spring batch中提供的用来标识一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">jobinstance</code>的东西是:<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobParameters</code>。<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobParameters</code>对象包含一组用于启动批处理作业的参数,它可以在运行期间用于识别或甚至用作参考数据。我们假设的运行时间,就可以作为一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobParameters</code>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">例如, 我们前面的'EndOfDay'的job现在已经有了两个实例,一个产生于1月1日,另一个产生于1月2日,那么我们就可以定义两个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobParameter</code>对象:一个的参数是01-01, 另一个的参数是01-02。因此,识别一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>的方法可以定义为:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2273449920508744" src="/upload/7f3d05c70083592781e203d70e62c869.png" data-type="png" data-w="1258" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">因此,我么可以通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Jobparameter</code>来操作正确的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JfTPiahTHJhrm1nfla2H4oZWqzibjElG0mY0zXqiaNicvhQrACwrSV4QJboic56ib07hulzuPCewn30ctFQicHs0rcd0A/640?wx_fmt=png");background-size: 15px 15px;display: inline-block;width: 15px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 16px;font-weight: bold;display: inline-block;margin-left: 8px;color: rgb(60,112,198);">什么是JobExecution</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobExecution</code>指的是单次尝试运行一个我们定义好的Job的代码层面的概念。job的一次执行可能以失败也可能成功。只有当执行成功完成时,给定的与执行相对应的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>才也被视为完成。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">还是以前面描述的EndOfDay的job作为示例,假设第一次运行<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">01-01-2019</code>的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>结果是失败。那么此时如果使用与第一次运行相同的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Jobparameter</code>参数(即01-01-2019)作业参数再次运行,那么就会创建一个对应于之前<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">jobInstance</code>的一个新的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobExecution</code>实例,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobInstance</code>仍然只有一个。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">JobExecution</code>的接口定义如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">JobExecution</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<b
作者:微信小助手
<p style="white-space: normal;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"><span style="font-size: 15px;text-align: left;">今天聊聊在项目中遇到的一次事故,来一起复盘下吧。</span><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;margin-bottom: 0px;text-align: left;"> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">事故现场</span></strong></h2> <p><span style="font-size: 15px;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="white-space: normal;margin-bottom: 0px;text-align: left;"> <li style="white-space: normal;margin-bottom: 0px;text-align: left;font-size: 15px;"> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;">环境:测试环境</span> </section></li> <li style="white-space: normal;margin-bottom: 0px;text-align: left;font-size: 15px;"> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;">时间:上午10:30</span> </section></li> <li style="white-space: normal;margin-bottom: 0px;text-align: left;font-size: 15px;"> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;">反馈人员:测试群,炸锅了,研发同事初步排查后,发现可能是数据库问题。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">然后就开始找原因吧。因为这套集群环境是我部署的,所以我来排查的话轻车熟路。</span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></h2> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><strong><span style="color: rgb(171, 25, 66);"><span style="color: rgb(171, 25, 66);font-size: 15px;">系统部署图</span></span></strong></h2> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">先说下系统的部署图,方便大家理解。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">两个数据库部署在 node55 和 node56 节点上,它们互为主从关系,所以叫做双主。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: center;"><span style="font-size: 15px;"></span><img class="rich_pages wxw-img" data-ratio="0.29970674486803517" src="/upload/6fd8f526e2672f26decdf89a3c253013.png" data-type="png" data-w="1705" style="white-space: normal;margin-bottom: 0px;"></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">还有两个 Keepalived 部署在 node55 和 node56 上面,分别监控 MySQL 容器的状态。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"></span><img class="rich_pages wxw-img" data-ratio="0.5388739946380697" src="/upload/6f8f04fcc8f290cd0a6fea12f0e26e70.png" data-type="png" data-w="1492" style="white-space: normal;margin-bottom: 0px;"></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: center;"><span style="font-size: 12px;color: rgb(136, 136, 136);">报错原因和解决方案</span></h2> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">我第一个想法就是,不是有 Keepalived 来保证高可用么?即使 MySQL 挂了,也可以通过 Keepalived 来自动重启才对。即使一台重启不起来,还有另外一台可以用的吧?</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">那就到服务器上看下 MySQL 容器的状态吧。到 MySQL 的两台服务器上,先看下 MySQL 容器的状态,docker ps 命令,发现两台 MySQL 容器都不在列表中,这代表容器没正常运行。</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.08527131782945736" src="/upload/38d4e51184567538b8a745097d0c1de.png" data-type="png" data-w="387" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">这不可能。我可是安装了 Keepalived 高可用组件的,难道 Keepalived 也挂了?</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">赶紧检查一波 Keepalived,发现两台 Keepalived 是正常运行的。通过执行命令查看:systemctl status keepalived。</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.4024896265560166" src="/upload/d71e00a7f2affc214874c45919158c50.png" data-type="png" data-w="964" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;"><br></span> </section> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;">纳尼?Keepalived 也是正常的。Keepalived 每隔几秒会重启 MySQL,可能我在那一小段空闲时间没看到 MySQL 容器启动?换个命令执行下,docker ps -a,列出所有容器的状态。可以看到 MySQL 启动后又退出了,说明 MySQL 确实是在重启。</span> </section> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;"><br></span> </section> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;"></span> </section> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.31911532385466035" src="/upload/93e6a115b77dda2305f8668801a7f59c.png" data-type="png" data-w="633" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">那说明 Keepalived 虽然重启了 MySQL 容器,但是 MySQL 自身有问题,那 Keepalived 的高可用也没办法了。</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">那怎么整?只能看下 MySQL 报什么错了。执行查看容器日志的命令。docker logs <容器 id>。找到最近发生的日志:</span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.16630669546436286" src="/upload/3d5d7cc40172437c8b17a0744dbf0aa7.png" data-type="png" data-w="926" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;"><br></span> </section> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;">提示 mysql-bin.index 文件不存在,这个文件是配置在主从同步那里的,在 my.cnf 配置里面。</span> </section> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <span style="font-size: 15px;"><br></span> </section> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.4950711938663746" src="/upload/536a007e096b79f2e9cabfb2a2319008.png" data-type="png" data-w="913" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">这个配置好后,然后执行主从同步的时候,就会在 var/lib/mysql/log 目录下生成多个 mysql-bin.xxx 的文件。还有一个 mysql-bin.index 索引文件,它会标记现在 binlog 日志文件记录到哪里了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.48132780082987553" src="/upload/dab587d05fe72b3c8cb68e4328dc7483.png" data-type="png" data-w="241" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">mysql-bin.index 文件里面的内容如下:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">/<span class="code-snippet__keyword">var</span>/lib/mysql/log/mysql-bin<span class="code-snippet__number">.000001</span></span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">这个 mysql-bin.000001 文件还是带序号的,这里还有坑,后面我再说。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">报错信息是提示缺少 mysql-bin.index,那我们就去检查下呗,确实没有啊!先不管</span><span style="font-size: 15px;">这个文件怎么消失的吧,赶紧把这个 log 文件夹先创建出来,然后 mysql 会自动给我们生成这个文件的。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">解决方案:执行以下命令创建文件夹和添加权限。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="perl"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">mkdir</span> <span class="code-snippet__keyword">log</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">chmod</span> <span class="code-snippet__number">777</span> <span class="code-snippet__keyword">log</span> -R</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">两台服务器上都有这个 log 目录后,Keepalived 也帮我们自动重启好了 MySQL 容器,再来访问下其中一个节点 node56 的 MySQL 的状态。咦?居然报错了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: center;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: center;"><span style="font-size: 15px;"></span><img class="rich_pages wxw-img" data-ratio="0.4884819846426462" src="/upload/b3ce2317db4cc71991f1f4895799d113.png" data-type="png" data-w="1693" style="white-space: normal;margin-bottom: 0px;"></p> <pre data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">Last_IO_Error: Got fatal error <span class="code-snippet__number">1236</span> <span class="code-snippet__keyword">from</span> master when reading data <span class="code-snippet__keyword">from</span> binary log: <span class="code-snippet__string">'Could not find first log file name in binary log index file'</span></span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"></span></pre> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">可以看到几个关键信息:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="white-space: normal;margin-bottom: 0px;text-align: left;"> <li style="white-space: normal;margin-bottom: 0px;text-align: left;font-size: 15px;"> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <strong><span style="font-size: 15px;">Slave_IO_Running: NO</span></strong> <span style="font-size: 15px;">,当前同步的 I/O 线程没有运行,这个 I/O 线程是从库的,它会去请求主库的 binlog,并将得到的 binlog 写到本地的 relay-log (中继日志)文件中。没有运行,则代表从库同步是没有正常运行。</span> </section></li> <li style="white-space: normal;margin-bottom: 0px;text-align: left;font-size: 15px;"> <section style="white-space: normal;margin-bottom: 0px;text-align: left;"> <strong><span style="font-size: 15px;">Master_Log_File: mysql-bin.000014</span></strong> <span style="font-size: 15px;">,说明当前同步的日志文件为 000014,之前我们看到节点 node56 上 mysql.index 里面写的是 000001,这个 000014 根本就不在 index 文件里面,所以就会报错了。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">这里涉及到主从同步的原理,上一张图:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: center;"><span style="font-size: 15px;"></span><img class="rich_pages wxw-img" data-ratio="1.0869565217391304" src="/upload/c1f4d7cc082131cfed72a7245efaf5fd.png" data-type="png" data-w="943" style="white-space: normal;margin-bottom: 0px;"></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li style="font-size: 15px;"><p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">从库会生成两个线程,一个 I/O 线程,一个 SQL 线程;</span></p></li> <li style="font-size: 15px;"><p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">I/O线程会去请求主库的 binlog 日志文件,并将得到的 binlog 日志文件 写到本地的 relay-log (中继日志)文件中;</span></p></li> <li style="font-size: 15px;"><p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">主库会生成一个 dump 线程,用来给从库 I/O 线程传 binlog;</span></p></li> <li style="font-size: 15px;"><p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">SQL线程,会读取 relay log 文件中的日志,并解析成 SQL 语句逐一执行。</span></p></li> </ul> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 14px;color: rgb(136, 136, 136);">那好办啊,我们重新指定下同步哪个日志文件,以及同步的位置就好了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><strong><span style="font-size: 15px;color: rgb(171, 25, 66);">解决方案</span></strong></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">看下主库 node55 上日志文件状态。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.14285714285714285" src="/upload/5244d972fb1b6f4905ae06aeeaad7b52.png" data-type="png" data-w="693" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">记下这两个信息:File=mysql-bin.00001,Position=117748。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">(这里也有个坑:先要锁表,再看这两个值,从库开始同步后,再解锁表)。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">具体执行的命令如下:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">FLUSH</span> <span class="code-snippet__keyword">TABLES</span> <span class="code-snippet__keyword">WITH</span> <span class="code-snippet__keyword">READ</span> <span class="code-snippet__keyword">LOCK</span>;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">SHOW</span> <span class="code-snippet__keyword">MASTER</span> <span class="code-snippet__keyword">STATUS</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">UNLOCK</span> <span class="code-snippet__keyword">TABLES</span></span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"></span></pre> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">然后在从库 node56 上重新指定同步的日志文件和位置:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 停止从库同步</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">STOP</span> <span class="code-snippet__keyword">SLAVE</span>;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 设置同步文件和位置</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">CHANGE</span> <span class="code-snippet__keyword">MASTER</span> <span class="code-snippet__keyword">TO</span> MASTER_HOST=<span class="code-snippet__string">'10.2.1.55'</span>,</span></code><code><span class="code-snippet_outer">MASTER_PORT=<span class="code-snippet__number">3306</span>,</span></code><code><span class="code-snippet_outer">MASTER_USER=<span class="code-snippet__string">'vagrant'</span>,</span></code><code><span class="code-snippet_outer">MASTER_PASSWORD=<span class="code-snippet__string">'vagrant'</span>,</span></code><code><span class="code-snippet_outer">MASTER_LOG_FILE=<span class="code-snippet__string">'mysql-bin.000001'</span>,</span></code><code><span class="code-snippet_outer">MASTER_LOG_POS=<span class="code-snippet__number">117748</span>;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 开启同步</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">START</span> <span class="code-snippet__keyword">SLAVE</span>;</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">再次查看就不报错了,I/O 线程也跑起来了,</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.42758620689655175" src="/upload/75fde764ec72f49e7cfc2e6f1fa29af.png" data-type="png" data-w="870" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">然后将 node55 当做从库,node56 当做主库,同样执行上面的几步,状态显示正常了,然后用 navicat 工具连下数据库,都是正常的。在测试群反馈下结果,搞定收工。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">好像忘了一个问题,<strong>为啥 log 文件夹被干掉了</strong>??</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><strong><span style="font-size: 15px;color: rgb(171, 25, 66);">为什么会出现问题?</span></strong></h2> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">然后问了一波当时有没有人删除这个 /var/lib/mysql/log 目录,也没有人会随便删除这个目录的吧。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">但是发现 log 的上级目录 /var/lib/mysql 有很多其他文件夹,比如 xxcloud, xxcenter 等。这不就是我们项目中几个数据库的名字么,只要在这个目录的文件夹,都会显示在 navicat 上,是一一对应的,如下图所示。其中也显示了 log 数据库。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.896011396011396" src="/upload/49669f398fd8a6275ea1153c16c55eaf.png" data-type="png" data-w="702" style="white-space: normal;margin-bottom: 0px;text-align: left;"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">那会不会有人从 navicat 上干掉了 log 数据库?极有可能啊!</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">果然,有位同事之前在迁移升级的过程中,发现这个 log 数据库在老的系统是没有的,所以就清理了。这就相当于把 log 数据库干掉了,同时也会把 log 文件夹干掉了。好了,终于水落石出了!这个其实也是我前期没有考虑到 log 目录的一个问题。没错,这是我的锅~</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><strong><span style="font-size: 15px;color: rgb(171, 25, 66);">改进</span></strong></h2> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">其实操作同步数据库的时候,<strong>不应该用这种覆盖同步的方式</strong>。可以采取单库同步的方式,也就不会干掉 log 数据库了。但是,这个 log 数据库放在这里有点奇怪啊,能不能不要出现在这里呢?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">我们只要指定这个 log 目录不在 /var/lib/mysql 目录下就好了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">东哥建议:log 文件和数据库 data 文件进行隔离:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="ini"><code><span class="code-snippet_outer"><span class="code-snippet__attr">datadir</span> = /var/lib/mysql/data</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">log_bin</span> = /var/lib/mysql/log</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">另外一个问题,我们的高可用真的高可用了吗?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">至少<strong>没有做到及时报警</strong>,MySQL 数据库挂了,我是不知道的,都是通过测试同学反馈的。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">能不能及时感知到 MySQL 异常呢?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;margin-bottom: 0px;text-align: left;"><span style="font-size: 15px;">这里可以利用 Keepalived 发送邮件的功能,或者通过日志报警系统。这个是后面需要改进的地方。</span></p> </section>