作者:じ☆ve宝贝
#### 在开发中我们经常用1代表男生、0代表女生。 在jsp中常用的是使用三目运算符,但是html静态页面怎么开发? ##### jsp中判断: ``` ${sex==1?'男':sex==0?'女':'妖'} ``` ##### html页面中判断: ``` <html> <head> <title>studyjava测试</title> <script> var sex = { 1 : "<span style='color:green'>男</span>", 0 : "<span style='color:red'>女</span>" }; </script> </head> <body> <script>document.write(sex['0'])</script> </body> </html> ``` 缺憾:如果取得值不存在会出现undefined。
作者:じ☆ve宝贝
### 为什么你需要Lambda表达式 程序员需要Lambda表达式的原因主要有三个: 1. 更紧凑的代码 2. 通过提供额外的功能对方法的功能进行修改的能力 3. 更好地支持多核处理 ##### 更紧凑的代码 Lambda表达式以一种简洁的方式去实现仅有一个方法的Java类。 例如,如果代码中有大量的匿名内部类–诸如用于UI应用中的监听器与处理器实现,以及用于并发应用中的Callable与Runnable实现–在使用了Lambda表达式之后,将使代码变得非常短,且更易于理解。 ##### 修改方法的能力 有时,方法不具备我们想要的一些功能。例如,Collection接口中的contains()方法只有当传入的对象确实存在于该集合对象中时才会返回true。但我们无法去干预该方法的功能,比如,若使用不同的大小写方案也可以认为正在查找的字符串存在于这个集合对象中,我们希望此时contains()方法也能返回true。 简单点儿说,我们所期望做的就是”将我们自己的新代码传入”已有的方法中,然后再调用这个传进去的代码。Lambda表达式提供了一种很好的途径来代表这种被传入已有方法且应该还会被回调的代码。 ##### 更好地支持多核处理 当今的CPU具备多个内核。这就意味着,多线程程序能够真正地被并行执行,这完全不同于在单核CPU中使用时间共享这种方式。通过在Java中支持函数式编程语法,Lambda表达式能帮助你编写简单的代码去高效地应用这些CPU内核。 例如,你能够并行地操控大集合对象,通过利用并行编程模式,如过滤、映射和化简(后面将会很快接触到这些模式),就可使用到CPU中所有可用的硬件线程。 ### Lambda表达式概览 在前面提到的使用不同大小写方案查找字符串的例子中,我们想做的就是把方法toLowerCase()的表示法作为第二个参数传入到contains()方法中,为此需要做如下的工作: 1. 找到一种途径,可将代码片断当作一个值(某种对象)进行处理 2. 找到一种途径,将上述代码片断传递给一个变量 换言之,我们需要将一个程序逻辑包装到某个对象中,并且该对象可以被进行传递。为了说的更具体点儿,让我们来看两个基本的Lambda表达式的例子,它们都是可以被现有的Java代码进行替换的。 ##### 过滤 你想传递的代码片断可能就是过滤器,这是一个很好的示例。例如,假设你正在使用(Java SE 7预览版中的)java.io.FileFilter去确定目录隶属于给定的路径,如清单1所示, 清单1 ``` File dir = new File("/an/interesting/location/"); FileFilter directoryFilter = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } }; File[] directories = dir.listFiles(directoryFilter); ``` 在使用Lambda表达式之后,代码会得到极大的简化,如清单2所示, 清单2 ``` File dir = new File("/an/interesting/location/"); FileFilter directoryFilter = (File f) -> f.isDirectory(); File[] directories = dir.listFiles(directoryFilter); ``` 赋值表达式的左边会推导出类型(FileFilter),右边则看起来像FileFilter接口中accept()方法的一个缩小版,该方法会接受一个File对象,在判定f.isDirectory()之后返回一个布尔值。 实际上,由于Lambda表达式利用了类型推导,基于后面的工作原理,我们还可以进一步简化上述代码。编译器知道FileFilter只有唯一的方法accept(),所以它必定是该方法的实现。我们还知,accept()方法只需要一个File类型的参数。因此,f必定是File类型的。如清单3所示, 清单3 ``` File dir = new File("/an/interesting/location/"); File[] directories = dir.listFiles(f -> f.isDirectory()); ``` 你可以看到,使用Lambda表达式会大幅降低模板代码的数量。 一旦你习惯于使用Lambda表达式,它会使逻辑流程变得非常易于阅读。在达到这一目的的关键方法之一就是将过滤逻辑置于使用该逻辑的方法的侧边。 事件处理器 UI程序是另一个大量使用匿名内部类的领域。让我们将一个点击监听器赋给一个按钮,如清单4所示, 清单4 ``` Button button = new Button(); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ui.showSomething(); } }); ``` 这多么代码无非是说”当点击该按钮时,调用该方法”。使用Lambda表达式就可写出如清单5所示的代码, 清单5 ``` ActionListener listener = event -> {ui.showSomething();}; button.addActionListener(listener); ``` 该监听器在必要时可被复用,但如果它仅需被使用一次,清单6中的代码则考虑了一种很好的方式。 清单6 ``` button.addActionListener(event -> {ui.showSomething();}); ``` 在这个例子中,这种使用额外花括号的语法有些古怪,但这是必须的,因为actionPerformed()方法返回的是void。后面我们会看到与此有关的更多内容。 现在让我们转而关注Lambda表达式在编写处理集合对象的新式代码中所扮演的角色,尤其是当针对两种编程风格,外部遍历与内部遍历,之间的转换的时候。 外部遍历 vs. 内部遍历 到目前为止,处理Java集合对象的标准方式是通过外部遍历。之所以称其为外部遍历,是因为要使用集合对象外部的控制流程去遍历集合所包含的元素。这种传统的处理集合的方式为多数Java程序员所熟知,尽管他们并不知道或不使用外部遍历这个术语。 如清单7所示,Java语言为增强的for循环构造了一个外部迭代器,并使用这个迭代器去遍历集合对象, 清单7 ``` List<String> myStrings = getMyStrings(); for (String myString : myStrings) { if (myString.contains(possible)) System.out.println(myString + " contains " + possible); } ``` 使用这种方法,集合类代表着全部元素的一个”整体”视图,并且该集合对象还能支持对任意元素的随机访问,程序员可能会有这种需求。 基于这种观点,可通过调用iterator()方法去遍历集合对象,该方法将返回集合元素类型的迭代器,该迭代器是针对同一集合对象的更具限制性的视图。它没有为随机访问暴露任何接口;相反,它纯粹是为了顺序地访问集合元素而设计的。这种顺序本性使得当你试图并发地访问集合对象时就会造成臭名昭著的ConcurrentModificationException。 另一种可选的方案就是要求集合对象要能够在内部管理迭代器(或循环),这种方案就是内部遍历,当使用Lambda表达式时会优先选择内部遍历。 除了新的Lambda表达式语法以外,Lambda项目还包括一个经过大幅升级的集合框架类库。这次升级的目的是为了能更易于编写使用内部遍历的代码,以支持一系列众所周知的函数式编程典范。 ###使用Lambda的函数式编程 曾经,大多数开发者发现他们需要集合能够执行如下一种或几种操作: 1. 创建一个新的集合对象,但要过滤掉不符合条件的元素。 2. 对集合中的元素逐一进行转化,并使用转化后的集合。 3. 创建集合中所有元素的某个属性的总体值,例如,合计值与平均值。这样的任务(分别称之为过滤,映射和化简)具有共通的要点:它们都需要处理集合中的每个元素。 程序无论是判定某个元素是否存在,或是判断元素是否符合某个条件(过滤),或是将元素转化成新元素并生成新集合(映射),或是计算总体值(化简),关键原理就是”程序必须处理到集合中的每个元素”。 这就暗示我们需要一种简单的途径去表示用于内部遍历的程序。幸运地是,Java SE 8为此类表示法提供了构建语句块。 支持基本函数式编程的Java SE 8类 Java SE 8中的一些类意在被用于实现前述的函数式典范,这些类包括Predicate,Mapper和Block–当然,还有其它的一些类–它们都在一个新的java.util.functions包中。 看看Predicate类的更多细节,该类常被用于实现过滤算法;将它作用于一个集合,以返回一个包含有符合谓语条件元素的新集合。何为谓语,有很多种解释。Java SE 8认为谓语是一个依据其变量的值来判定真或假的方法。 再考虑一下我们之前看过的一个例子。给定一个字符串的集合,我们想判定它是否包含有指定的字符串,但希望字符串的比较是大小写不敏感的。 在Java SE 7中,我们将需要使用外部遍历,其代码将如清单8所示, 清单8 ``` public void printMatchedStrings(List<String> myStrings) { List<String> out = new ArrayList<>(); for (String s: myStrings) { if (s.equalsIgnoreCase(possible)) out.add(s); } log(out); } ``` 而在即将发布的Java SE 8中,我们使用Predicate以及Collections类中一个新的助手方法(过滤器)就可写出更为紧凑的程序,如清单9所示, 清单9 ``` public void printMatchedStrings() { Predicate<String> matched = s -> s.equalsIgnoreCase(possible); log(myStrings.filter(matched)); } ``` 事实上,如果使用更为通用的函数式编程风格,你只需要写一行代码,如清单10所示, 清单10 ``` public void printMatchedStrings() { log(myStrings.filter(s -> s.equalsIgnoreCase(possible))); } ``` 如你所见,代码依然非常的易读,并且我们也体会到了使用内部遍历的好处。 最后,让我们讨论一下Lambda表达式语法的更多细节。 Lambda表达式的语法规则 Lambda表达式的基本格式是以一个可被接受的参数列表开头,以一些代码(称之为表达式体/body)结尾,并以箭头(->)将前两者分隔开。 注意:Lambda表达式的语法仍可能会面临改变,但在撰写本文的时候,下面示例中所展示的语法是能够正常工作的。 Lambda表达式非常倚重类型推导,与Java的其它语法相比,这显得极其不同寻常。 让我们进一步考虑之前已经看过的一个示例(请见清单11)。如果看看ActionListener的定义,可以发现它只有一个方法(请见清单12)。 清单11 ``` ActionListener listener = event -> {ui.showSomething();}; ``` 清单12 ``` public interface ActionListener { public void actionPerformed(ActionEvent event); } ``` 所以,在清单11右侧的Lambda表达式,能够很容易地理解为”这是针对仅声明单个方法的接口的方法定义”。注意,仍然必须要遵守Java静态类型的一般规则;这是使类型推导能正确工作的唯一途径。 据此可以发现,使用Lambda表达式可以将先前所写的匿名内部类代码转换更紧凑的代码。 还需要意识到有另一个怪异的语法。让我们再回顾下上述示例,如清单13所示, 清单13 ``` FileFilter directoryFilter = (File f) -> f.isDirectory(); ``` 仅一瞥之,它看起来与ActionListener的示例相似,但让我们看看FileFilter接口的定义(请见清单14)。accept()方法会返回一个布尔值,但并没有一个显式的返回语句。相反,该返回值的类型是从Lambda表达式中推导出来的 清单14 ``` public interface FileFilter { public boolean accept(File pathname); } ``` 这就能解释,当方法返回类型为void时,为什么要进行特别处理了。对于这种情形,Lambda表达式会使用一对额外的小括号去包住代码部分(表达式体/body)。若没有这种怪异的语法,类型推导将无法正常工作–但你要明白,这一语法可能会被改变。 Lambda表达式的表达式体可以包含多条语句,对于这种情形,表达式体需要被小括号包围住,但”被推导出的返回类型”这种语法将不启作用,那么返回类型关键字就必不可少。 最后还需要提醒你的是:当前,IDE似乎还不支持Lambda语法,所以当你第一次尝试Lambda表达式时,必须要格外注意javac编译器抛出的任何警告。 结论 Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性。应用得当,Lambda表达式可使你写出简洁的代码,为已有方法增加额外的功能,并能更好地适应多核处理器。到目前为止,我们能肯定的是,你正急切地想去尝试Lambda表达式,所以咱也别啰嗦了… 你可以从Lambda项目的主页中获得包含有Lambda表达式的Java SE 8快照版。同样地,在试用二进制包时,你也应该先阅读一下”Lambda项目状态”的相关文章,可以在此处找到它们。
作者:微信小助手
<section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">Redis 是一个开源( BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5592654424040067" data-s="300,640" src="/upload/67f16bb92ce176b53d96a9eeb0e67bf.png" data-type="png" data-w="599" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">它支持的数据类型很丰富,如字符串、链表、集合、以及散列等,并且还支持多种排序功能。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">什么叫持久化?</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">用一句话可以将持久化概括为:将数据(如内存中的对象)保存到可永久保存的存储设备中。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、 XML 数据文件中等等。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">也可以从如下两个层面来理解持久化:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">应用层:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果关闭( Close )你的应用,然后重新启动则先前的数据依然存在。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统层:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果关闭( Shut Down )你的系统(电脑),然后重新启动则先前的数据依然存在。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">Redis 为什么要持久化?</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 中的数据类型都支持 Push/Pop、Add/Remove 及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在此基础上,Redis 支持各种不同方式的排序。与 Memcached 一样,为了保证效率,数据都是缓存在内存中。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因为数据都是缓存在内存中的,当你重启系统或者关闭系统后,缓存在内存中的数据都会消失殆尽,再也找不回来了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以,为了让数据能够长期保存,就要将 Redis 放在缓存中的数据做持久化存储。</span></p> <p style="line-height: normal;text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.6641901931649331" data-s="300,640" src="/upload/fc30480cad555d3efe47f823cd54ba49.png" data-type="png" data-w="673" style="white-space: normal;line-height: 28px;text-align: justify;"></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">Redis 怎么实现持久化?</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在设计之初,Redis 就已经考虑到了这个问题。</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">官方提供了多种不同级别的数据持久化的方式:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 持久化方式能够在指定的时间间隔对你的数据进行快照存储。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你也可以同时开启两种持久化方式,在这种情况下,当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果你不知道该选择哪一个级别的持久化方式,那我们就先来了解一下 AOF 方式和 RDB 方式有什么样的区别,并且它们各自有何优劣,学习完之后,再来考虑该选择哪一种级别。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">RDB 方式与 AOF 方式的优势对比</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>RDB 方式与 AOF 方式的优点对比</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">首先我们来看一看官方对于两种方式的优点描述,并做个对比,然后再看一看两种方式的缺点描述。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.3818565400843882" data-s="300,640" src="/upload/a36fcee09603ee4ebc151bd78a8663d3.png" data-type="png" data-w="474" style="line-height: 25.6px;text-align: center;white-space: normal;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">RDB 方式的优点:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如你可以在每个小时保存一下过去 24 小时内的数据,同时每天保存过去 30 天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 是一个紧凑的单一文件,很方便传送到另一个远端数据中心,非常适用于灾难恢复。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 在保存 RDB 文件时父进程唯一需要做的就是 Fork 出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他 IO 操作,所以 RDB 持久化方式可以最大化 Redis 的性能。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">与 AOF 相比,在恢复大的数据集的时候,RDB 方式会更快一些。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:</span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 调用 Forks,同时拥有父进程和子进程。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">子进程将数据集写入到一个临时 RDB 文件中。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。</span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.20085470085470086" data-s="300,640" src="/upload/7bfa86d284ee3ab2691b2a00c8c17e04.png" data-type="png" data-w="702" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">AOF 方式的优点:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用 AOF 会让你的 Redis 更加耐久。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你可以使用不同的 Fsync 策略:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">无 Fsync、每秒 Fsync 、每次写的时候 Fsync 使用默认的每秒 Fsync 策略。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 的性能依然很好( Fsync 是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失 1 秒的数据。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AOF文件是一个只进行追加的日志文件,所以不需要写入 Seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也可使用 redis-check-aof 工具修复这些问题。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"> 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 </span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 协议的格式保存。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">导出(export) AOF 文件也非常简单。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">举个例子,如果你不小心执行了 FLUSHALL 命令,但只要 AOF 文件未被重写,那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令,并重启 Redis ,就可以将数据集恢复到 FLUSHALL 执行之前的状态。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">优点对比总结:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 方式可以保存过去一段时间内的数据,并且保存结果是一个单一的文件,可以将文件备份到其他服务器,并且在回复大量数据的时候,RDB 方式的速度会比 AOF 方式的回复速度要快。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AOF 方式默认每秒钟备份 1 次,频率很高,它的操作方式是以追加的方式记录日志而不是数据,并且它的重写过程是按顺序进行追加,所以它的文件内容非常容易读懂。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以在某些需要的时候打开 AOF 文件对其编辑,增加或删除某些记录,最后再执行恢复操作。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>RDB 方式与 AOF 方式的缺点对比</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">RDB 方式的缺点:</span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果你希望在 Redis 意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么 RDB 不适合你。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">虽然你可以配置不同的 Save 时间点(例如每隔 5 分钟并且对数据集有 100 个写的操作),但是 Redis 要完整的保存整个数据集是一个比较繁重的工作。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你通常会每隔 5 分钟或者更久做一次完整的保存,万一 Redis 意外宕机,你可能会丢失几分钟的数据。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 需要经常 Fork 子进程来保存数据集到硬盘上,当数据集比较大的时,Fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果数据集巨大并且 CPU 性能不是很好的情况下,这种情况会持续 1 秒,AOF 也需要 Fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">AOF 方式的缺点:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">根据所使用的 Fsync 策略,AOF 的速度可能会慢于 RDB。在一般情况下,每秒 Fsync 的性能依然非常高,而关闭 Fsync 可以让 AOF 的速度和 RDB 一样快,即使在高负荷之下也是如此。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(Latency)。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">缺点对比总结:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RDB 由于备份频率不高,所以在回复数据的时候有可能丢失一小段时间的数据,而且在数据集比较大的时候有可能对毫秒级的请求产生影响。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AOF 的文件提及比较大,而且由于保存频率很高,所以整体的速度会比 RDB 慢一些,但是性能依旧很高。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">RDB 与 AOF 工作原理</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5404120443740095" data-s="300,640" src="/upload/d25f1f29e052768bd04071e8ae24feec.png" data-type="png" data-w="631" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:</span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 执行 fork() ,现在同时拥有父进程和子进程。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">子进程开始将新 AOF 文件的内容写入到临时文件。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。</span></p></li> </ul> <h1 class="heading" data-id="heading-6" style="line-height: normal;"><br></h1> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;">付诸实践,RDB 与 AOF 的实现</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.9919354838709677" data-s="300,640" src="/upload/a1c661808866216fcb2c0567e71b125b.png" data-type="png" data-w="248" style=""></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p class="heading" style="box-sizing: border-box;"><strong>RDB 方式持久化的开启与配置</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 默认的持久化方式是 RDB ,并且默认是打开的。RDB 的保存方式分为主动保存与被动保存。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主动保存可以在 redis-cli 中输入 Save 即可;被动保存需要满足配置文件中设定的触发条件,目前官方默认的触发条件可以在 redis.conf 中看到:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs nginx" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-attribute" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">save</span> <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">900</span> <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span><br>save <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">300</span> <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">10</span><br>save <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">60</span> <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">10000</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">其含义为:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">服务器在900秒之内,对数据库进行了至少1次修改。<br>服务器在300秒之内,对数据库进行了至少10次修改。<br>服务器在60秒之内,对数据库进行了至少10000次修改。<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">满足触发条件后,数据就会被保存为快照,正是因为这样才说 RDB 的数据完整性是比不上 AOF 的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">触发保存条件后,会在指定的目录生成一个名为 dump.rdb 的文件,等到下一次启动 Redis 时,Redis 会去读取该目录下的 dump.rdb 文件,将里面的数据恢复到 Redis。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="color: rgb(71, 193, 168);"><span style="font-size: 15px;letter-spacing: 1px;">这个目录在哪里呢?</span><span style="font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">我们可以在客户端中输入命令 config get dir 查看:</span></span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs ruby" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">gannicus@$ src/redis-cli<br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">127.0.0.1:6379></span> config get dir<br><span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span>) <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"dir"</span><br><span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">2</span>) <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"/home/gannicus/Documents/redis-5.0.0"</span><br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">127.0.0.1:6379></span> <br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.6329113924050633" src="/upload/5f7d6beefec2f88141ec3e6bf8d2c845.gif" data-type="gif" data-w="948" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">返回结果中的"/home/gannicus/Documents/redis-5.0.0"就是存放 dump.rdb 的目录。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;"><strong>在测试之前,说明一下前提:</strong>Redis 是直接从官网下载的压缩包,解压后得到 redis-x.x.x 文件夹。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">比如我的是 redis-5.0.0,然后进入文件夹,在 redis-5.0.0 项目根目录使用 make 命令安装。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.6370370370370371" src="/upload/79f87f622eb828ae23e1955afab9d114.gif" data-type="gif" data-w="945" style=""></p> <p style="line-height: normal;"><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;color: rgb(89, 89, 89);letter-spacing: 1px;"><strong>RDB 被动触发保存测试</strong></span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">刚才提到它分为主动保存与被动触发,现在我们来测试一下被动触发。首先启动 redis-server,然后再打开客户端 redis-cli ,先增添几条记录:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lca</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lcb</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lcc</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lcd</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lce</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lcf</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lcg</span> 1<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">OK</span><br>127<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.0.0.1</span><span class="hljs-selector-pseudo" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">:6379</span>> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inhe
作者:微信小助手
<p style="white-space: normal;background-color: rgb(255, 255, 255);text-align: center;letter-spacing: 1px;line-height: 1.5em;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"><br></p> <section class="KolEditor" data-tools-id="47339" style="white-space: normal;"> <section data-width="100%" style="width: 556px;text-align: center;"> <section style="margin-right: auto;margin-left: auto;display: inline-block;height: 30px;word-break: normal !important;"> <p style="background-image: -webkit-linear-gradient(left, rgb(17, 67, 87), rgb(242, 148, 146), rgb(17, 67, 87));background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-width: 0px;border-style: initial;border-color: initial;-webkit-background-clip: text;-webkit-text-fill-color: transparent;word-break: normal !important;"><span style="font-size: 14px;word-break: normal !important;"><strong><span style="color: rgb(110, 191, 248);word-break: normal !important;"><span style="color: rgb(51, 51, 51);word-break: normal !important;overflow-wrap: break-word !important;">公众号后台回复 “</span><span style="color: rgb(201, 56, 28);word-break: normal !important;overflow-wrap: break-word !important;"> <span style="word-break: normal !important;overflow-wrap: break-word !important;"><strong>学习</strong></span></span><span style="color: rgb(51, 51, 51);word-break: normal !important;overflow-wrap: break-word !important;"> ” ,获取作者</span><span style="color: rgb(201, 56, 28);word-break: normal !important;overflow-wrap: break-word !important;">独家秘制</span><span style="color: rgb(51, 51, 51);word-break: normal !important;overflow-wrap: break-word !important;">学习资料</span></span></strong></span></p> </section> </section> </section> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 14px;"></span></p> <section class="KolEditor" style="white-space: normal;"> <section style="margin: 10px auto;display: flex;align-items: center;justify-content: center;"> <section style="margin-top: 5px;display: flex;justify-content: center;align-items: center;"> <section style="background: rgb(115, 183, 174);border-width: 1px;border-style: solid;border-color: rgb(65, 85, 133);border-radius: 10px;"> <section style="margin: -5px 5px 5px -5px;background: rgb(255, 255, 255);border-width: 1px;border-style: solid;border-color: rgb(65, 85, 133);border-radius: 10px;"> <section style="padding: 5px 12px;"> <p style="font-size: 15px;color: rgb(46, 67, 118);text-align: center;letter-spacing: 2px;"><strong>目录</strong></p> <p style="line-height: 2em;"><span style="font-size: 13px;"></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(0)写在前面</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(1)如何保证宕机时数据不丢失?</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(2)多副本冗余的高可用机制</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(3)多副本之间数据如何同步?</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(4)ISR到底指的什么东西?</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(5)acks参数的含义?</span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">(6)最后的思考</span></p> <p><br></p> </section> </section> </section> </section> </section> </section> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 14px;"></span></p> <p style="line-height: 2em;"><strong><span style="font-size: 15px;"><br></span></strong></p> <section class="KolEditor" style="white-space: normal;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color: inherit;font-size: 18px;"><strong style="color: inherit;"><span style="padding: 5px 15px 5px 10px;border-radius: 0px 50px 50px 0px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;"><strong style="white-space: normal;"><span style="font-size: 15px;">(0)写在前面 </span></strong></span></strong></span> </section> </section> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">面试大厂时,一旦简历上写了Kafka,几乎必然会被问到一个问题:说说acks参数对消息持久化的影响?</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">这个acks参数在kafka的使用中,是非常核心以及关键的一个参数,决定了很多东西。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">所以无论是为了面试还是实际项目使用,大家都值得看一下这篇文章对Kafka的acks参数的分析,以及背后的原理。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(1)如何保证宕机的时候数据不丢失?</span></strong></span> </section> </section> <p><br></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">如果要想理解这个acks参数的含义,首先就得搞明白kafka的高可用架构原理。<br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">比如下面的图里就是表明了对于每一个Topic,我们都可以设置他包含几个Partition,每个Partition负责存储这个Topic一部分的数据。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">然后Kafka的Broker集群中,每台机器上都存储了一些Partition,也就存放了Topic的一部分数据,这样就实现了Topic的数据分布式存储在一个Broker集群上。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="1.3669064748201438" data-type="png" src="/upload/1bbba561c99c93598ffb7f0324a4177e.png" data-w="278"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">但是有一个问题,万一 一个Kafka Broker宕机了,此时上面存储的数据不就丢失了吗?</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">没错,这就是一个比较大的问题了,分布式系统的数据丢失问题,是他首先必须要解决的,一旦说任何一台机器宕机,此时就会导致数据的丢失。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(2)多副本冗余的高可用机制</span></strong></span> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以如果大家去分析任何一个分布式系统的原理,比如说zookeeper、kafka、redis cluster、elasticsearch、hdfs,等等,其实他都有自己内部的一套多副本冗余的机制,多副本冗余几乎是现在任何一个优秀的分布式系统都一般要具备的功能。<br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">在kafka集群中,每个Partition都有多个副本,其中一个副本叫做leader,其他的副本叫做follower,如下图。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="1.3669064748201438" data-type="png" src="/upload/fac62849568da5c83ce5bbcc4346d385.png" data-w="278"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">如上图所示,假设一个Topic拆分为了3个Partition,分别是Partition0,Partiton1,Partition2,此时每个Partition都有2个副本。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">比如Partition0有一个副本是Leader,另外一个副本是Follower,Leader和Follower两个副本是分布在不同机器上的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这样的多副本冗余机制,可以保证任何一台机器挂掉,都不会导致数据彻底丢失,因为起码还是有副本在别的机器上的。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(3)多副本之间数据如何同步?</span></strong></span> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">接着我们就来看看多个副本之间数据是如何同步的?其实任何一个Partition,只有Leader是对外提供读写服务的<br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">也就是说,如果有一个客户端往一个Partition写入数据,此时一般就是写入这个Partition的Leader副本。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">然后Leader副本接收到数据之后,Follower副本会不停的给他发送请求尝试去拉取最新的数据,拉取到自己本地后,写入磁盘中。如下图所示:</span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.5702364394993046" data-type="png" src="/upload/e30b28f8ea86650a782346e418953a1d.png" data-w="719"></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(4)ISR到底指的是什么东西?</span></strong></span> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">既然大家已经知道了Partiton的多副本同步数据的机制了,那么就可以来看看ISR是什么了。<br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">ISR全称是“In-Sync Replicas”,也就是保持同步的副本,他的含义就是,跟Leader始终保持同步的Follower有哪些。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">大家可以想一下 ,如果说某个Follower所在的Broker因为JVM FullGC之类的问题,导致自己卡顿了,无法及时从Leader拉取同步数据,那么是不是会导致Follower的数据比Leader要落后很多?</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以这个时候,就意味着Follower已经跟Leader不再处于同步的关系了。但是只要Follower一直及时从Leader同步数据,就可以保证他们是处于同步的关系的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以每个Partition都有一个ISR,这个ISR里一定会有Leader自己,因为Leader肯定数据是最新的,然后就是那些跟Leader保持同步的Follower,也会在ISR里。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(5)acks参数的含义</span></strong></span> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">铺垫了那么多的东西,最后终于可以进入主题来聊一下acks参数的含义了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">如果大家没看明白前面的那些副本机制、同步机制、ISR机制,那么就无法充分的理解acks参数的含义,这个参数实际上决定了很多重要的东西。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">首先这个acks参数,是在KafkaProducer,也就是生产者客户端里设置的</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">也就是说,你往kafka写数据的时候,就可以来设置这个acks参数。然后这个参数实际上有三种常见的值可以设置,分别是:0、1 和 all。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="letter-spacing: 2px;"><strong><span style="letter-spacing: 2px;font-size: 15px;">第一种选择是把acks参数设置为0</span></strong><span style="letter-spacing: 2px;font-size: 15px;">,意思就是我的KafkaProducer在客户端,只要把消息发送出去,不管那条数据有没有在哪怕Partition Leader上落到磁盘,我就不管他了,直接就认为这个消息发送成功了。</span></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">如果你采用这种设置的话,那么你必须注意的一点是,可能你发送出去的消息还在半路。结果呢,Partition Leader所在Broker就直接挂了,然后结果你的客户端还认为消息发送成功了,此时就会导致这条消息就丢失了。</span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.5702364394993046" data-type="png" src="/upload/9829ba2832d5adf6ebc7255c2fa8701f.png" data-w="719"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="letter-spacing: 2px;"><strong><span style="font-size: 15px;">第二种选择是设置 acks = 1</span></strong><span style="font-size: 15px;">,意思就是说只要Partition Leader接收到消息而且写入本地磁盘了,就认为成功了,不管他其他的Follower有没有同步过去这条消息了。</span></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">这种设置其实是kafka默认的设置,大家请注意,<strong><span style="letter-spacing: 2px;font-size: 15px;color: rgb(201, 56, 28);">划重点!</span></strong><strong><span style="letter-spacing: 2px;font-size: 15px;color: rgb(201, 56, 28);">这是默认的设置</span></strong></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">也就是说,默认情况下,你要是不管acks这个参数,只要Partition Leader写成功就算成功。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">但是这里有一个问题,万一Partition Leader刚刚接收到消息,Follower还没来得及同步过去,结果Leader所在的broker宕机了,此时也会导致这条消息丢失,因为人家客户端已经认为发送成功了。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6099518459069021" data-type="png" src="/upload/e0311981e7df73c1da19c209f2400ab8.png" data-w="623"></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="letter-spacing: 2px;"><strong><span style="font-size: 15px;">最后一种情况,就是设置acks=all</span></strong><span style="font-size: 15px;">,这个意思就是说,Partition Leader接收到消息之后,还必须要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步过去,才能认为这条消息是写入成功了。</span></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">如果说Partition Leader刚接收到了消息,但是结果Follower没有收到消息,此时Leader宕机了,那么客户端会感知到这个消息没发送成功,他会重试再次发送消息过去。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 2px;">此时可能Partition 2的Follower变成Leader了,此时ISR列表里只有最新的这个Follower转变成的Leader了,那么只要这个新的Leader接收消息就算成功了。</span><span style="font-size: 15px;"></span></p> <p style="line-height: 2em;text-align: center;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.5702364394993046" data-type="png" src="/upload/e30b28f8ea86650a782346e418953a1d.png" data-w="719"></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;"></span></p> <section class="KolEditor"> <section style="margin-top: 10px;margin-bottom: 10px;"> <span style="color:inherit;font-size:18px;"><strong style="color:inherit;"><span style="border-radius: 0px 50px 50px 0px;padding: 5px 15px 5px 10px;background-color: rgb(89, 155, 171);color: rgb(255, 255, 255);font-size: 15px;font-weight: 700;">(6)最后的思考</span></strong></span> </section> </section> <p><span style="color: rgb(64, 179, 230);font-size: 15px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="color: rgb(64, 179, 230);font-size: 15px;letter-spacing: 2px;">acks=all 就可以代表数据一定不会丢失了吗?</span><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">当然不是,如果你的Partition只有一个副本,也就是一个Leader,任何Follower都没有,你认为acks=all有用吗?</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">当然没用了,因为ISR里就一个Leader,他接收完消息后宕机,也会导致数据丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以说,这个acks=all,必须跟ISR列表里至少有2个以上的副本配合使用,起码是有一个Leader和一个Follower才可以。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这样才能保证说写一条数据过去,一定是2个以上的副本都收到了才算是成功,此时任何一个副本宕机,不会导致数据丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以希望大家把这篇文章好好理解一下,对大家出去面试,或者工作中用kafka都是很好的一个帮助。</span><span style="font-size: 15px;"></span></p> <p style="white-space: normal;"><br></p> <p style="white-space: normal;line-height: 2em;text-align: center;"><span style="font-size: 24px;"><strong><span style="color: rgb(241, 136, 35);">End</span></strong></span></p> <p style="white-space: normal;line-height: 2em;text-align: center;"><br></p> <p style="white-space: normal;vertical-align: baseline;letter-spacing: 1px;text-align: center;line-height: 2em;"><span style="font-family: Helvetica Neue, Helvetica, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;font-size: 18px;">喜欢本文的各位朋友,欢迎长按下图关注公众号<span style="color: rgb(201, 56, 28);"><strong>石杉的架构笔记</strong></span>,收看更多精彩内容</span></p> <section class="KolEditor" data-tools-id="23687" style="white-space: normal;"> <section style="margin: 25px 10px;display: flex;justify-content: center;align-items: center;"> <section style="padding: 23px 30px 25px;background-image: url(https://mmbiz.qpic.cn/mmbiz_gif/1J6IbIcPCLbL5ObibXxibsicOibxlVzsNYiaKmPoKA3B0CeYckYqhWNQZrjken9jvInnuthB0Nk9iaDOe5QBMBwXr6fA/640?wx_fmt=gif);background-position: center bottom;background-repeat: repeat;background-size: 300px 200px;"> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-cropselx1="0" data-cropselx2="258" data-cropsely1="0" data-cropsely2="258" data-ratio="0.6666666666666666" data-s="300,640" data-type="jpeg" data-w="258" src="/upload/5794833c5e809aec929a706d11272497.jpg" style="height: 258px;width: 258px;"></p> </section> </section> </section> <p style="white-space: normal;font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;line-height: 2em;"><strong style="letter-spacing: 1px;"><span style="font-style: inherit;font-variant-ligatures: inherit;font-variant-caps: inherit;-webkit-font-smoothing: antialiased;border-width: 0px;border-style: initial;border-color: initial;font-weight: bolder;font-stretch: inherit;vertical-align: baseline;user-select: text;color: rgba(13, 0, 19, 0.72);font-family: PingFangSC-Regular;font-variant-numeric: normal;font-variant-east-asian: normal;line-height: 23.324px;text-align: justify;widows: 1;font-size: 16px;">石杉的架构笔记</span></strong></p> <p style="white-space: normal;font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;line-height: 2em;"><span style="font-size: 16px;"><strong><span style="color: rgba(13, 0, 19, 0.72);font-family: PingFangSC-Regular;text-align: justify;widows: 1;-webkit-font-smoothing: antialiased;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: normal;font-stretch: inherit;line-height: 23.324px;vertical-align: baseline;user-select: text;"><span style="-webkit-font-smoothing: antialiased;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;vertical-align: baseline;user-select: text;color: rgb(247, 150, 70);">BAT架构经验</span>倾囊相授</span></strong></span></p> <p style="white-space: normal;font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;"><br></p> <p style="white-space: normal;font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;"><br></p> <section class="KolEditor" style="white-space: normal;"> <section style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;"> <img class="" data-ratio="0.6666666666666666" data-type="gif" data-w="640" src="/upload/5e6ee350d82ec9a041021bebdad268ca.gif"> </section> </section> </section>
作者:微信小助手
<p data-mpa-powered-by="yiban.io" style="margin: 5px 16px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;caret-color: rgb(51, 51, 51);word-spacing: 2px;text-align: center;line-height: normal;"><span style="font-size: 12px;color: rgb(178, 178, 178);letter-spacing: 1px;">点击上方</span><span style="letter-spacing: 0.544px;font-size: 12px;color: rgb(120, 172, 254);"><strong><span style="letter-spacing: 1px;">蓝色字体</span></strong></span><span style="font-size: 12px;color: rgb(178, 178, 178);letter-spacing: 1px;">,选择“置顶公众号”</span></p> <p data-mpa-powered-by="yiban.io" style="margin: 5px 16px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;caret-color: rgb(51, 51, 51);word-spacing: 2px;text-align: center;line-height: normal;"><span style="color: rgb(178, 178, 178);font-size: 12px;letter-spacing: 0.544px;">优质文章,第一时间送达</span></p> <section data-role="outer" label="Powered by 135editor.com" style="font-family: 微软雅黑;"> <section data-role="outer" label="Powered by 135editor.com" style=""> <section data-role="paragraph" class="_135editor" style="border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;caret-color: red;"><img class="mpa-image" data-ratio="0.5628517823639775" data-w="533" src="/upload/21d6f8ab9c725bfe71a8bf2a052133b9.null"></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;caret-color: red;">本文我们来谈谈项目中常用的MySQL优化方法,共19条,具体如下:</span><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;caret-color: red;">1、EXPLAIN</span></strong></span><br></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">做MySQL优化,我们要善用EXPLAIN查看SQL执行计划。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">下面来个简单的示例,标注(1、2、3、4、5)我们要重点关注的数据:</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-copyright="0" data-ratio="0.12916666666666668" data-s="300,640" data-type="jpeg" data-w="720" src="/upload/c3b62dbf692f51cd191cfab62a1095e0.jpg" style=""></p> <p style="font-size: 16px;text-align: center;"><br></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">type列</span></strong><strong><span style="font-size: 15px;">,</span></strong><span style="font-size: 15px;">连接类型。一个好的SQL语句至少要达到range级别。杜绝出现all级别。</span></p></li> <li><p style="line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;caret-color: red;">key列,</span></strong><span style="font-size: 15px;caret-color: red;">使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式。</span></p></li> <li><p style="line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;caret-color: red;">key_len列,</span></strong><span style="font-size: 15px;caret-color: red;">索引长度。</span></p></li> <li><p style="line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;caret-color: red;">rows列,</span></strong><span style="font-size: 15px;caret-color: red;">扫描行数。该值是个预估值。</span></p></li> <li><p style="line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;caret-color: red;">extra列,</span></strong><span style="font-size: 15px;caret-color: red;">详细说明。注意,常见的不太友好的值,如下:Using filesort,Using temporary。</span></p></li> </ul> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">2、SQL语句中IN包含的值不应过多</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from t where num in(1,2,3) 对于连续的数值,能用between就不要用in了;再或者使用连接来替换。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">3、SELECT语句务必指明字段名称</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">SELECT*增加很多不必要的消耗(CPU、IO、内存、网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前断也需要更新。所以要求直接在select后面接上字段名。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">4、当只需要一条数据的时候,使用limit 1</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">这是为了使EXPLAIN中type列达到const类型</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">5、如果排序字段没有用到索引,就尽量少排序</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">6、如果限制条件中其他字段没有索引,尽量少用or</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">or两边的字段中,如果有一个不是索引字段,而其他条件也不是索引字段,会造成该查询不走索引的情况。很多时候使用union all或者是union(必要的时候)的方式来代替“or”会得到更好的效果。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">7、尽量用union all代替union</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">8、不使用ORDER BY RAND()</span></strong></span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select id from `dynamic` order by rand() limit 1000;</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">上面的SQL语句,可优化为:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <p><span style="font-size: 10px;">select id from `dynamic` t1 join (select rand() * (select max(id) from `dynamic`) as nid) t2 on t1.id > t2.nidlimit 1000;</span></p> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">9、区分in和exists、not in和not exists</span></strong></span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select * from 表A where id in (select id from 表B)</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">上面SQL语句相当于</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select * from 表A where exists(select * from 表B where 表B.id=表A.id)</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效的写出一个替代not exists的SQL语句?</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">原SQL语句:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select colname … from A表 where a.id not in (select b.id from B表)</span> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">高效的SQL语句:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select colname … from A表 Left join B表 on where a.id = b.id where b.id is null</span> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">取出的结果集如下图表示,A表不在B表中的数据:</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-croporisrc="/upload/14a924d1d04c09cc5d1b975666388d78.jpg" data-cropx1="0" data-cropx2="331" data-cropy1="9" data-cropy2="198" data-ratio="0.5709969788519638" data-s="300,640" data-type="jpeg" data-w="331" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPT9CIGTQ687SojVpDT1ahRgOKMJMJ3Ax33fFibn5RluXicXNEoYYdUZVQ/640?wx_fmt=jpeg" style="width: 331px;height: 189px;"></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">10、使用合理的分页方式以提高分页的效率</span></strong></span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select id,name from product limit 866613, 20</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">使用上述SQL语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866612。SQL可以采用如下的写法:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select id,name from product where id> 866612 limit 20</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">11、分段查询</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">在一些用户选择页面中,可能一些用户选择的时间范围过大,造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段进行查询,循环遍历,将结果合并处理进行展示。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">如下图这个SQL语句,扫描的行数成百万级以上的时候就可以使用分段查询:</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"></span></p> <p style="font-size: 16px;text-align: center;"><img class="" data-copyright="0" data-ratio="0.125" data-s="300,640" data-type="jpeg" data-w="720" src="/upload/df81eac8eae3e1284a959c143b645eb9.jpg" style=""></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">12、避免在where子句中对字段进行null值判断</span></strong></span><br></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">对于null的判断会导致引擎放弃使用索引而进行全表扫描。</span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">13、不建议使用%前缀模糊查询</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">例如LIKE“%name”或者LIKE“%name%”,这种查询会导致索引失效而进行全表扫描。但是可以使用LIKE “name%”。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">那如何查询%name%?</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">如下图所示,虽然给secret字段添加了索引,但在explain结果并没有使用:</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-copyright="0" data-ratio="0.4625" data-s="300,640" data-type="jpeg" data-w="640" src="/upload/77820afedf2179d7cf191d46ca77e71d.jpg" style=""></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">那么如何解决这个问题呢,答案:使用全文索引。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">在我们查询中经常会用到select id,fnum,fdst from dynamic_201606 where user_name like '%zhangsan%'; 。这样的语句,普通索引是无法满足查询需求的。庆幸的是在MySQL中,有全文索引来帮助我们。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">创建全文索引的SQL语法是:</span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><br></span></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: #efefef;"> <section class="135brush"> <span style="font-size: 10px;">ALTER TABLE `dynamic_201606` ADD FULLTEXT INDEX `idx_user_name` (`user_name`);</span> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">使用全文索引的SQL语句是:</span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"></span></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: #efefef;"> <section class="135brush"> <p><span style="font-size: 10px;">select id,fnum,fdst from dynamic_201606 where match(user_name) against('zhangsan' in boolean mode);</span></p> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><strong><br></strong></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><strong>注意:</strong>在需要创建全文索引之前,请联系DBA确定能否创建。同时需要注意的是查询语句的写法与普通索引的区别。</span><br></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">14、避免在where子句中对字段进行表达式操作</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">比如:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <p><span style="font-size: 10px;">select user_id,user_project from user_base where age*2=36;</span></p> </section> </section> </section> </section> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">中对字段就行了算术运算,这会造成引擎放弃使用索引,建议改成:</span></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <section class="135brush"> <span style="font-size: 10px;">select user_id,user_project from user_base where age=36/2;</span> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">15、避免隐式类型转换</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">where子句中出现column字段的类型和传入的参数类型不一致的时候发生的类型转换,建议先确定where中的参数类型。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">16、对于联合索引来说,要遵守最左前缀法则</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">举列来说索引含有字段id、name、school,可以直接用id字段,也可以id、name这样的顺序,但是name;school都无法使用这个索引。所以在创建联合索引的时候一定要注意索引字段顺序,常用的查询字段放在最前面。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">17、必要时可以使用force index来强制查询走某个索引</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">有的时候MySQL优化器采取它认为合适的索引来检索SQL语句,但是可能它所采用的索引并不是我们想要的。这时就可以采用forceindex来强制优化器使用我们制定的索引。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">18、注意范围查询语句</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">对于联合索引来说,如果存在范围查询,比如between、>、<等条件时,会造成后面的索引字段失效。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="color: #138bde;"><strong><span style="font-size: 15px;">19、关于JOIN优化</span></strong></span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-croporisrc="/upload/70e0b8fd6956402d37884bfc4b7ceb31.jpg" data-cropx1="0" data-cropx2="640" data-cropy1="33.26164874551972" data-cropy2="198.4229390681004" data-ratio="0.2578125" data-s="300,640" data-type="jpeg" data-w="640" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPcKk5H7XnmAaZzuhtOJ4ODpsuBhmBNCic1aF8t9nITOxDRYsRa3A10ew/640?wx_fmt=jpeg" style="width: 558px;height: 144px;"></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">LEFT JOIN A表为驱动表,</span><span style="font-size: 15px;caret-color: red;">INNER JOIN MySQL会自动找出那个数据少的表作用驱动表,RIGHT JOIN B表为驱动表。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">注意:</span></strong></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">1)MySQL中没有full join,可以用以下方式来解决:</span></strong></p> <p style="font-size: 16px;"><br></p> <section class="_135editor" data-tools="135编辑器" data-id="88286" style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;background-color: #efefef;"> <section class="135brush"> <p><span style="font-size: 10px;">select * from A left join B on B.name = A.namewhere B.name is nullunion allselect * from B;</span></p> </section> </section> </section> </section> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">2)尽量使用inner join,避免left join:</span></strong></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">3)合理利用索引:</span></strong></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">被驱动表的索引字段作为on的限制字段。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">4)利用小表去驱动大表:</span></strong></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-croporisrc="/upload/df22c1560306615ba9a933fdefd5fb56.jpg" data-cropx1="0" data-cropx2="391" data-cropy1="23" data-cropy2="312" data-ratio="0.7391304347826086" data-s="300,640" data-type="jpeg" data-w="391" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPbiag8y4LggbC1B0VSIAFyoUWeOPzb4dsjdhlz7NAV7zSyFhmtF77xyw/640?wx_fmt=jpeg" style="width: 391px;height: 289px;"></p> <p style="font-size: 16px;text-align: center;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">从原理图能够直观的看出如果能够减少驱动表的话,减少嵌套循环中的循环次数,以减少 IO总量及CPU运算的次数。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><strong><span style="font-size: 15px;">5)巧用STRAIGHT_JOIN:</span></strong></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">inner join是由MySQL选择驱动表,但是有些特殊情况需要选择另个表作为驱动表,比如有group by、order by等「Using filesort」、「Using temporary」时。STRAIGHT_JOIN来强制连接顺序,在STRAIGHT_JOIN左边的表名就是驱动表,右边则是被驱动表。在使用STRAIGHT_JOIN有个前提条件是该查询是内连接,也就是inner join。其他链接不推荐使用STRAIGHT_JOIN,否则可能造成查询结果不准确。</span></p> <p style="font-size: 16px;"><br></p> <p style="font-size: 16px;text-align: center;"><img class="" data-croporisrc="/upload/2fd7dbf575790dbae66655ae50ba304.jpg" data-cropx1="0" data-cropx2="623" data-cropy1="0" data-cropy2="430.9641577060932" data-ratio="0.6918138041733547" data-s="300,640" data-type="jpeg" data-w="623" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPY46CzOXhvOrlSuFKghXic9EnRot4jD3ibzIFS3DQNcuZOojibXoJcXqYw/640?wx_fmt=jpeg" style="width: 558px;height: 386px;"></p> <p style="font-size: 16px;text-align: center;"><br></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">这个方式有时能减少3倍的时间。</span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;line-height: 1.75em;letter-spacing: 0.5px;margin-left: 5px;margin-right: 5px;text-align: justify;"><span style="font-size: 15px;">以上19条MySQL优化方法希望对大家有所帮助!</span></p> <section class="" data-tools="135编辑器" data-id="88286" style="font-size: 16px;color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;background-color: rgb(255, 255, 255);"> <section style="margin-top: 10px;margin-bottom: 10px;"> <section style="margin-top: -4px;padding: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <section class=""> <p style="margin-right: 5px;margin-left: 5px;letter-spacing: 0.5px;line-height: 1.5em;"><span style="color: rgb(127, 127, 127);letter-spacing: 0.544px;font-size: 14px;">作者:喜欢拿铁的人</span></p> <p style="margin-right: 5px;margin-left: 5px;letter-spacing: 0.5px;line-height: 1.5em;"><span style="text-align: justify;"></span><span style="text-align: justify;color: rgb(127, 127, 127);letter-spacing: 0.544px;font-size: 14px;">来源:</span><span style="text-align: justify;color: rgb(127, 127, 127);letter-spacing: 0.544px;font-size: 12px;">https://zhuanlan.zhihu.com/p/49888088</span></p> <p style="margin-right: 5px;margin-left: 5px;letter-spacing: 0.5px;line-height: 1.5em;"><span style="max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;">整理自dbaplus</span></p> </section> </section> </section> </section> <section data-role="paragraph" class="" style="font-size: 16px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;background-color: rgb(255, 255, 255);max-width: 100%;color: rgb(62, 62, 62);"> <section style="letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"> <section> <section> <section> <section> <section> <section> <section> <section> <section> <section> <section> <section style="letter-spacing: 0.544px;text-align: left;"> <section> <section style="letter-spacing: 0.544px;text-align: center;"> <section style="margin-right: auto;margin-left: auto;padding-right: 15px;padding-left: 15px;background-color: rgb(254, 254, 254);display: inline-block;"> <section style="letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: left;"> <section> <section style="letter-spacing: 0.544px;text-align: center;"> <section style="margin-right: auto;margin-left: auto;padding-right: 15px;padding-left: 15px;background-color: rgb(254, 254, 254);display: inline-block;"> <p style="color: inherit;"><span style="font-size: 14px;">正文结束</span></p> </section> <section style="margin-top: -12px;margin-bottom: 10px;"> <section style="border-top: 1px dashed rgb(33, 33, 34);width: 652px;border-right-color: rgb(33, 33, 34);border-bottom-color: rgb(33, 33, 34);border-left-color: rgb(33, 33, 34);"> <br> </section> </section> </section> </section> </section> </section> </section> </section> </section> <p style="letter-spacing: 0.544px;font-size: 16px;font-family: 微软雅黑;text-align: center;line-height: 1.75em;"><img class="" data-ratio="0.45454545454545453" data-w="22" src="/upload/859063c763d83e4844bdcb74e8e08fab.null" style="font-variant-numeric: normal;letter-spacing: 0.544px;line-height: 25.6px;widows: 1;display: inline;visibility: visible !important;width: auto !important;" title="音符" width="auto"></p> <section style="letter-spacing: 0.544px;"> <section style="letter-spacing: 0.544px;text-align: left;"> <section style="margin-right: auto;margin-left: auto;padding-right: 15px;padding-left: 15px;background-color: rgb(254, 254, 254);display: inline-block;"> <p style="letter-spacing: 0.544px;font-size: 16px;font-family: 微软雅黑;text-align: center;line-height: 1.75em;"><br></p> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section>
作者:じ☆ve宝贝
# 安装配置Zabbix server ### a. 安装 MySQL 数据库 ``` rpm -i https://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm rpm -ivh zabbix-release-3.0-1.el7.noarch.rpm ``` ### b. 安装Zabbix server,Web前端,agent ``` yum install zabbix-server-mysql zabbix-web-mysql zabbix-agent ``` ### c. 创建初始数据库 ``` # mysql -uroot -p password(默认密码空) mysql> create database zabbix character set utf8 collate utf8_bin; mysql> grant all privileges on zabbix.* to zabbix@''%'' identified by ''zabbix''; mysql> quit; ``` 导入初始架构和数据,系统将提示您输入新创建的密码。 ``` zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix ``` ### d. 为Zabbix server配置数据库 编辑配置文件 ``` /etc/zabbix/zabbix_server.conf DBPassword=password ``` ### e. 为Zabbix前端配置PHP ``` /etc/httpd/conf.d/zabbix.conf(注释掉如下代码) # php_value date.timezone Europe/Riga ``` ### f. 启动Zabbix server和agent进程 启动Zabbix server和agent进程,并为它们设置开机自启: ``` systemctl restart zabbix-server zabbix-agent httpd systemctl enable zabbix-server zabbix-agent httpd ``` 现在您的Zabbix server已经安装完成并开始运行! ### 配置Zabbix前端 连接到新安装的Zabbix前端: ``` http://server_ip_or_name/zabbix ``` 根据Zabbix文件里步骤操作: 安装前端 systemctl stop zabbix-agent ### 默认的用户名密码: ``` 用户名:Admin 密码:zabbix ``` **用户自定义脚本的位置:/usr/lib/zabbix/alertscripts** ## 问题: ### 1、zabbix server is not running ``` 1. selinux是否关闭。一定要关闭这个,开启selinux会引起一连串问题,甚至zabbix的discovery功能也不能正常使用 关闭SELinux的方法: 修改/etc/selinux/config文件中的SELINUX="" 为 disabled ,然后重启。 如果不想重启系统,使用命令setenforce 0 setenforce 1 设置SELinux 成为enforcing模式 setenforce 0 设置SELinux 成为permissive模式 在lilo或者grub的启动参数中增加:selinux=0,也可以关闭selinux ```
作者:微信小助手
<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">转自:掘金,作者:千山qianshan</span></p> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;">rrd.me/et29e</span></p> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">Redis占用内存大小</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我们知道Redis是基于内存的key-value数据库,因为系统的内存大小有限,所以我们在使用Redis的时候可以配置Redis能使用的最大的内存大小。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">1、通过配置文件配置</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过在Redis安装目录下面的redis.conf配置文件中添加以下配置设置内存大小</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//设置Redis最大占用内存大小为100M</span><br>maxmemory <span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">100</span>mb</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">2、通过命令修改</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Redis支持运行时通过命令动态修改内存大小</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//设置Redis最大占用内存大小为100M</span><br><span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">127.0.0.1</span>:<span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">6379</span>> config <span class="hljs-built_in" style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">set</span> maxmemory <span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">100</span>mb<br><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//获取设置的Redis能使用的最大内存大小</span><br><span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">127.0.0.1</span>:<span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">6379</span>> config get maxmemory</code></pre> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="59" data-source-title=""> <section class="js_blockquote_digest"> <p>如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">Redis的内存淘汰</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">既然可以设置Redis最大占用内存大小,那么配置的内存就有用完的时候。那在内存用完的时候,还继续往Redis里面添加数据不就没内存可用了吗?</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">实际上Redis定义了几种策略用来处理这种情况:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">noeviction(默认策略)</span><span style="font-size: 15px;">:对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">allkeys-lru:</span><span style="font-size: 15px;">从所有key中使用LRU算法进行淘汰</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">volatile-lru:</span><span style="font-size: 15px;">从设置了过期时间的key中使用LRU算法进行淘汰</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">allkeys-random:</span><span style="font-size: 15px;">从所有key中随机淘汰数据</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">volatile-random:</span><span style="font-size: 15px;">从设置了过期时间的key中随机淘汰</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">volatile-ttl:</span><span style="font-size: 15px;">在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="82" data-source-title=""> <section class="js_blockquote_digest"> <p>当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">如何获取及设置内存淘汰策略</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">获取当前内存淘汰策略:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">127.0.0.1</span>:<span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">6379</span>> config get maxmemory-policy</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过配置文件设置淘汰策略(修改redis.conf文件):</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp">maxmemory-policy allkeys-lru</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过命令修改淘汰策略:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">127.0.0.1</span>:<span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">6379</span>> config <span class="hljs-built_in" style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">set</span> maxmemory-policy allkeys-lru</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">LRU算法</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">什么是LRU?</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">上面说到了Redis可使用最大内存使用完了,是可以使用LRU算法进行内存淘汰的,那么什么是LRU算法呢?</span></p> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="185" data-source-title=""> <section class="js_blockquote_digest"> <p><span style="color: rgb(171, 25, 66);">LRU(Least Recently Used)</span>,即最近最少使用,是一种缓存置换算法。在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU算法了。其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">使用java实现一个简单的LRU算法</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> LRUCache<k, v> {<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//容量</span><br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> capacity;<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//当前有多少节点的统计</span><br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> count;<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//缓存节点</span><br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> Map<k, Node<k, v>> nodeMap;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> Node<k, v> head;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> Node<k, v> tail;<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">LRUCache</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(<span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> capacity)</span> </span>{<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (capacity < <span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">1</span>) {<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throw</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> IllegalArgumentException(String.valueOf(capacity));<br> }<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.capacity = capacity;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.nodeMap = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> HashMap<>();<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//初始化头节点和尾节点,利用哨兵模式减少判断头结点和尾节点为空的代码</span><br> Node headNode = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> Node(null, null);<br> Node tailNode = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> Node(null, null);<br> headNode.next = tailNode;<br> tailNode.pre = headNode;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.head = headNode;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.tail = tailNode;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">put</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(k key, v value)</span> </span>{<br> Node<k, v> node = nodeMap.get(key);<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (node == null) {<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (count >= capacity) {<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//先移除一个节点</span><br> removeNode();<br> }<br> node = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> Node<>(key, value);<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//添加节点</span><br> addNode(node);<br> } <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">else</span> {<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//移动节点到头节点</span><br> moveNodeToHead(node);<br> }<br> }<br><br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> Node<k, v> get(k key) {<br> Node<k, v> node = nodeMap.get(key);<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (node != null) {<br> moveNodeToHead(node);<br> }<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">return</span> node;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">removeNode</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">()</span> </span>{<br> Node node = tail.pre;<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//从链表里面移除</span><br> removeFromList(node);<br> nodeMap.remove(node.key);<br> count--;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">removeFromList</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(Node<k, v> node)</span> </span>{<br> Node pre = node.pre;<br> Node next = node.next;<br><br> pre.next = next;<br> next.pre = pre;<br><br> node.next = null;<br> node.pre = null;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">addNode</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(Node<k, v> node)</span> </span>{<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//添加节点到头部</span><br> addToHead(node);<br> nodeMap.put(node.key, node);<br> count++;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">addToHead</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(Node<k, v> node)</span> </span>{<br> Node next = head.next;<br> next.pre = node;<br> node.next = next;<br> node.pre = head;<br> head.next = node;<br> }<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">moveNodeToHead</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(Node<k, v> node)</span> </span>{<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//从链表里面移除</span><br> removeFromList(node);<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//添加节点到头部</span><br> addToHead(node);<br> }<br><br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> Node<k, v> {<br> k key;<br> v value;<br> Node pre;<br> Node next;<br><br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">Node</span><span class="hljs-params" style="font-weight: 400;font-style: normal;">(k key, v value)</span> </span>{<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.key = key;<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">this</span>.value = value;<br> }<br> }<br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="44" data-source-title=""> <section class="js_blockquote_digest"> <p>上面这段代码实现了一个简单的LUR算法,代码很简单,也加了注释,仔细看一下很容易就看懂。</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">LRU在Redis中的实现</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">近似LRU算法</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key。</span></p> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="91" data-source-title=""> <section class="js_blockquote_digest"> <p>可以通过maxmemory-samples参数修改采样数量: 例:maxmemory-samples 10 maxmenory-samples配置的越大,淘汰的结果越接近于严格的LRU算法</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);font-size: 15px;">Redis3.0对近似LRU的优化</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Redis3.0对近似LRU算法进行了一些优化。新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">当需要淘汰的时候,则直接从池中选取最近访问时间最小(最久没被访问)的key淘汰掉就行。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);font-size: 15px;">LRU算法的对比</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我们可以通过一个实验对比各LRU算法的准确率,先往Redis里面添加一定数量的数据n,使Redis可用内存用完,再往Redis里面添加n/2的新数据,这个时候就需要淘汰掉一部分的数据,如果按照严格的LRU算法,应该淘汰掉的是最先加入的n/2的数据。</span></p> <p><span style="font-size: 15px;">生成如下各LRU算法的对比图:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;"><img class="lazyload inited loaded" data-height="449" data-ratio="0.5257611241217799" src="/upload/d278544f561ed6cbddb6a99b1c7162d2.other" data-type="other" data-w="854" data-width="854" style="width: auto;border-style: none;max-height: none;visibility: visible;background-color: rgb(255, 255, 255);background-position: 50% center;background-repeat: no-repeat;cursor: zoom-in;font-family: -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;text-align: center;white-space: normal;"></span></p> <p style="text-align: center;"><span style="font-size: 12px;">图片来源:https://segmentfault.com/a/1190000017555834</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">你可以看到图中有三种不同颜色的点:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">浅灰色是被淘汰的数据</span></p></li> <li><p><span style="font-size: 15px;">灰色是没有被淘汰掉的老数据</span></p></li> <li><p><span style="font-size: 15px;">绿色是新加入的数据</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我们能看到Redis3.0采样数是10生成的图最接近于严格的LRU。而同样使用5个采样数,Redis3.0也要优于Redis2.8。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">LFU算法</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">LFU一共有两种策略:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key</span></p></li> <li><p><span style="font-size: 15px;">allkeys-lfu:在所有的key中使用LFU算法淘汰数据</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="68" data-source-title=""> <section class="js_blockquote_digest"> <p>设置使用这两种淘汰策略跟前面讲的一样,不过要注意的一点是这两周策略只能在Redis4.0及以上设置,如果在Redis4.0以下设置会报错</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">问题</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">最后留一个小问题,可能有的人注意到了,我在文中并没有解释为什么Redis使用近似LRU算法而不使用准确的LRU算法,可以在评论区给出你的答案,大家一起讨论学习。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">参考文献:</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">https://redis.io/topics/lru-cache</span></p> <p><span style="font-size: 15px;">https://segmentfault.com/a/1190000016743562</span></p> <p><span style="font-size: 15px;">https://segmentfault.com/a/1190000017555834</span></p> <p><span style="font-size: 15px;"><br></span></p> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;max-width: 100%;box-sizing: border-box;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="padding-top: 1.1em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;overflow-wrap: break-word !important;"> <section style="padding: 0.2em 0.4em;max-width: 100%;box-sizing: border-box;border-top-left-radius: 0px;border-top-right-radius: 0.5em;border-bottom-right-radius: 0.5em;border-bottom-left-radius: 0px;background-color: rgb(249, 110, 87);color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;">推荐阅读</strong></p> </section> <section style="max-width: 100%;box-sizing: border-box;width: 0px;border-right-width: 4px;border-right-style: solid;border-right-color: rgb(249, 110, 87);border-top-width: 4px;border-top-style: solid;border-top-color: rgb(249, 110, 87);overflow-wrap: break-word !important;border-left-width: 4px !important;border-left-style: solid !important;border-left-color: transparent !important;border-bottom-width: 4px !important;border-bottom-style: solid !important;border-bottom-color: transparent !important;"></section> </section> <section style="padding-left: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;color: rgb(160, 160, 160);font-size: 14px;overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;">(点击标题可跳转阅读)</p> </section> <section style="margin-top: -3.5em;margin-left: 8px;padding: 3.5em 10px 10px;max-width: 100%;box-sizing: border-box;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;font-size: 14px;line-height: 2.6;overflow-wrap: break-word !important;"> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651485180&idx=1&sn=f289bb3fc7401433b83302fc98fb3c53&chksm=bd251f838a52969583b3f8b0ebfded7d02e380e611b2a63fed106643571c74b2d48287ec8c09&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 12px;">Redis 不谈缓存和队列</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651483483&idx=1&sn=4a76d8b346d0c21cff5000d4f934dbe1&chksm=bd2501248a528832fe6b52109980152786028d6dd11e663c3360b31760e2f210e8dcf880cdc8&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 12px;">Redis 为何这么快--数据存储角度</span></a><br></p> <p><span style="text-decoration: underline;font-size: 12px;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651483657&idx=1&sn=9cdc5fec3d3d78bd1c2eeaec4d96e0b2&chksm=bd2502768a528b60e7e62e1ee885974591c968ba38fbda71f82c600d316f7f4452b832abd0a2&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;font-size: 12px;" data-linktype="2">Redis 的各项功能解决了哪些问题?</a></span></p> </section> </section> </section> </section> </section> </section> </section> </section> <p style="white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="" data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>
作者:Happy生龙
## 1.什么是SQL语句 sql语言:结构化的查询语言。(Structured Query Language),是关系数据库管理系统的标准语言。 它是一种解释语言:写一句执行一句,不需要整体编译执行。 语法特点: 1.没有“ ”,字符串使用‘ ’包含 2.没有逻辑相等,赋值和逻辑相等都是= 3.类型不再是最严格的。任何数据都可以包含在‘ ’以内 4.没有bool值的概念,但是在视图中可以输入true/false 5.它也有关系运算符:> < >= <= = <> != ,它返回一个bool值 6.它也有逻辑运算符: !(not) &&(and) ||(or) 7.它不区别大小写 ## 2.使用sql语句创建数据库和表 语法: ``` create database 数据库名称 on primary --默认在主文件组上 ( name='逻辑名称_data' , --当你发现它不是一句完整的sql语句,而仅仅是一个处理结构中的某一句的时候,就需要添加 , size=初始大小,--数值不包含在‘’以内 filegrowth=文件增长 , maxsize=最大容量, filename='物理路径' ) log on ( name='逻辑名称_log' , --当你发现它不是一句完整的sql语句,而仅仅是一个处理结构中的某一句的时候,就需要添加 , size=初始大小,--数值不包含在‘’以内 filegrowth=文件增长 , maxsize=最大容量, --一般来说日志文件不限制最大容量 filename='物理路径' ) ``` ##3.创建数据表 语法: ``` create table 表名 ( 字段名称 字段类型 字段特征(是否为null,默认值 标识列 主键 唯一键 外键 check约束), 字段名称 字段类型 字段特征(是否为null,默认值 标识列 主键 唯一键 外键 check约束) ) 创建老师表Teacher :Id、Name、Gender、Age、Salary、Birthday ``` ``` use School if exists(select * from sysobjects where name='Classes') drop table Classes create table Classes ( Classid int identity(1,1), ClassName nvarchar(50) not null ) if exists(select * from sysobjects where name='teacher') drop table teacher create table Teacher ( Id int identity(1,1),--可以同时创建多个特征,用空格 分隔开。 identity是标识列,第一个参数是种子,第二个是增量 Name nvarchar(50) not null,-- not null标记它的值不能为null--不能不填写 ClassId int not null, Gender bit not null, Age int , Salary money, --如果不标记为 not null.那么就相当于标记了null Birthday datetime ) ``` ##4.数据完整性约束 实体完整性:实体就是指一条记录。这种完整性就是为了保证每一条记录不是重复记录。是有意义的 -- 主键:非空和唯一.一个表只有一个主键,但是一个主键可以是由多个字段组成的 组合键 -- 标识列:系统自动生成,永远不重复 -- 唯一键:唯一,但是可以为null,只能null一次 域完整性:域就是指字段,它是为了保证字段的值是准和有效,合理值 -- 类型 是否null,默认值,check约束,关系 自定义完整性: -- check约束 , 存储过程 触发器 引用完整性:一个表的某个字段的值是引用自另外一个表的某个字段的值。引用的表就是外键表,被引用的表就是主键表 -- 1.建立引用的字段类型必须一致 -- 2.建立引用的字段的意义一样 -- 3.建立主外键关系的时候选择 外键表 去建立主外键关系 -- 4.建立主外键关系的字段在主表中必须是主键或者唯一键 -- 5.对于操作的影响 : -- 1.在添加数据时,先添加主键表再添加外键表数据 -- 2.在删除的时候先外键表数据再删除主键表数据 -- 级联的操作:不建议使用:会破坏数据完整性 -- 不执行任何操作:该报错就报错,该删除就删除 -- 级联:删除主表记录,从表引用该值的记录也被删除 -- 设置null:删除主表记录,从表对应的字段值设置为null,前提是可以为null -- 设置为default:删除主表记录,从表对应的字段值设置为default,前提是可以为default 主键约束(PK Primary key) 唯一键约束(UQ unique) 外键约束(FK foreign key) 默认值约束(DF default)check约束(CK check) 语法: ``` alter table 表名 add constraint 前缀_约束名称 约束类型 约束说明(字段 关系表达式 值) ``` ``` use School if exists(select * from sysobjects where name='PK_Classes_Classid') alter table classes drop constraint PK_Classes_Classid alter table classes add constraint PK_Classes_Classid primary key(classid) --为id添加主键 alter table teacher add constraint PK_teacher_id primary key(id) --为name添加唯一键 alter table teacher add constraint UQ_Teacher_Name unique(Name) --同时创建salary的默认约束和age的check约束 alter table teacher add constraint DF_Teacher_Salary default(5000) for salary, constraint CK_Teacher_Age check(age>0 and age<=100) --为teacher表的classid字段创建主外键 if exists(select * from sysobjects where name='FK_Teacher_Classes_Classid') alter table teacher drop constraint FK_Teacher_Classes_Classid alter table teacher with nocheck --不检查现有数据 add constraint FK_Teacher_Classes_Classid foreign key(classid) references classes(classid) --on delete set default 级联操作 --不执行任何操作:该报错就报错,该删除就删除 --no action --默认选择 --级联:删除主表记录,从表引用该值的记录也被删除 --cascade --设置null:删除主表记录,从表对应的字段值设置为null,前提是可以为null --set null --设置为default:删除主表记录,从表对应的字段值设置为default,前提是可以为default --set default ``` ## 5.四中基本字符类型说明 ``` --len(参数) --获取指定参数内容的字符个数 select LEN('abcd') 【4】运行结果 select LEN('中华人民共和国') 【7】 --DataLength(参数):获取指定内占据的字节数--空间大小 select DataLength('abcd') 【4】 select DataLength('中华人民共和国') 【14】 --char类型:当空间分配后,不会因为存储的内容比分配的空间小就回收分配的空间。但是如果存储的内容超出了指定的空间大小,就会报错,当你存储的内容的长度变化区间不大的时候可以考虑使用char select LEN(char) from CharTest 【2】 select DataLength(char) from CharTest 【10】 --varchar var--变化的:当你存储的内容小于分配的空间的时候,多余的空间会自动收缩。但是如果存储的内容超出了指定的空间大小,就会报错 当存储的内容波动区间比较大时候使用varchar select LEN(varchar) from CharTest 【2】 select DataLength(varchar) from CharTest 【2】 --nchar-- n代表它是一个unicode字符。规定不管什么样的字符都占据两个字节。 char:空间是固定的 select LEN(nchar) from CharTest 【10】 select DataLength(nchar) from CharTest 【20】 --nvarchar n var char select LEN(nvarchar) from CharTest 【2】 select DataLength(nvarchar) from CharTest 【4】 ``` ## 6.SQL基本语句 数据插入 调用方法 一 一对应原则:类型对应,数量对应,顺序对应 语法: 形参 实参 insert into 表名([字段列表]) values(值列表) --数据必须要符合数据完整性 插入操作是单个表的操作 插入操作insert一次只能插入一条记录 数据删除 语法: ``` delete [from] 表名 where 条件 ``` ``` delete from Teacher where Age<20 --特点: --1.删除是一条一条进行删除的 --2.每一条记录的删除都需要将操作写入到日志文件中 --3.标识列不会从种子值重新计算,以从上次最后一条标识列值往下计算 --4.这种删除可以触发delete触发器 --truncate table 表名 --没有条件,它是一次性删除所有数据 --特点: --1.一次性删除所有数据,没有条件,那么日志文件只以最小化的数据写入 --2.它可以使用标识列从种子值重新计算 --3.它不能触发delete触发器 truncate table teacher 数据更新(数据修改):一定需要考虑是否有条件 语法: ``` ``` update 表名 set 字段=值,字段=值 。。where 条件 ``` ``` update Teacher set Gender='true' --修改时添加条件 update Teacher set Gender=0 where Id=20 --多字段修改 update Teacher set ClassId=4,Age+=5,Salary=5000 where Id=22 --修改班级id=4,同时年龄》20岁的人员工资+500 update Teacher set Salary=Salary+500 where ClassId=4 and Age>20 ``` 数据检索--查询 语法: *代表所有字段 ``` select */字段名称列表 from 表列表 ``` ``` select StudentNo,StudentName,Sex,[Address] from Student --可以为标题设置 别名,别名可以是中文别名 select StudentNo as 学号,StudentName 姓名,性别=Sex,[Address] from Student --添加常量列 select StudentNo as 学号,StudentName 姓名,性别=Sex,[Address] ,国籍='中华人民共和国' from Student select的作用 ``` 聚合函数 模糊查询 分组统计 ## 7.类型转换函数 --select :输出为结果集--虚拟表 --print:以文本形式输出 只能输出一个字符串值. print 1+'a' select 1,2 select * from Student --类型转换 --Convert(目标类型,源数据,[格式]) --日期有格式 print '我的成绩是:'+convert(char(3),100) print '今天是个大日子:'+convert(varchar(30),getdate(),120) select getdate() select len(getdate()) --cast(源数据 as 目标类型) 它没有格式 print '我的成绩是:'+cast(100 as char(3)) ## 8.日期函数 --getdate():获取当前服务器日期 select GETDATE() --可以在源日期值是追加指定时间间隔的日期数 select DATEADD(dd,-90,GETDATE()) --dateDiff:找到两个日期之间指定格式的差异值 select StudentName,DATEDIFF(yyyy,getdate(),BornDate) as age from Student order by age --DATENAME:可以获取日期的指定格式的字符串表现形式 select DATENAME(dw,getdate()) --DATEPART:可以获取指定的日期部分 select cast(DATEPART(yyyy,getdate()) as CHAR(4))+'-' +cast(DATEPART(mm,getdate()) as CHAR(2))+'-' +cast(DATEPART(dd,getdate()) as CHAR(2)) ## 9.数学函数 --rand:随机数:返回0到1之间的数,理论上说可以返回0但是不能返回1 select RAND() --abs:absolute:取绝对值 select ABS(-100) --ceiling:获取比当前数大的最小整数 select CEILING(1.00) --floor:获取比当前数小的最大整数 select floor(1.99999) power: select POWER(3,4) --round():四舍五入.只关注指定位数后一位 select ROUND(1.549,1) --sign:正数==1 负数 ==-1 0=0 select SIGN(-100) select ceiling(17*1.0/5) ## 10.字符串函数 --1.CHARINDEX --IndexOf():能够返回一个字符串在源字符串的起始位置。找不到就返回0,如果可以找到就返回从1开始的索引--没有数组的概念 --第一个参数是指需要查询的字符串,第二个是源字符串,第三个参数是指从源字符的那个索引位置开始查找 select CHARINDEX('人民','中华人民共和国人民',4) --LEN():可以返回指定字符串的字符个数 select LEN('中华人民共和国') --UPPER():小写字母转换为大写字母 LOWER():大写转小写 select LOWER(UPPER('sadfasdfa')) --LTRIM:去除左空格 RTIRM:去除右空格 select lTRIM(RTRIM(' sdfsd '))+'a' --RIGHT:可以从字符串右边开始截取指定位数的字符串 如果数值走出范围,不会报错,只会返回所有字符串值,但是不能是负值 select RIGHT('中华人民共和国',40) select LEFT('中华人民共和国',2) --SUBSTRING() select SUBSTRING('中华人民共和国',3,2) --REPLACE 第一个参数是源字符串,第二个参数是需要替换的字符串,第三个参数是需要替换为什么 select REPLACE('中华人民共和国','人民','居民') select REPLACE('中 华 人民 共 和 国',' ','') --STUFF:将源字符串中从第几个开始,一共几个字符串替换为指定的字符串 select STUFF('中华人民共和国',3,2,'你懂的') --sudyfsagfyas@12fasdf6.fsadfdsaf declare @email varchar(50)='sudyfsagfyas@12fasdf6.fsadfdsaf' select CHARINDEX('@',@email) select LEFT(@email,CHARINDEX('@',@email)-1) --使用right select right(@email,len(@email)-CHARINDEX('@',@email)) --使用substring select SUBSTRING(@email,CHARINDEX('@',@email)+1,LEN(@email)) --使用stuff select STUFF(@email,1,CHARINDEX('@',@email),'') ## 11.联合结果集union --联合结果集union ``` select * from Student where Sex='男' --union select * from Student where Sex='女' ```` --联合的前提是: --1.列的数量需要一致:使用 UNION、INTERSECT 或 EXCEPT 运算符合并的所有查询必须在其目标列表中有相同数目的表达式 --2.列的类型需要可以相互转换 select StudentName,Sex from Student --在字符串排序的时候,空格是最小的,排列在最前面 union select cast(ClassId as CHAR(3)),classname from grade --union和union all的区别 --union是去除重复记录的 --union all不去除重复 :效率更高,因为不需要判断记录是否重复,也没有必须在结果庥是执行去除重复记录的操作。但是可以需要消耗更多的内存存储空间 select * from Student where ClassId=2 union all select * from Student where ClassId=2 --查询office这科目的全体学员的成绩,同时在最后显示它的平均分,最高分,最低分 select ' '+cast(StudentNo as CHAR(3)),cast(SubjectId as CHAR(2)),StudentResult from Result where SubjectId=1 union select '1','平均分',AVG(StudentResult) from Result where SubjectId=1 union select '1','最高分',max(StudentResult) from Result where SubjectId=1 union select '1','最低分',min(StudentResult) from Result where SubjectId=1 --一次性插入多条数据 --1.先将数据复制到另外一个新表中,删除源数据表,再将新表的数据插入到源数据表中 --1.select */字段 into 新表 from 源表 --1.新表是系统自动生成的,不能人为创建,如果新表名称已经存在就报错 --2.新表的表结构与查询语句所获取的列一致,但是列的属性消失,只保留非空和标识列。其它全部消失,如主键,唯一键,关系,约束,默认值 select * into newGrade from grade truncate table grade select * from newGrade --select * into grade from newGrade --2.insert into 目标表 select 字段列表/* from 数据源表 --1、目标表必须先存在,如果没有就报错 --2.查询的数据必须符合目标表的数据完整性 --3.查询的数据列的数量和类型必须的目标的列的数量和对象完全对应 insert into grade select classname from newGrade delete from admin --使用union一次性插入多条记录 --insert into 表(字段列表) --select 值。。。。 用户自定义数据 --union --select 值 。。。。 insert into Admin select 'a','a' union all select 'a','a' union all select 'a','a' union all select 'a',null ## 12.CASE函数用法 相当于switch case---c#中的switch...case只能做等值判断 这可以对字段值或者表达式进行判断,返回一个用户自定义的值,它会生成一个新列 2.要求then后面数据的类型一致 1.第一种做等值判断的case..end case 字段或者表达式 when .值..then .自定义值 when .值..then .自定义值 ..... else 如果不满足上面所有的when就满足这个else end --显示具体班级的名称 ``` select StudentNo,StudentName, case ClassId --如果case后面接有表达式或者字段,那么这种结构就只能做等值判断,真的相当于switch..case when 1 then '一班' when 2 then '2班' when 3 then '3班' when null then 'aa' --不能判断null值 else '搞不清白' end, sex from Student --2.做范围判断,相当于if..else,它可以做null值判断 --case --如果没有表达式或者字段就可实现范围判断 -- when 表达式 then 值 --不要求表达式对同一字段进行判断 -- when 表达式 then 值 -- ..... --else 其它情况 --end select StudentNo,StudentName, case when BornDate>'2000-1-1' then '小屁孩' when BornDate>'1990-1-1' then '小青年' when BornDate>'1980-1-1' then '青年' --when Sex='女' then '是女的' when BornDate is null then '出生不详' else '中年' end from Student --百分制转换为素质教育 90 -A 80--B 70 --C 60 --D <60 E NULL--没有参加考试 select StudentNo,SubjectId, case when StudentResult>=90 then 'A' when StudentResult>=80 then 'B' when StudentResult>=70 then 'C' when StudentResult>=60 then 'D' when StudentResult is null then '没有参加考试' else 'E' end 成绩, ExamDate from Result ``` ## 13.IF ELSE语法 1,.没有{},使用begin..end.如果后面只有一句,可以不使用begin..end包含 2.没有bool值,只能使用关系运算符表达式 3.也可以嵌套和多重 4.if后面的()可以省略 ``` declare @subjectname nvarchar(50)='office' --科目名称 declare @subjectId int=(select Subjectid from Subject where SubjectName=@subjectname) --科目ID declare @avg int --平均分 set @avg=(select AVG(StudentResult) from Result where SubjectId=@subjectId and StudentResult is not null) --获取平均分 print @avg if @avg>=60 begin print '成绩不错,输出前三名:' select top 3 * from Result where SubjectId=@subjectId order by StudentResult desc end else begin print '成绩不好,输出后三名:' select top 3 * from Result where SubjectId=@subjectId order by StudentResult end ``` ## 14.WHILE循环语法 没有{},使用begin..end 没有bool值,需要使用条件表达式 可以嵌套 也可以使用break,continue ``` go declare @subjectName nvarchar(50)='office' --科目名称 declare @subjectId int--科目ID declare @classid int =(select classid from Subject where SubjectName=@subjectName) --查询当前科目属于那一个班级 set @subjectId=(select SubjectId from Subject where SubjectName=@subjectName) --获取科目ID declare @totalCount int --总人数 :那一个班级需要考试这一科目 set @totalCount=(select COUNT(*) from Student where ClassId=@classid) print @totalcount --14 declare @unpassNum int --不及格人数 set @unpassNum=(select COUNT(distinct Studentno) from Result where SubjectId=@subjectId and StudentNo in(select StudentNo from Student where ClassId=@classid) and StudentResult<60) while(@unpassNum>@totalCount/2) begin --执行循环加分 update Result set StudentResult+=2 where SubjectId=@subjectId and StudentNo in(select StudentNo from Student where ClassId=@classid) and StudentResult<=98 --重新计算不及格人数 set @unpassNum=(select COUNT(distinct Studentno) from Result where SubjectId=@subjectId and StudentNo in(select StudentNo from Student where ClassId=@classid) and StudentResult<60) end go declare @subjectName nvarchar(50)='office' --科目名称 declare @subjectId int--科目ID declare @classid int =(select classid from Subject where SubjectName=@subjectName) --查询当前科目属于那一个班级 set @subjectId=(select SubjectId from Subject where SubjectName=@subjectName) --获取科目ID declare @totalCount int --总人数 set @totalCount=(select COUNT(*) from Student where ClassId=@classid) print @totalcount --14 declare @unpassNum int --不及格人数 while(1=1) begin set @unpassNum=(select COUNT(distinct Studentno) from Result where SubjectId=@subjectId and StudentNo in(select StudentNo from Student where ClassId=@classid) and StudentResult<60) if(@unpassNum>@totalCount/2) update Result set StudentResult+=2 where SubjectId=@subjectId and StudentNo in(select StudentNo from Student where ClassId=@classid) and StudentResult<=98 else break end ``` ## 15.子查询 子查询--一个查询中包含另外一个查询。被包含的查询就称为子查询,。包含它的查询就称父查询 1.子查询的使用方式:使用()包含子查询 2.子查询分类: 1.独立子查询:子查询可以直接独立运行 查询比“王八”年龄大的学员信息 select * from Student where BornDate<(select BornDate from Student where StudentName='王八') 2.相关子查询:子查询使用了父查询中的结果 --子查询的三种使用方式 --1.子查询做为条件,子查询接在关系运算符后面 > < >= <= = <> !=,如果是接这关系运算符后面,必须保证 子查询只返回一个值 --查询六期班的学员信息 ``` select * from Student where ClassId=(select ClassId from grade where classname='八期班') --子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。 select * from Student where ClassId=(select ClassId from grade) --查询八期班以外的学员信息 --当子查询返回多个值(多行一列),可以使用in来指定这个范围 select * from Student where ClassId in(select ClassId from grade where classname<>'八期班') --当没有用 EXISTS 引入子查询时,在选择列表中只能指定一个表达式。如果是多行多列或者一行多列就需要使用exists --使用 EXISTS 关键字引入子查询后,子查询的作用就相当于进行存在测试。外部查询的 WHERE 子句测试子查询返回的行是否存在 select * from Student where EXISTS(select * from grade) select * from Student where ClassId in(select * from grade) --2.子查询做为结果集-- select top 5 * from Student --前五条 --使用top分页 select top 5 * from Student where StudentNo not in(select top 5 studentno from Student) --使用函数分页 ROW_NUMBER() over(order by studentno),可以生成行号,排序的原因是因为不同的排序方式获取的记录顺序不一样 select ROW_NUMBER() over(order by studentno),* from Student --查询拥有新生成行号的结果集 注意:1.子查询必须的别名 2.必须为子查询中所有字段命名,也就意味着需要为新生成的行号列命名 select * from (select ROW_NUMBER() over(order by studentno) id,* from Student) temp where temp.id>0 and temp.id<=5 select * from (select ROW_NUMBER() over(order by studentno) id,* from Student) temp where temp.id>5 and temp.id<=10 select * from (select ROW_NUMBER() over(order by studentno) id,* from Student) temp where temp.id>10 and temp.id<=15 ``` --3.子查询还可以做为列的值 ``` select (select studentname from student where studentno=result.studentno),(select subjectname from subject where subjectid=result.SubjectId), StudentResult from Result --使用Row_number over()实现分页 --1.先写出有行号的结果集 select ROW_NUMBER() over(order by studentno),* from Student --2.查询有行号的结果集 子查询做为结果集必须添加别名,子查询的列必须都有名称 select * from (select ROW_NUMBER() over(order by studentno) id,* from Student) temp where id>0 and id<=5 --查询年龄比“廖杨”大的学员,显示这些学员的信息 select * from Student where BornDate<(select BornDate from Student where StudentName='廖杨') --查询二期班开设的课程 select * from Subject where ClassId=(select ClassId from grade where classname='二期班') --查询参加最近一次“office”考试成绩最高分和最低分 --1查询出科目 ID select subjectid from Subject where SubjectName='office' ``` --2.查询出这一科目的考试日期 ``` select MAX(ExamDate) from Result where SubjectId=(select subjectid from Subject where SubjectName='office') --3,写出查询的框架 select MAX(StudentResult),MIN(StudentResult) from Result where SubjectId=() and ExamDate=() --4.使用子查询做为条件 select MAX(StudentResult),MIN(StudentResult) from Result where SubjectId=( select subjectid from Subject where SubjectName='office' ) and ExamDate=( select MAX(ExamDate) from Result where SubjectId=( select subjectid from Subject where SubjectName='office' ) ) ``` ## 16.表连接Join --1.inner join :能够找到两个表中建立连接字段值相等的记录 --查询学员信息显示班级名称 select Student.StudentNo,Student.StudentName,grade.classname from Student inner join grade on Student.ClassId=grade.ClassId --左连接: 关键字前面的表是左表,后面的表是右表 --左连接可以得到左表所有数据,如果建立关联的字段值在右表中不存在,那么右表的数据就以null值替换 select PhoneNum.*,PhoneType.* from PhoneNum left join PhoneType on PhoneNum.pTypeId=PhoneType.ptId --右连接: 关键字前面的表是左表,后面的表是右表 --右连接可以得到右表所有数据,如果建立关联的字段值在右左表中不存在,那么左表的数据就以null值替换 select PhoneNum.*,PhoneType.* from PhoneNum right join PhoneType on PhoneNum.pTypeId=PhoneType.ptId --full join :可以得到左右连接的综合结果--去重复 select PhoneNum.*,PhoneType.* from PhoneNum full join PhoneType on PhoneNum.pTypeId=PhoneType.ptId ## 17.事务 一种处理机制。以事务处理的操作,要么都能成功执行,要么都不执行 事务的四个特点 ACID: A:原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。它是一个整体,不能再拆分 C:一致性:事务在完成时,必须使所有的数据都保持一致状态。。某种程度的一致 I:隔离性:事务中隔离,每一个事务是单独的请求将单独的处理,与其它事务没有关系,互不影响 D:持久性:如果事务一旦提交,就对数据的修改永久保留 使用事务: 将你需要操作的sql命令包含在事务中 1.在事务的开启和事务的提交之间 2.在事务的开启和事务的回滚之间 三个关键语句: 开启事务:begin transaction 提交事务:commit transaction 回滚事务:rollback transaction declare @num int =0 --记录操作过程中可能出现的错误号 begin transaction update bank set cmoney=cmoney-500 where name='aa' set @num=@num+@@ERROR --说明这一句的执行有错误 但是不能在语句执行的过程中进行提交或者回滚 --语句块是一个整体,如果其中一句进行了提交或者回滚,那么后面的语句就不再属于当前事务, --事务不能控制后面的语句的执行 ``` update bank set cmoney=cmoney+500 where name='bb' set @num=@num+@@ERROR select * from bank if(@num<>0 ) --这个@@ERROR只能得到最近一一条sql语句的错误号 begin print '操作过程中有错误,操作将回滚' rollback transaction end else begin print '操作成功' commit transaction end --事务一旦开启,就必须提交或者回滚 --事务如果有提交或者回滚,必须保证它已经开启 ``` ## 18.视图 视图就是一张虚拟表,可以像使用子查询做为结果集一样使用视图 select * from vw_getinfo 使用代码创建视图 语法: create view vw_自定义名称 as 查询命令 go --查询所有学员信息 ``` if exists(select * from sysobjects where name='vw_getAllStuInfo') drop view vw_getAllStuInfo go --上一个批处理结果的标记 create view vw_getAllStuInfo as --可以通过聚合函数获取所以记录数 select top (select COUNT(*) from Student) Student.StudentNo,Student.StudentName,grade.ClassId,grade.classname from Student inner join grade on Student.ClassId=grade.ClassId order by StudentName --视图中不能使用order by --select * from grade --只能创建一个查询语句 --delete from grade where ClassId>100 --在视图中不能包含增加删除修改 go ``` --使用视图。。就像使用表一样 ``` select * from vw_getAllStuInfo --对视图进行增加删除和修改操作--可以对视图进行增加删除和修改操作,只是建议不要这么做:所发可以看到:如果操作针对单个表就可以成功,但是如果 多张的数据就会报错:不可更新,因为修改会影响多个基表。 update vw_getAllStuInfo set classname='asdas' ,studentname='aa' where studentno=1 ``` ## 19.触发器 触发器:执行一个可以改变表数据的操作(增加删除和修改),会自动触发另外一系列(类似于存储过程中的模块)的操作。 语法: ``` create trigger tr_表名_操作名称 on 表名 after|instead of 操作名称 as go if exists(select * from sysobjects where name='tr_grade_insert') drop trigger tr_grade_insert go create trigger tr_grade_insert on grade for insert ---为grade表创建名称为tr_grade_insert的触发器,在执行insert操作之后触发 as declare @cnt int set @cnt = (select count(*) from student) select * ,@cnt from student select * from grade go ``` --触发器不是被调用的,而是被某一个操作触 发的,意味着执行某一个操作就会自动触发 触发器 ``` insert into grade values('fasdfdssa') ---替换触 发器:本来需要执行某一个操作,结果不做了,使用触 发器中的代码语句块进行替代 if exists(select * from sysobjects where name='tr_grade_insert') drop trigger tr_grade_insert go create trigger tr_grade_insert on grade instead of insert ---为grade表创建名称为tr_grade_insert的触发器,在执行insert操作之后触发 as declare @cnt int set @cnt = (select count(*) from student) select * ,@cnt from student select * from grade go insert into grade values('aaaaaaaaaaaa') go ---触 发器的两个临时表: --inserted: 操作之后的新表:所有新表与原始的物理表没有关系,只与当前操作的数据有关 --deleted:操作之前的旧表:所有新表与原始的物理表没有关系,只与当前操作的数据有关 if exists(select * from sysobjects where name='tr_grade_insert') drop trigger tr_grade_insert go create trigger tr_grade_insert on grade after insert as print '操作之前的表:操作之前,这一条记录还没有插入,所以没有数据' select * from deleted print '操作之后的表:已经成功插入一条记录,所有新表中有一条记录' select * from inserted go --测试: insert into grade values('aaaaa') if exists(select * from sysobjects where name='tr_grade_update') drop trigger tr_grade_update go create trigger tr_grade_update on grade after update as print '操作之前的表:存储与这个修改操作相关的没有被修改之前的记录' select * from deleted print '操作之后的表:存储这个操作相关的被修改之后 记录' select * from inserted go --测试 update grade set classname=classname+'aa' where ClassId>15 if exists(select * from sysobjects where name='tr_grade_delete') drop trigger tr_grade_delete go create trigger tr_grade_delete on grade after delete as print '操作之前的表:存储与这个修改操作相关的没有被删除之前的记录' select * from deleted print '操作之后的表:存储这个操作相关的被删除之后 记录--没有记录' select * from inserted go --测试 delete from grade where ClassId>15 ``` ## 20.存储过程 存储过程就相当于c#中的方法 参数,返回值,参数默认值,参数:值的方式调用 在调用的时候有三个对应:类型对应,数量对应,顺序对应 创建语法: ``` create proc usp_用户自定义名称 对应方法的形参 --(int age, out string name) as 对应方法体:创建变量,逻辑语句,增加删除修改和查询..return返回值 go 调用语法: exec 存储过程名称 实参,实参,实参 ... ``` --获取所有学员信息 ``` if exists(select * from sysobjects where name='usp_getAllStuInfo') drop proc usp_getAllStuInfo go create procedure usp_getAllStuInfo as select * from Student go --调用存储过程,获取的有学员信息 execute usp_getAllStuInfo --exec sp_executesql 'select * from Student' --查询指定性别的学员信息 go if exists(select * from sysobjects where name='usp_getAllStuInfoBySex') drop proc usp_getAllStuInfoBySex go create procedure usp_getAllStuInfoBySex @sex nchar(1) --性别 参数不需要declare as select * from Student where Sex=@sex go --调用存储过程,获取指定性别的学员信息 Exec usp_getAllStuInfoBySex '女' --创建存储过程获取指定班级和性别的学员信息 go if exists(select * from sysobjects where name='usp_getAllStuInfoBySexandClassName') drop proc usp_getAllStuInfoBySexandClassName go create procedure usp_getAllStuInfoBySexandClassName @classname nvarchar(50), --班级名称 @sex nchar(1)='男'--性别 有默认的参数建议写在参数列表的最后 as declare @classid int ---班级ID set @classid=(select classid from grade where classname=@classname) --通过参数班级名称获取对应的班级ID select * from Student where Sex=@sex and ClassId=@classid go --执行存储过程获取指定班级和性别的学员信息 --exec usp_getAllStuInfoBySexandClassName '八期班' exec usp_getAllStuInfoBySexandClassName default, '八期班' --有默认值的参数可以传递default exec usp_getAllStuInfoBySexandClassName @classname='八期班' --也可以通过参数=值的方式调用 exec usp_getAllStuInfoBySexandClassName @classname='八期班' ,@sex='女' exec usp_getAllStuInfoBySexandClassName @classname='八期班',@sex='女' --创建存储过程,获取指定性别的学员人数及总人数 go if exists(select * from sysobjects where name='usp_getCountBySexandClassName') drop proc usp_getCountBySexandClassName go create procedure usp_getCountBySexandClassName @cnt int=100 output, --output标记说明它是一个输出参数。output意味着你向服务器请求这个参数的值,那么在执行的时候,服务器发现这个参数标记了output,就会将这个参数的值返回输出 @totalnum int =200output, --总人数 @className nvarchar(50), --输入参数没有默认值,在调用的时候必须传入值 @sex nchar(1)='男'--输入参数有默认值,用户可以选择是否传入值 as declare @classid int ---班级ID set @classid=(select classid from grade where classname=@classname) --通过参数班级名称获取对应的班级ID select * from Student where Sex=@sex and ClassId=@classid set @cnt= (select COUNT(*) from Student where Sex=@sex and ClassId=@classid) --获取指定班级和性别的总人数 set @totalnum=(select COUNT(*) from Student) ----获取总人数 go --调用存储过程,获取指定性别的学员人数及总人数 declare @num int,@tnum int exec usp_getCountBySexandClassName @cnt=@num output ,@totalnum=@tnum output , @className='八期班' print @num print @tnum print '做完了' ---获取指定班级的人数 if exists(select * from sysobjects where name='usp_getCount') drop proc usp_getCount go create procedure usp_getCount @className nvarchar(50)='八期班' as declare @classid int=(select classid from grade where classname=@className) declare @cnt int set @cnt =(select COUNT(*) from Student where ClassId=@classid) --return 只能返回int整数值 --return '总人数是'+cast(@cnt as varchar(2)) return @cnt go --调用存储过程,接收存储过程的返回值 declare @count int --set @count=(exec usp_getCount) exec @count=usp_getCount '八期班' print @count if exists(select * from sysobjects where name='usp_getClassList') drop proc usp_getClassList go create procedure usp_getClassList as select classid,classname from grade go ``` ## 21.分页存储过程 ``` if exists(select * from sysobjects where name='usp_getPageData') drop proc usp_getPageData go create procedure usp_getPageData @totalPage int output,--总页数 @pageIndex int =1 ,--当前页码,默认是第一页 @pageCount int =5 --每一页显示的记录数 as select * from (select ROW_NUMBER() over(order by studentno) id,* from Student) temp where temp.id>(@pageindex-1)*@pagecount and temp.id<=(@pageindex*@pagecount) set @totalPage=CEILING((select COUNT(*) from Student)*1.0/@pageCount) go ``` ## 22.索引 ``` select * from sysindexes --create index IX_Student_studentName --on 表名(字段名) --clustered index:聚集索引 nonclustered index--非聚集索引 if exists(select * from sysindexes where name='IX_Student_studentName') drop index student.IX_Student_studentName go create clustered index IX_Student_studentName on student(studentname) --如果是先创建主键再创建聚集索引就不可以,因为主键默认就是聚集索引 --但是如果先创建聚集索引,那么还可以再创建主键,因为主键不一定需要是聚集的 ``` ## 23.临时表 --创建局部临时表 ``` create table #newGrade ( classid int , classname nvarchar(50) ) ---局部临时表只有在当前创建它的会话中使用,离开这个会话临时表就失效.如果关闭创建它的会话,那么临时表就会消失 insert into #newGrade select * from grade select * from #newGrade select * into #newnewnew from grade select * into newGrade from #newgrade --创建全局临时表:只要不关闭当前会话,全局临时表都可以使用,但是关闭当前会话,全局临时表也会消失 create table ##newGrade ( classid int , classname nvarchar(50) ) drop table ##newGrade select * into ##newGrade from grade select * from ##newGrade --创建表变量 declare @tb table(cid int,cname nvarchar(50)) insert into @tb select * from grade select * from @tb ```
作者:じ☆ve宝贝
## 14.模板方法模式(Template Method) 解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:  就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子: ``` public abstract class AbstractCalculator { /*主方法,实现对本类其它方法的调用*/ public final int calculate(String exp,String opt){ int array[] = split(exp,opt); return calculate(array[0],array[1]); } /*被子类重写的方法*/ abstract public int calculate(int num1,int num2); public int[] split(String exp,String opt){ String array[] = exp.split(opt); int arrayInt[] = new int[2]; arrayInt[0] = Integer.parseInt(array[0]); arrayInt[1] = Integer.parseInt(array[1]); return arrayInt; } } ``` ``` public class Plus extends AbstractCalculator { @Override public int calculate(int num1,int num2) { return num1 + num2; } } ``` 测试类: ``` public class StrategyTest { public static void main(String[] args) { String exp = "8+8"; AbstractCalculator cal = new Plus(); int result = cal.calculate(exp, "\\+"); System.out.println(result); } } ``` 跟踪下这个小程序的执行过程:首先将exp和"\+"做参数,调用AbstractCalculator类里的calculate(String,String)方法,在calculate(String,String)里调用同类的split(),之后再调用calculate(int ,int)方法,从这个方法进入到子类中,执行完return num1 + num2后,将值返回到AbstractCalculator类,赋给result,打印出来。正好验证了我们开头的思路。
作者:微信小助手
<p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一个互联网应用(例如网上商城),搜索功能基本上是必备的。搜索的解决方案要快,最好有一个零配置和完全免费的搜索模式,能够简单地使用JSON通过HTTP的索引数据。搜索服务器始终可用,并能够从一台扩展到数百台,搜索的实时性要好......</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5913426265590609" data-s="300,640" src="/upload/f76462f9c4890b064c1499d5495d0b3c.png" data-type="png" data-w="1363" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Elasticsearch(简称ES)不仅可以解决这些问题,而且可以做更多的事情。</p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES 是什么</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES是基于Lucene的实时搜索和分析引擎,可以用来做全文检索、结构化数据检索、聚合分析等等。ES支持分布式部署到数百节点,以及PB级别的数据。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">2010年有了第一个版本,后来成立了商用公司,到2014年2月发布的1.0版本,直到最新的6.5版本。多家云服务厂商也提供了ES服务。ES对外提供RESTful API接口,数据以JSON形式组织,查询也以JSON形式来描述。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">为了通俗的理解,可以与MySQL 进行类比。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.6456" data-s="300,640" src="/upload/7b1103d8167d57e09df95b2e82b6922b.png" data-type="png" data-w="1250" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES的集群类似于数据库集群。Index的名字,相当于database的名字;Type的名字,相当于table名; Field的名字,相当于column名,这里对Field有约束: 数据类型必须是string,否则报错,另外建立索引时对string做分词处理。在http的URL里包含插入的Index,Type,还有Document的唯一标识ID。如果没有指定ID,那么ES会自动生成ID。</p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Lucene 基础</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES 是构建在Lucene基础之上的,了解一下Lucene是必要的。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.6007259528130672" data-s="300,640" src="/upload/bf6a1441a14a7ca35ffebaeff9f8969f.jpg" data-type="jpeg" data-w="551" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">类似Lucene这样的检索工具,主要过程包括获取内容,构建文档,分析文档和索引文件。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Lucene Directory 是一个抽象的文件系统的接口,用来允许你读写文件,不管lucene的索引是存放在内存中还是在物理磁盘上,它都是通过lucene的Directory抽象层来访问和维护的。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">IndexWriter 用来添加、删除和更新lucene里面的索引文档。这些操作是在内存中完成以保证更好的性能,但是保证这些操作的持久化,这些操作是需要flush到磁盘的。flush操作或者是显式的commit提交开销都是比较大的,每次只能有一个IndexWriter对象来对一个索引目录进行索引操作,并且创建这个对象的开销很大。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.40150093808630394" data-s="300,640" src="/upload/f3215bcbd6e457190bb792a61be70cc8.jpg" data-type="jpeg" data-w="533" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Lucene 索引被分解为很多段(segments)。每个索引段实际上是一个功能完整的lucene索引,一旦一个索引段创建完成,它将是不可变的,并且不能删除段里面的索引文档。lucene内部会来对commit提交的这些段进行合并,所以要有策略来控制这些合并。</p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES架构概要</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Elastic Stack 整体的架构如下:</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5497237569060773" data-s="300,640" src="/upload/e3cc38c8411dc9713f0648f7152b4d9c.png" data-type="png" data-w="1448" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES的集群节点主要分为Master Node和Data Node,每个节点上都存有全量的元数据。元数据的修改从master node开始,然后全量/增量地publish到数据节点。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Master Node负责节点加入与退出,Index创建与删除、Mapping修改,集群配置修改(比如线程数,Shard分配,移动等)。Data Node负责存储用户数据。每个节点的代码一致,只是根据配置看是否启用相应的功能.</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">随着产品的升级,引入了更多节点类型:</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="2.198237885462555" data-s="300,640" src="/upload/e01357ec18ee7c04260d2632460a68b6.png" data-type="png" data-w="454" style="width: 260px;height: 570px;"></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES有很多特点,个人觉得需要重点关注的是——Sharding,Replica 和Transaction Log。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Sharding</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">在实用性的角度来看基于文档的分区方式已经被证明是一个构建大型的分布式信息检索系统的一种行之有效的方法, ES 使用的就是基于文档的分区方式。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一个Index被划分为多个shard,每个shard都有一个单独的Lucene引擎。通过REST API来指定shard(Shardid = hash(doc id)% shard_num)的数目,并且只能在创建Index时指定。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Replication</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一般有两种方式来实现复制: Push Replication(推模式) 和 Pull Replication(拉模式)。ES 使用的是Push Replication(推模式)。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">用户通过REST API来指定副本的数目,可以在创建Index时指定,也可以创建完成后修改。在多个replica中,ES会选择一个来做Primary。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES的写入流程如下:</p> <ol style="" class=" list-paddingleft-2"> <li><p><span style="font-size: 14px;">Client 将数据发送到链接的节点上</span></p></li> <li><p><span style="font-size: 14px;">节点根据routing计算,把数据发送到primary节点上</span></p></li> <li><p><span style="font-size: 14px;">Primary local写入成功</span></p></li> <li><p><span style="font-size: 14px;">发送给replica</span></p></li> </ol> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES的查询流程分为两个阶段:Query 和 Fetch。具体流程如下:</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">1)客户端把search请求发送到某个节点上,该节点把请求转发到所有的shard上,每个shard收到请求后,需要在本地执行请求,并建立一个优先级队列。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">2)每个shard把doc id和排序相关的值返回给该节点,此节点合并各个shard的结果,并建立本地的优先级队列。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">3)该节点根据本地排序的结果计算出那些doc需要获取,向对应的shard发送fetch请求。每个shard从本地读取文档,返回给节点。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">4)此节点获取完所有的文档后,把结果返回到客户端。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Transaction Log</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">索引提交的开销较大大,但又必须通过提交操作来保证数据被可靠的持久化,ES通过使用 transaction log,通过日志记录发生在索引上的各种操作,来保证数据的持久化。并且能够很自然的支持推送复制,就算某些节点崩溃了,如果有必要,可以很轻松对日志操作进行回放。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Transaction log 周期性的将数据刷新(flushed)到磁盘,你可以通过 参数 来进行控制。 简单来说就是保存两次提交之间的连续数据操作的记录。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ransaction log还有一个重要的功能就是可以保证在生成快照、分片恢复或是分片热迁移的时候,索引数据不会丢失。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES的大规模部署架构大约是这样的:</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.4354066985645933" data-s="300,640" src="/upload/e25b5494ab6429a60aa1977a36a1190a.png" data-type="png" data-w="1254" style=""></p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一些场景</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">举几个例子吧,Github使用ES搜索20TB的数据,包括数十亿的文件和数千亿行的代码。Github在2013年1月升级了代码搜索,由solr转为ES详情请看博客https://github.com/blog/1381-a-whole-new-code-search。而Mozilla公司以火狐著名,使用 WarOnOrange 这个项目来进行单元或功能测试,测试的结果以 json的方式索引到ES中,开发人员可以非常方便的查找 bug。Socorro是公司的程序崩溃报告系统,一有错误信息就插入到 Hbase和PG中,然后从 Hbase中读取数据索引到ES中,方便查找。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5233333333333333" data-s="300,640" src="/upload/2c65db78cd1dcd077aea6a34b326b31e.jpg" data-type="jpeg" data-w="1200" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">除了搜索功能以外,ES 还有一些典型的应用场景。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">日志分析</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ELK是社区非常流行的一个用来收集和分析日志的架构:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: 14px;">Beats: 轻量级数据摄取组件</span></p></li> <li><p><span style="font-size: 14px;">Logstash:ETL组件,把数据抽取并写入ES</span></p></li> <li><p><span style="font-size: 14px;">ES: 分布式的搜索引擎</span></p></li> <li><p><span style="font-size: 14px;">Kibana: 查询ES并做数据可视化,百度自己也开发了一些可视化组件</span></p></li> </ul> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5207496653279786" data-s="300,640" src="/upload/c498cf96f2a70b7a6ad69a33b9caa298.png" data-type="png" data-w="1494" style=""></p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">APM度量<br></h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">度量是性能优化的前提,ES与APM结合,可以更好的完成性能可视化的工作。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.40273972602739727" data-s="300,640" src="/upload/b6840b5c0e078baae928a0ee7b454423.png" data-type="png" data-w="1460" style=""></p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">多租户视图</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES中的用户体系包括内置用户(root/superuser)和普通用户。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Root是内置的系统管理用户,进行cluster setting,recovery设置,节点加入退出等。Superuser是管理数据的超级用户,能够创建用户,访问所有用户的数据等。普通用户由Superuser来建立,用来操作相应的业务数据,其权限类型包括<code style="-webkit-print-color-adjust: exact;margin-right: 2px;margin-left: 2px;padding-right: 5px;padding-left: 5px;white-space: nowrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;">READ_ONLY</code>和<code style="-webkit-print-color-adjust: exact;margin-right: 2px;margin-left: 2px;padding-right: 5px;padding-left: 5px;white-space: nowrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;">READ_WRITE</code>。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.4288354898336414" data-s="300,640" src="/upload/7801806dada07560dfb777e370415599.png" data-type="png" data-w="1082" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">多租户管理用来解决多个业务共享一个集群的问题,隔离的内容包括配置,索引,cpu和io等。每个节点上启动多个实例,每个实例配置IO,CPU,MEMORY和DISK QUOTA等。Master节点。根据一定的策略把实例分配给一个租户。对用户的可视化呈现同样如此。</p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">遇到过的一些问题</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">“没有银弹”,ES 也不例外,踩坑和填坑是必然的成长过程。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.6636363636363637" data-s="300,640" src="/upload/8390f58abfbfed66a84467ff15985d5e.jpg" data-type="jpeg" data-w="440" style=""></p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES不稳定问题</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES不稳定的后果比较严重,可能会停服。大多数不稳定是由高并发读/写。一般滴,可以考虑用户写入MySQL或修改操作、事务仲裁,做数据备份,并提供降级服务,以防止ES不稳定,但MySQL的MPP查询是有限制。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">在捕获MySQL的变更之后,ES完成分析查询。需要注意的是,部分业务有分词需求。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">数据不一致问题</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">各个节点的元数据不是实时一致的。客户端可能连接到一个旧的Primary,然后读取到旧数据的副本集,从而不能拒绝处于“假死”状态Primary的写请求。另外,各个副本接收到的数据的顺序也可能不一致。一旦Primary出现故障,无法通过简单的Truncate机制将各副本的数据恢复到一致状态。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">这时,可以考虑快照抓取和lease机制。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">shard 的恢复问题</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">当ES集群挂掉一个节点,可能会触发集群中大量的shard恢复和迁移操作,占用大量的系统资源,此时继续写入入就会因为个别节点负载过高而导致速度下降甚至超时。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一般地,可以放宽shard的迁移条件,减少不必要的平衡。当必须要进行shard迁移时,优先移动那些“较老”当shard,或者延迟recovery的时间。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">总少不了OOM</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES查询时会把字段值放到内存,尤其是facet查询,对内存要求高,会把结果都放在内存,然后进行排序等操作,一直到使用完毕,当内存不够用时就有可能出现out of memory错误了。</p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">一般地, 可以考虑将缓存类型设置为软引用,并调整ES的最大缓存数据条数和缓存失效时间。</p> <h3 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 18px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">跨机房脑裂</h3> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">Master节点需要至少<code style="-webkit-print-color-adjust: exact;margin-right: 2px;margin-left: 2px;padding-right: 5px;padding-left: 5px;white-space: nowrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;">minimum_master_nodes</code><span class="Apple-converted-space"> </span>节点投票才能赢得选举,将其设置为合适的数据可部分防止脑裂的场景。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.28888888888888886" data-s="300,640" src="/upload/e674b620ce533ec60738c225a3ada2f8.png" data-type="png" data-w="1350" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">生产集群一般有3个专用的master节点,可以设置<span style="color: rgb(0, 0, 0);font-family: monospace;font-size: 14px;text-align: start;caret-color: rgb(0, 0, 0);background-color: rgb(248, 248, 248);">minimum_master_nodes</span>=2。</p> <h2 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;font-size: 24px;border-bottom: 1px solid rgb(204, 204, 204);color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">照见现实</h2> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">ES 从开始的Elasticsearch发展成了Elast Stack,那些x-pack的插件也都称为了ES的诸多feature:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: 14px;">Security</span></p></li> <li><p><span style="font-size: 14px;">Alerting</span></p></li> <li><p><span style="font-size: 14px;">Monitoring</span></p></li> <li><p><span style="font-size: 14px;">Reporting</span></p></li> <li><p><span style="font-size: 14px;">Graph</span></p></li> <li><p><span style="font-size: 14px;">Machine Learning</span></p></li> <li><p><span style="font-size: 14px;">Elasticsearch SQL</span></p></li> <li><p><span style="font-size: 14px;">Canvas</span></p></li> </ul> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">在各种技术的组合应用中,ES同样凸显价值,例如与Kafka配合完成跨机房数据传输,与Flink配合实现实时流数据查询,与各种分析工具的无缝衔接等等。个人曾经尝试过 ES over Yarn,现在ES已经几乎是Hadoop上的一个标准插件了。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5716272600834492" data-s="300,640" src="/upload/4f501d892169c0a72347c10cea0bc8fe.png" data-type="png" data-w="1438" style=""></p> <p style="-webkit-print-color-adjust: exact;margin-top: 15px;margin-bottom: 15px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Helvetica, arial, sans-serif;font-size: 14px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">这是一个云计算的时代,各大公有云厂商(例如,国外的AWS/Azure,国内的百度云/阿里云/青云等等)也都提供了ES云服务。当然,使用ES可以构建自己的私有云服务,实现企业部署。</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.5138888888888888" data-s="300,640" src="/upload/7c5fd14c7156ddd04067e687cc3a1258.png" data-type="png" data-w="720" style=""></p> <h6 style="-webkit-print-color-adjust: exact;margin-top: 20px;margin-bottom: 10px;font-weight: bold;-webkit-font-smoothing: antialiased;cursor: text;color: rgb(119, 119, 119);font-size: 14px;font-family: Helvetica, arial, sans-serif;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);">参考资料</h6> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: 12px;">https://lucene.apache.org</span></p></li> <li><p><span style="font-size: 12px;">https://www.elastic.co/</span></p></li> <li><p><span style="font-size: 12px;">https://www.elastic.co/pdf/architecture-best-practices.pdf</span></p></li> </ul> <p><br></p>