文章列表

【阿里云】Linux系统初始化后重新挂载数据盘

作者:じ☆ve宝贝

问题:如果服务器需要初始化系统,数据盘数据不需要改变,初始化服务器后需要对数据盘重新挂载。 1、输入mount命令可以看到并没有挂载数据盘,输入fdisk -l 命令,看到已经分区的数据盘 /dev/xvdb1 (设备编号以实际情况为准)。 ![阿里云重新挂载数据盘](/upload/c69359fa997c40cabf868a45dd7895d1.jpg "阿里云重新挂载数据盘") 2、输入命令mount /dev/xvdb1 /目录,下面的例子是挂载到了/alidata1 目录上,这个目录需要按照各自实际需要挂载的目录进行填写,这里只是举例。 ![阿里云重新挂载数据盘](/upload/3850787c55a2427c96edb7e596ef299a.jpg "阿里云重新挂载数据盘") 挂载后再输入mount命令可以看到数据盘已经挂载,并且格式是ext4 3、修改/etc/fstab自动挂载文件,增加数据盘挂载条目,分区类型是ext3还是ext4要注意与刚才执行mount命令看到类型相对应。 ![阿里云重新挂载数据库](/upload/45406f3cbc564cc98122db5b8b4405ed.jpg "阿里云重新挂载数据盘") 4、最后可以使用umount命令卸除磁盘挂载,再重新使用mount -a 命令查看是否可以自动挂载上。 ![阿里云重新挂载数据盘](/upload/e6c85626ba604ce785dbd267275f829a.jpg "阿里云重新挂载数据盘")

Eclipse 安装SVN插件老是弹出subversive connector discovery的解决方案

作者:じ☆ve宝贝

wokrspace下面有个.metadata的目录,该目录主要是保存eclipse在该worksapce的运行时信息,比如项目结构,透视图等的组织信息 进入该目录,删除相关文件夹,重新启动,不断重试,最终定位到: ``` .metadata\.plugins\org.eclipse.core.runtime\.settings\org.eclipse.ui.workbench.prefs ``` 该文件,通过文件对比工具,比较出现该故障前和故障后的该文件,发现 正常情况: ``` org.eclipse.team.svn.ui.decorator.SVNLightweightDecorator\:false ``` 异常情况: ``` org.eclipse.team.svn.ui.decorator.SVNLightweightDecorator\:true ``` 处理: 在org.eclipse.ui.workbench.prefs文件中搜索"**SVNLightweightDecorator**"将后面的true值该为**false**即可

深入探索 Java 热部署

作者:微信小助手

<section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;" data-mpa-powered-by="yiban.io"> <blockquote style="line-height: inherit;padding: 15px 15px 15px 1rem;font-size: 0.9em;margin-top: 1em;margin-bottom: 1em;color: rgb(129, 145, 152);border-left-width: 6px;border-left-color: rgb(220, 230, 240);background: rgb(242, 247, 251);overflow: auto;overflow-wrap: normal;"> <p style="font-size: inherit;color: inherit;line-height: inherit;">来源:https://www.ibm.com/developerworks/cn/java/j-lo-hotdeploy/<br>作者:丁志君</p> </blockquote> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">简介</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作。对于某些大型的应用来说,每次的重启都需要花费大量的时间成本。虽然 osgi 架构的出现,让模块重启成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克。本文将探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新。</p> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">类加载的探索</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">首先谈一下何为热部署(hotswap),热部署是在不重启 Java 虚拟机的前提下,能自动侦测到 class 文件的变化,更新运行时 class 的行为。Java 类是通过 Java 虚拟机加载的,某个类的 class 文件在被 classloader 加载后,会生成对应的 Class 对象,之后就可以创建该类的实例。默认的虚拟机行为只会在启动时加载类,如果后期有一个类需要更新的话,单纯替换编译的 class 文件,Java 虚拟机是不会更新正在运行的 class。如果要实现热部署,最根本的方式是修改虚拟机的源代码,改变 classloader 的加载行为,使虚拟机能监听 class 文件的更新,重新加载 class 文件,这样的行为破坏性很大,为后续的 JVM 升级埋下了一个大坑。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">另一种友好的方法是创建自己的 classloader 来加载需要监听的 class,这样就能控制类加载的时机,从而实现热部署。本文将具体探索如何实现这个方案。首先需要了解一下 Java 虚拟机现有的加载机制。目前的加载机制,称为双亲委派,系统在使用一个 classloader 来加载类时,会先询问当前 classloader 的父类是否有能力加载,如果父类无法实现加载操作,才会将任务下放到该 classloader 来加载。这种自上而下的加载方式的好处是,让每个 classloader 执行自己的加载任务,不会重复加载类。但是这种方式却使加载顺序非常难改变,让自定义 classloader 抢先加载需要监听改变的类成为了一个难题。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">不过我们可以换一个思路,虽然无法抢先加载该类,但是仍然可以用自定义 classloader 创建一个功能相同的类,让每次实例化的对象都指向这个新的类。当这个类的 class 文件发生改变的时候,再次创建一个更新的类,之后如果系统再次发出实例化请求,创建的对象讲指向这个全新的类。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">下面来简单列举一下需要做的工作。</p> <ul style="" class=" list-paddingleft-2"> <li><p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">创建自定义的 classloader,加载需要监听改变的类,在 class 文件发生改变的时候,重新加载该类。</p></li> <li><p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">改变创建对象的行为,使他们在创建时使用自定义 classloader 加载的 class。</p></li> </ul> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">自定义加载器的实现</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">自定义加载器仍然需要执行类加载的功能。这里却存在一个问题,同一个类加载器无法同时加载两个相同名称的类,由于不论类的结构如何发生变化,生成的类名不会变,而 classloader 只能在虚拟机停止前销毁已经加载的类,这样 classloader 就无法加载更新后的类了。这里有一个小技巧,让每次加载的类都保存成一个带有版本信息的 class,比如加载 Test.class 时,保存在内存中的类是 Test_v1.class,当类发生改变时,重新加载的类名是 Test_v2.class。但是真正执行加载 class 文件创建 class 的 defineClass 方法是一个 native 的方法,修改起来又变得很困难。所以面前还剩一条路,那就是直接修改编译生成的 class 文件。</p> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">利用 ASM 修改 class 文件</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">可以修改字节码的框架有很多,比如 ASM,CGLIB。本文使用的是 ASM。先来介绍一下 class 文件的结构,class 文件包含了以下几类信息,一个是类的基本信息,包含了访问权限信息,类名信息,父类信息,接口信息。第二个是类的变量信息。第三个是方法的信息。ASM 会先加载一个 class 文件,然后严格顺序读取类的各项信息,用户可以按照自己的意愿定义增强组件修改这些信息,最后输出成一个新的 class。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">首先看一下如何利用 ASM 修改类信息。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 1. 利用 ASM 修改字节码</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs javascript" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;">ClassWriter&nbsp;cw&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassWriter(ClassWriter.COMPUTE_MAXS);&nbsp;<br><br>ClassReader&nbsp;cr&nbsp;=&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;&nbsp;&nbsp;&nbsp;&nbsp;<br><br><span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">String</span>&nbsp;enhancedClassName&nbsp;=&nbsp;classSource.getEnhancedName();<br><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">try</span>&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;cr&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassReader(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;FileInputStream(<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classSource.getFile()));<br><br>}&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>}<br><br>ClassVisitor&nbsp;cv&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;EnhancedModifier(cw,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>),<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enhancedClassName.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>));<br><br>cr.accept(cv,&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>);<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">ASM 修改字节码文件的流程是一个责任链模式,首先使用一个 ClassReader 读入字节码,然后利用 ClassVisitor 做个性化的修改,最后利用 ClassWriter 输出修改后的字节码。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">之前提过,需要将读取的 class 文件的类名做一些修改,加载成一个全新名字的派生类。这里将之分为了 2 个步骤。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">第一步,先将原来的类变成接口。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 2. 重定义的原始类</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cs" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;">&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;Class&lt;?&gt;&nbsp;redefineClass(String&nbsp;className){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassWriter&nbsp;cw&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassWriter(ClassWriter.COMPUTE_MAXS);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassReader&nbsp;cr&nbsp;=&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassSource&nbsp;cs&nbsp;=&nbsp;classFiles.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">get</span>(className);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(cs==<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">try</span>&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cr&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassReader(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;FileInputStream(cs.getFile()));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassModifier&nbsp;cm&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassModifier(cw);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cr.accept(cm,&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">byte</span>[]&nbsp;code&nbsp;=&nbsp;cw.toByteArray();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;defineClass(className,&nbsp;code,&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>,&nbsp;code.length);<br><br>}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">首先 load 原始类的 class 文件,此处定义了一个增强组件 ClassModifier,作用是修改原始类的类型,将它转换成接口。原始类的所有方法逻辑都会被去掉。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">第二步,生成的派生类都实现这个接口,即原始类,并且复制原始类中的所有方法逻辑。之后如果该类需要更新,会生成一个新的派生类,也会实现这个接口。这样做的目的是不论如何修改,同一个 class 的派生类都有一个共同的接口,他们之间的转换变得对外不透明。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 3. 定义一个派生类</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cs" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;">&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;在&nbsp;class&nbsp;文件发生改变时重新定义这个类</span><br><br>&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;Class&lt;?&gt;&nbsp;redefineClass(String&nbsp;className,&nbsp;ClassSource&nbsp;classSource){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassWriter&nbsp;cw&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassWriter(ClassWriter.COMPUTE_MAXS);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassReader&nbsp;cr&nbsp;=&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classSource.update();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;enhancedClassName&nbsp;=&nbsp;classSource.getEnhancedName();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">try</span>&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cr&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ClassReader(<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;FileInputStream(classSource.getFile()));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EnhancedModifier&nbsp;em&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;EnhancedModifier(cw,&nbsp;className.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>),<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enhancedClassName.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExtendModifier&nbsp;exm&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ExtendModifier(em,&nbsp;className.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>),<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enhancedClassName.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cr.accept(exm,&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">byte</span>[]&nbsp;code&nbsp;=&nbsp;cw.toByteArray();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classSource.setByteCopy(code);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&lt;?&gt;&nbsp;clazz&nbsp;=&nbsp;defineClass(enhancedClassName,&nbsp;code,&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>,&nbsp;code.length);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classSource.setClassCopy(clazz);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;clazz;<br><br>}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">再次 load 原始类的 class 文件,此处定义了两个增强组件,一个是 EnhancedModifier,这个增强组件的作用是改变原有的类名。第二个增强组件是 ExtendModifier,这个增强组件的作用是改变原有类的父类,让这个修改后的派生类能够实现同一个原始类(此时原始类已经转成接口了)。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">自定义 classloader 还有一个作用是监听会发生改变的 class 文件,classloader 会管理一个定时器,定时依次扫描这些 class 文件是否改变。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">改变创建对象的行为<br>Java 虚拟机常见的创建对象的方法有两种,一种是静态创建,直接 new 一个对象,一种是动态创建,通过反射的方法,创建对象。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">由于已经在自定义加载器中更改了原有类的类型,把它从类改成了接口,所以这两种创建方法都无法成立。我们要做的是将实例化原始类的行为变成实例化派生类。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">对于第一种方法,需要做的是将静态创建,变为通过 classloader 获取 class,然后动态创建该对象。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 4. 替换后的指令集所对应的逻辑</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cpp" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;"><span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;原始逻辑&nbsp;&nbsp;</span><br><br>&nbsp;&nbsp;Greeter&nbsp;p&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;Greeter();<br><br><span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;改变后的逻辑</span><br><br>&nbsp;&nbsp;IGreeter&nbsp;p&nbsp;=&nbsp;(IGreeter)MyClassLoader.getInstance().<br><br>&nbsp;&nbsp;findClass(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"com.example.Greeter"</span>).newInstance();<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这里又需要用到 ASM 来修改 class 文件了。查找到所有 new 对象的语句,替换成通过 classloader 的形式来获取对象的形式。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 5. 利用 ASM 修改方法体</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs dart" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;"><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span><br><br>public&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;visitTypeInsn(<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">int</span>&nbsp;opcode,&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">String</span>&nbsp;type)&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(opcode==Opcodes.NEW&nbsp;&amp;&amp;&nbsp;type.equals(className)){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">List</span>&lt;LocalVariableNode&gt;&nbsp;variables&nbsp;=&nbsp;node.localVariables;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">String</span>&nbsp;compileType&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">for</span>(<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">int</span>&nbsp;i=<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>;i&lt;variables.size();i++){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalVariableNode&nbsp;localVariable&nbsp;=&nbsp;variables.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">get</span>(i);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compileType&nbsp;=&nbsp;formType(localVariable.desc);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(matchType(compileType)&amp;&amp;!valiableIndexUsed[i]){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valiableIndexUsed[i]&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">break</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mv.visitMethodInsn(Opcodes.INVOKESTATIC,&nbsp;CLASSLOAD_TYPE,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"getInstance"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"()L"</span>+CLASSLOAD_TYPE+<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">";"</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mv.visitLdcInsn(type.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,&nbsp;CLASSLOAD_TYPE,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"findClass"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"(Ljava/lang/String;)Ljava/lang/Class;"</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"java/lang/Class"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"newInstance"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"()Ljava/lang/Object;"</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mv.visitTypeInsn(Opcodes.CHECKCAST,&nbsp;compileType);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;flag&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">else</span>&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv.visitTypeInsn(opcode,&nbsp;type);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">对于第二种创建方法,需要通过修改 Class.forName()和 ClassLoader.findClass()的行为,使他们通过自定义加载器加载类。</p> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">使用 JavaAgent 拦截默认加载器的行为</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">之前实现的类加载器已经解决了热部署所需要的功能,可是 JVM 启动时,并不会用自定义的加载器加载 classpath 下的所有 class 文件,取而代之的是通过应用加载器去加载。如果在其之后用自定义加载器重新加载已经加载的 class,有可能会出现 LinkageError 的 exception。所以必须在应用启动之前,重新替换已经加载的 class。如果在 jdk1.4 之前,能使用的方法只有一种,改变 jdk 中 classloader 的加载行为,使它指向自定义加载器的加载行为。好在 jdk5.0 之后,我们有了另一种侵略性更小的办法,这就是 JavaAgent 方法,JavaAgent 可以在 JVM 启动之后,应用启动之前的短暂间隙,提供空间给用户做一些特殊行为。比较常见的应用,是利用 JavaAgent 做面向方面的编程,在方法间加入监控日志等。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">JavaAgent 的实现很容易,只要在一个类里面,定义一个 premain 的方法。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 6. 一个简单的 JavaAgent</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cpp" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">ReloadAgent</span>&nbsp;{</span><br><br>&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">premain</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(String&nbsp;agentArgs,&nbsp;Instrumentation&nbsp;inst)</span></span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GeneralTransformer&nbsp;trans&nbsp;=&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;GeneralTransformer();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inst.addTransformer(trans);<br><br>&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">然后编写一个 manifest 文件,将 Premain-Class属性设置成定义一个拥有 premain方法的类名即可。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">生成一个包含这个 manifest 文件的 jar 包。</p> <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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;"><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">manifest-Version</span>:&nbsp;1<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">.0</span><br><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">Premain-Class</span>:&nbsp;<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">com</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">.example</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">.ReloadAgent</span>&nbsp;<br><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">Can-Redefine-Classes</span>:&nbsp;<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">最后需要在执行应用的参数中增加 -javaagent参数 , 加入这个 jar。同时可以为 Javaagent增加参数,下图中的参数是测试代码中 test project 的绝对路径。这样在执行应用的之前,会优先执行 premain方法中的逻辑,并且预解析需要加载的 class。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">图 1. 增加执行参数</p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.458041958041958" data-s="300,640" data-type="png" data-w="572" src="/upload/82bcc695695ef409df5999c4f01f8ade.null" style=""></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这里利用 JavaAgent替换原始字节码,阻止原始字节码被 Java 虚拟机加载。只需要实现 一个 ClassFileTransformer的接口,利用这个实现类完成 class 替换的功能。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">清单 7. 替换 class</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cs" 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);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;overflow-wrap: normal !important;overflow: auto !important;">@Override<br><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">byte</span>&nbsp;[]&nbsp;transform(ClassLoader&nbsp;paramClassLoader,&nbsp;String&nbsp;paramString,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&lt;?&gt;&nbsp;paramClass,&nbsp;ProtectionDomain&nbsp;paramProtectionDomain,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">byte</span>&nbsp;[]&nbsp;paramArrayOfByte)&nbsp;throws&nbsp;IllegalClassFormatException&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;className&nbsp;=&nbsp;paramString.replace(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"."</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(className.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">equals</span>(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"com.example.Test"</span>)){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyClassLoader&nbsp;cl&nbsp;=&nbsp;MyClassLoader.getInstance();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl.defineReference(className,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"com.example.Greeter"</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;cl.getByteCode(className);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">else</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(className.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">equals</span>(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"com.example.Greeter"</span>)){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyClassLoader&nbsp;cl&nbsp;=&nbsp;MyClassLoader.getInstance();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl.redefineClass(className);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;cl.getByteCode(className);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br><br>&nbsp;}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">至此,所有的工作大功告成,欣赏一下 hotswap 的结果吧。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">图 2. Test 执行结果</p> <p style="text-align: center;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="256" data-ratio="0.21853146853146854" data-s="300,640" data-type="png" data-w="572" src="/upload/fee2052c78fb16fc19d58acc7c8ac5bf.null" style="width: 558px;height: 122px;"></p> <h1 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.6em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">结束语</span></h1> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">解决 hotswap 是个困难的课题,本文解决的仅仅是让新实例化的对象使用新的逻辑,并不能改变已经实例化对象的行为,如果 JVM 能够重新设计 class 的生命周期,支持运行时重新更新一个 class,hotswap 就会成为 Java 的一个闪亮新特性。官方的 JVM 一直没有解决热部署这个问题,可能也是由于无法完全克服其中的诸多难点,希望未来的 Jdk 能解决这个问题,让 Java 应用对于更新更友好,避免不断重启应用浪费的时间。</p> </section>

Java设计模式(转)——1.工厂方法模式

作者:じ☆ve宝贝

## 1.工厂方法模式(Factory Method) 工厂方法模式分为三种: ## 普通工厂模式 普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图: ![工厂设计模式](/upload/content8.png "工厂设计模式") 举例如下:(我们举一个发送邮件和短信的例子) 首先,创建二者的共同接口: ``` public interface Sender { public void Send(); } ``` 其次,创建实现类: ``` public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } ``` ``` public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is smssender!"); } } ``` 最后,建工厂类: ``` public class SendFactory { public Sender produce(String type) { if ("mail".equals(type)) { return new MailSender(); } else if ("sms".equals(type)) { return new SmsSender(); } else { System.out.println("请输入正确的类型!"); return null; } } } ``` 我们来测试下: ``` public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produce("sms"); sender.Send(); } } ``` 输出:this is smssender! ## 多个工厂方法模式 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图: ![工厂设计模式](/upload/content9.png "工厂设计模式") 将上面的代码做下修改,改动下SendFactory类就行,如下: ``` public class SendFactory { public Sender produceMail(){ return new MailSender(); } public Sender produceSms(){ return new SmsSender(); } } ``` 测试类如下: ``` public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produceMail(); sender.Send(); } } ``` 输出:this is mailsender! ## 静态工厂方法模式 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。 ``` public class SendFactory { public static Sender produceMail(){ return new MailSender(); } public static Sender produceSms(){ return new SmsSender(); } } ``` ``` public class FactoryTest { public static void main(String[] args) { Sender sender = SendFactory.produceMail(); sender.Send(); } } ``` 输出:this is mailsender! 总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

REST API安全设计指南

作者:じ☆ve宝贝

###### REST的全称是REpresentational State Transfer,它利用传统Web特点,提出一个既适于客户端应用又适于服务端的应用的、统一架构,极大程度上统一及简化了网站架构设计。 目前在三种主流的Web服务实现方案中,REST模式服务相比复杂的SOAP和XML-RPC对比来讲,更加简洁,越来越多的web服务开始使用REST设计并实现。但其缺少安全特性,《REST API 安全设计指南》就是一个REST API安全设计的指南,权当抛砖引玉,推荐网站后台设计及网站架构师们阅读。 ### 1、REST API 简介 REST的全称是REpresentational State Transfer,表示表述性无状态传输,无需session,所以每次请求都得带上身份认证信息。rest是基于http协议的,也是无状态的。只是一种架构方式,所以它的安全特性都需我们自己实现,没有现成的。建议所有的请求都通过https协议发送。RESTful web services概念的核心就是“资源”。资源可以用URI来表示。客户端使用HTTP协议定义的方法来发送请求到这些URIs,当然可能会导致这些被访问的”资源“状态的改变。HTTP请求对应关系如下: ``` ========== ===================== ======================== HTTP 方法 行为 示例 ========== ===================== ======================== GET 获取资源的信息 http://xx.com/api/orders GET 获取某个特定资源的信息 http://xx.com/api/orders/123 POST 创建新资源 http://xx.com/api/orders PUT 更新资源 http://xx.com/api/orders/123 DELETE 删除资源 http://xx.com/api/orders/123 ========== ====================== ====================== ``` 对于请求的数据一般用json或者xml形式来表示,推荐使用json。 ### 2、身份认证 身份认证包含很多种,有HTTP Basic、HTTP Digest、API KEY、Oauth、JWK等方式,下面简单讲解下: #### 2.1 HTTP Basic REST由于是无状态的传输,所以每一次请求都得带上身份认证信息,身份认证的方式,身份认证的方式有很多种,第一种便是http basic,这种方式在客户端要求简单,在服务端实现也非常简单,只需简单配置apache等web服务器即可实现,所以对于简单的服务来说还是挺方便的。但是这种方式安全性较低,就是简单的将用户名和密码base64编码放到header中。 ``` base64编码前:Basic admin:admin base64编码后:Basic YWRtaW46YWRtaW4= 放到Header中:Authorization: Basic YWRtaW46YWRtaW4= ``` 正是因为是简单的base64编码存储,切记切记在这种方式下一定得注意使用ssl,不然就是裸奔了。在某些产品中也是基于这种类似方式,只是没有使用apache的basic机制,而是自己写了认证框架,原理还是一样的,在一次请求中base64解码Authorization字段,再和认证信息做校验。很显然这种方式有问题,认证信息相当于明文传输,另外也没有防暴力破解功能。 #### 2.2 API KEY API Key就是经过用户身份认证之后服务端给客户端分配一个API Key,类似:http://example.com/api?key=dfkaj134,一般的处理流程如下: 一个简单的设计示例如下。 client端: ![rest接口](/upload/14453211382412.jpg "rest接口") server端: ![rest接口](/upload/144532116555321.jpg "rest接口") client端向服务端注册,服务端给客户端发送响应的api_key以及security_key,注意保存不要泄露,然后客户端根据api_key,secrity_key,timestrap,rest_uri采用hmacsha256算法得到一个hash值sign,构造途中的url发送给服务端。服务端收到该请求后,首先验证api_key,是否存在,存在则获取该api_key的security_key,接着验证timestrap是否超过时间限制,可依据系统成而定,这样就防止了部分重放攻击,途中的rest_api是从url获取的为/rest/v1/interface/eth0,最后计算sign值,完之后和url中的sign值做校验。这样的设计就防止了数据被篡改。通过这种API Key的设计方式加了时间戳防止了部分重放,加了校验,防止了数据被篡改,同时避免了传输用户名和密码,当然了也会有一定的开销。 #### 2.3 auth1.0a或者Oauth2 OAuth协议适用于为外部应用授权访问本站资源的情况。其中的加密机制与HTTP Digest身份认证相比,安全性更高。使用和配置都比较复杂,这里就不涉及了。 ####2.4 JWT JWT是JSON Web Token,用于发送可通过数字签名和认证的东西,它包含一个紧凑的、URL安全的JSON对象,服务端可通过解析该值来验证是否有操作权限,是否过期等安全性检查。由于其紧凑的特点,可放在url中或者HTTP Authorization头中,具体的算法就如下图: ![rest接口](/upload/14453212109244.jpg "rest接口") ###3、授权 身份认证之后就是授权,根据不同的身份,授予不同的访问权限。比如admin用户、普通用户、auditor用户都是不同的身份。简单的示例: ![rest接口](/upload/14453216104042.jpg "rest接口") 上述是垂直权限的处理,如果遇到了平行权限的问题,如用户A获取用户B的身份信息或者更改其他用户信息,对于这些敏感数据接口都需要加上对用户的判断,这一步一般都在具体的逻辑实现中实现。 ### 4、URL过滤 在进入逻辑处理之前,加入对URL的参数过滤,如: ``` /site/{num}/policy ``` 限定num位置为整数等,如果不是参数则直接返回非法参数,设定一个url清单,不在不在url清单中的请求直接拒绝,这样能防止开发中的api泄露。rest api接口一般会用到GET、POST、PUT、DELETE,未实现的方法则直接返回方法不允许,对于POST,PUT方法的数据采用json格式,并且在进入逻辑前验证是否json,不合法返回json格式错误。 ### 5、重要功能加密传输 第一步推荐SSL加密传输,同时对于系统中重要的功能做加密传输,如证书、一些数据、配置的备份功能,同时还得确保具备相应的权限,这一步会在授权中涉及。 ### 6、速率限制 请求速率限制,根据api_key或者用户来判断某段时间的请求次数,将该数据更新到内存数据库(redis,memcached),达到最大数即不接受该用户的请求,同时这样还可以利用到内存数据库key在特定时间自动过期的特性。在php中可以使用APC,Alternative PHP Cache (APC)是一个开放自由的PHP opcode缓存。它的目标是提供一个自由、开放,和健全的框架用于缓存和优化PHP的中间代码。在返回时设置X-Rate-Limit-Reset:当前时间段剩余秒数,APC的示例代码如下: ![rest接口](/upload/14453217216777.jpg "rest接口") ###7、错误处理 对于非法的,导致系统出错的等请求都进行记录,一些重要的操作,如登录、注册等都通过日志接口输出展示。有一个统一的出错接口,对于400系列和500系列的错误都有相应的错误码和相关消息提示,如401:未授权;403:已经鉴权,但是没有相应权限。如不识别的url: ``` {"result":"Invalid URL!"} ``` · 错误的请求参数 ``` {"result":"json format error"} ``` · 不允许的方法: ``` {"result":"Method Not Allowed"} ``` · 非法参数等。上面所说的都是单状态码,同时还有多状态码,表示部分成功,部分字符非法等。示例如下: ### 8、重要ID不透明处理 在系统一些敏感功能上,比如/user/1123可获取id=1123用户的信息,为了防止字典遍历攻击,可对id进行url62或者uuid处理,这样处理的id是唯一的,并且还是字符安全的。 9、其他注意事项 (1)请求数据,对于POST,DELETE方法中的数据都采用json格式,当然不是说rest架构不支持xml,由于xml太不好解析,对于大部分的应用json已经足够,近一些的趋势也是json越来越流行,并且json格式也不会有xml的一些安全问题,如xxe。使用json格式目前能防止扫描器自动扫描。 (2)返回数据统一编码格式,统一返回类型,如Content-Type: application/json; charset=”UTF-8″ (3)在逻辑实现中,json解码之后进行参数验证或者转义操作,第一步json格式验证,第二步具体参数验证基本上能防止大部分的注入问题了。 (4)在传输过程中,采用SSL保证传输安全。 (5)存储安全,重要信息加密存储,如认证信息hash保存。 总之,尽量使用SSL。

MySQL MySQL 5.7.9新的密码规则

作者:じ☆ve宝贝

安装的时候会生成随机密码,如果忘记可以修改MySQL的配置文件(linux默认为/etc/my.cnf,windows默认为my.ini),在[mysqld]下添加一行**skip-grant-tables **(跳过输入密码过程) 进入mysql后 ``` mysql> update mysql.user set authentication_string=password('root') where user='root' and Host = 'localhost'; mysql> flush privileges; mysql> quit; ``` 或者 ``` SET PASSWORD = PASSWORD(‘newpasswd‘); 设置新密码 ```

[MySQL错误]Table './web/t_tmp_book' is marked as crashed and should be repaired

作者:じ☆ve宝贝

Table './web/t_tmp_book' is marked as crashed and should be repaired 频繁查询和更新dede_archives表造成的索引错误 ` myisamchk -c -r ../data/mysql/shujukumingcheng/biaoming.MYI ` 然后myisamchk 工具会帮助你恢复数据表的索引。重新启动mysql,问题解决。

让Elasticsearch飞起来!百亿级实时查询优化实战

作者:微信小助手

<section style="box-sizing: border-box;font-size: 16px;"> <section 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;">最近的一个项目是风控过程数据实时统计分析和聚合的一个 OLAP 分析监控平台,日流量峰值在 10 到 12 亿上下,每年数据约 4000 亿条,占用空间大概 200T。</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="rich_pages" data-copyright="0" data-ratio="0.42825848849945236" data-s="300,640" src="/upload/6e55af3ebca20c4349ede186153a5886.png" data-type="png" data-w="913" style=""></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">面对这样一个数据量级的需求,我们的数据如何存储和实现实时查询将是一个严峻的挑战。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">经过对 Elasticsearch 多方调研和超过几百亿条数据的插入和聚合查询的验证之后,我们总结出以下几种能够有效提升性能和解决这一问题的方案:</span></p> <ul class=" list-paddingleft-2"> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群规划</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">存储策略</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">索引拆分</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">压缩</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">冷热分区等</span></strong></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本文所使用的 Elasticsearch 版本为 5.3.3。</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 27.2px;white-space: normal;text-align: center;"><img class="" data-copyright="0" data-ratio="0.4926719278466742" data-s="300,640" src="/upload/d769be36cf372ca0b7bdead40b334678.png" data-type="png" data-w="1774"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><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;">其主要特点体现在如下两个方面:</span></span></p> <ul class=" list-paddingleft-2"> <li><p style="margin-right: 8px;margin-left: 8px;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;">以时间为轴,数据只有增加,没有变更,并且必须包含 timestamp(日期时间,名称随意)字段。</span></p><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其作用和意义要大于数据的 id 字段,常见的数据比如我们通常要记录的操作日志、用户行为日志、或股市行情数据、服务器 CPU、内存、网络的使用率等。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">一定是以时间范围为第一过滤条件,然后是其他查询条件,比如近一天、一周、本月等等,然后在这个范围内进行二次过滤。</span></p><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如性别或地域等,查询结果中比较关注的是每条数据和 timestamp 字段具体发生的时间点,而非 id。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此类数据一般用于 OLAP、监控分析等场景。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">集群部署规划</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.6142857142857143" data-s="300,640" src="/upload/7083479511f4d22cf53f41bb1a7eba88.png" data-type="png" data-w="1260"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们都知道在 Elasticsearch(下称 ES)集群中有两个主要角色:Master Node 和 Data Node,其他如 Tribe Node 等节点可根据业务需要另行设立。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了让集群有更好的性能表现,我们应该对这两个角色有一个更好的规划,在 Nodes 之间做读取分离,保证集群的稳定性和快速响应,在大规模的数据存储和查询的压力之下能够坦然面对,各自愉快的协作。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Master Nodes</strong></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Master Node,整个集群的管理者,负有对 index 的管理、shards 的分配,以及整个集群拓扑信息的管理等功能。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">众所周知,Master Node 可以通过 Data Node 兼任,但是,如果对群集规模和稳定要求很高的话,就要职责分离,Master Node 推荐独立,它的状态关乎整个集群的存活。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">Master 的配置:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs kotlin" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">node.master:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">true</span>&nbsp;<br>node.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">data</span>:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">false</span>&nbsp;<br>node.ingest:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">false</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样 Master 不参与 I、O,从数据的搜索和索引操作中解脱出来,专门负责集群的管理工作,因此 Master Node 的节点配置可以相对低一些。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另外防止 ES 集群&nbsp;split brain(脑裂),合理配置 discovery.zen.minimum_master_nodes 参数,官方推荐 master-eligible nodes / 2 + 1 向下取整的个数。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这个参数决定选举 Master 的 Node 个数,太小容易发生“脑裂”,可能会出现多个 Master,太大 Master 将无法选举。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">更多 Master 选举相关内容请参考:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs ruby" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-symbol" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">https:</span>/<span class="hljs-regexp" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">/www.elastic.co/guide</span><span class="hljs-regexp" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">/en/elasticsearch</span><span class="hljs-regexp" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">/reference/</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;">5.3</span>/modules-discovery-zen.html<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">#master-election</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Data Nodes</strong></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Data Node 是数据的承载者,对索引的数据存储、查询、聚合等操作提供支持。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">这些操作严重消耗系统的 CPU、内存、IO 等资源,因此,应该把最好的资源分配给 Data Node,因为它们是真正干累活的角色,同样 Data Node 也不兼任 Master 的功能。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;line-height: 1.75em;color: rgb(71, 193, 168);">Data&nbsp;的配置:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs kotlin" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">node.master:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">false</span>&nbsp;<br>node.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">data</span>:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">true</span>&nbsp;<br>node.ingest:&nbsp;<span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">false</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Coordinating Only Nodes</strong></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;">&nbsp;</p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.7379032258064516" data-s="300,640" src="/upload/5c4b878d95bfe9f73c91ce99fbaf45f0.png" data-type="png" data-w="992"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 本身是一个分布式的计算集群,每个 Node 都可以响应用户的请求,包括 Master Node、Data Node,它们都有完整的 Cluster State 信息。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">正如我们知道的一样,在某个 Node 收到用户请求的时候,会将请求转发到集群中所有索引相关的 Node 上,之后将每个 Node 的计算结果合并后返回给请求方。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们暂且将这个 Node 称为查询节点,整个过程跟分布式数据库原理类似。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">那问题来了,这个查询节点如果在并发和数据量比较大的情况下,由于数据的聚合可能会让内存和网络出现瓶颈。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此,在职责分离指导思想的前提下,这些操作我们也应该从这些角色中剥离出来,官方称它是&nbsp;Coordinating Nodes,只负责路由用户的请求,包括读、写等操作,对内存、网络和 CPU 要求比较高。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本质上,Coordinating Only Nodes 可以笼统的理解为是一个负载均衡器,或者反向代理,只负责读,本身不写数据,</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">它的配置是:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><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;">node</span><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;">.master</span>:&nbsp;<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;">false</span>&nbsp;<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;">node</span><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;">.data</span>:&nbsp;<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;">false</span>&nbsp;<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;">node</span><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;">.ingest</span>:&nbsp;<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;">false</span>&nbsp;<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;">search</span><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;">.remote.connect</span>:&nbsp;<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;">false</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">增加 Coordinating Nodes 的数量可以提高 API 请求响应的性能,我们也可以针对不同量级的 Index 分配独立的 Coordinating Nodes 来满足请求性能。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">那是不是越多越好呢?在一定范围内是肯定的,但凡事有个度,过了负作用就会突显,太多的话会给集群增加负担。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在做 Master 选举的时候会先确保所有 Node 的 Cluster State 是一致的,同步的时候会等待每个 Node 的 Acknowledgement 确认,所以适量分配可以让集群畅快的工作。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">search.remote.connect 是禁用跨集群查询,防止在进行集群之间查询时发生二次路由:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs cpp" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">https:<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//www.elastic.co/guide/en/elasticsearch/reference/current/modules-cross-cluster-search.html</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Routing</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">类似于分布式数据库中的分片原则,将符合规则的数据存储到同一分片。ES 通过哈希算法来决定数据存储于哪个 Shard:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs ini" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-attr" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">shard_num</span>&nbsp;=&nbsp;hash(_routing)&nbsp;%&nbsp;num_primary_shards<br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中 hash(_routing) 得出一个数字,然后除以主 Shards 的数量得到一个余数,余数的范围是 0 到 number_of_primary_shards - 1,这个数字就是文档所在的 Shard。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Routing 默认是 id 值,当然可以自定义,合理指定 Routing 能够大幅提升查询效率,Routing 支持 GET、Delete、Update、Post、Put 等操作。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">如:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs vbscript" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">PUT&nbsp;my_index/my_type/<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>?routing=user1<br>{<br>&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"title"</span>:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"This&nbsp;is&nbsp;a&nbsp;document"</span><br>}<br><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">GET</span>&nbsp;my_index/my_type/<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>?routing=user1<br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">不指定 Routing 的查询过程:</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.7399813606710158" data-s="300,640" src="/upload/27ac3042d3436ebd679df1a7561bb0d.png" data-type="png" data-w="1073"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简单的来说,一个查询请求过来以后会查询每个 Shard,然后做结果聚合,总的时间大概就是所有 Shard 查询所消耗的时间之和。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">指定 Routing 以后:</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.36363636363636365" data-s="300,640" src="/upload/3e43c187addfbba7c190fb5dfc5c5283.png" data-type="png" data-w="2178"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">会根据 Routing 查询特定的一个或多个 Shard,这样就大大减少了查询时间,提高了查询效率。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当然,如何设置 Routing 是一个难点,需要一点技巧,要根据业务特点合理组合 Routing 的值,来划分 Shard 的存储,最终保持数据量相对均衡。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以组合几个维度做为 Routing ,有点类似于 Hbase Key,</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">例如不同的业务线加不同的类别,不同的城市和不同的类型等等,如:</span></p> <ul class=" list-paddingleft-2"> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">_search?routing=beijing:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按城市。</span></p></li> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">_search?routing=beijing_user123:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按城市和用户。</span></p></li> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">_search?routing=beijing_android,shanghai_android:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按城市和手机类型等。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据不均衡?</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">假如你的业务在北京、上海的数据远远大于其他二三线城市的数据。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">再例如我们的业务场景,A 业务线的数据量级远远大于 B 业务线,有时候很难通过 Routing 指定一个值保证数据在所有 Shards 上均匀分布,会让部分 Shard 变的越来越大,影响查询性能,怎么办?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一种解决办法是单独为这些数据量大的渠道创建独立的 Index,如:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs cpp" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">http:<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//localhost:9200/shanghai,beijing,other/_search?routing=android</span><br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样可以根据需要在不同 Index 之间查询,然而每个 Index 中 Shards 的数据可以做到相对均衡。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另一种办法是指定 Index 参数 index.routing_partition_size,来解决最终可能产生群集不均衡的问题,指定这个参数后新的算法如下:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">shard_num&nbsp;=&nbsp;(<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">hash</span>(_routing)&nbsp;+&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">hash</span>(_id)&nbsp;%&nbsp;routing_partition_size)&nbsp;%&nbsp;num_primary_shards<br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">index.routing_partition_size 应具有大于 1 且小于 index.number_of_shards 的值。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最终数据会在 routing_partition_size 几个 Shard 上均匀存储,是哪个 Shard 取决于 hash(_id) % routing_partition_size 的计算结果。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">指定参数 index.routing_partition_size 后,索引中的 Mappings 必须指定 _routing 为 "required": true,另外 Mappings 不支持 parent-child 父子关系。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">很多情况下,指定 Routing 后会大幅提升查询性能,毕竟查询的 Shard 只有那么几个,但是如何设置 Routing 是个难题,可根据业务特性巧妙组合。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">索引拆分</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Index 通过横向扩展 Shards 实现分布式存储,这样可以解决 Index 大数据存储的问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但在一个 Index 变的越来越大,单个 Shard 也越来越大,查询和存储的速度也越来越慢。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">更重要的是一个 Index 其实是有存储上限的(除非你设置足够多的 Shards 和机器),如官方声明单个 Shard 的文档数不能超过 20 亿(受限于 Lucene index,每个 Shard 是一个 Lucene index)。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">考虑到 I、O,针对 Index 每个 Node 的 Shards 数最好不超过 3 个,那面对这样一个庞大的 Index,我们是采用更多的 Shards,还是更多的 Index,我们如何选择?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Index 的 Shards 总量也不宜太多,更多的 Shards 会带来更多的 I、O 开销,其实答案就已经很明确,除非你能接受长时间的查询等待。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Index 拆分的思路很简单,时序索引有一个好处就是只有增加,没有变更,按时间累积,天然对索引的拆分友好支持,可以按照时间和数据量做任意时间段的拆分。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 提供的 Rollover Api + Index Template 可以非常便捷和友好的实现 Index 的拆分工作,把单个 index docs 数量控制在百亿内,也就是一个 Index 默认 5 个 Shards 左右即可,保证查询的即时响应。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简单介绍一下 Rollover API 和 Index Template 这两个东西,如何实现 index 的拆分。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Index Template</strong></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们知道 ES 可以为同一目的或同一类索引创建一个 Index Template,之后创建的索引只要符合匹配规则就会套用这个 Template,不必每次指定 Settings 和 Mappings 等属性。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一个 Index 可以被多个 Template 匹配,那 Settings 和 Mappings 就是多个 Template 合并后的结果。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">有冲突通过 Template 的属性"order" : 0 从低到高覆盖(这部分据说会在 ES6 中会做调整,更好的解决 Template 匹配冲突问题)。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">示例:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">PUT&nbsp;_template/template_1<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"index_patterns"</span>&nbsp;:&nbsp;[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"log-*"</span>],<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"order"</span>&nbsp;:&nbsp;0,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"settings"</span>&nbsp;:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"number_of_shards"</span>&nbsp;:&nbsp;5<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"aliases"</span>&nbsp;:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"alias1"</span>&nbsp;:&nbsp;{}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Rollover Index</strong></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.2978723404255319" data-s="300,640" src="/upload/89a688c359ce6d1c51ea43d03cfd5cdd.png" data-type="png" data-w="658"></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Rollover Index 可以将现有的索引通过一定的规则,如数据量和时间,索引的命名必须是 logs-000001 这种格式,并指定 aliases,示例:</span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">PUT&nbsp;/logs-000001&nbsp;<br>{<br>&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"aliases"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"logs_write"</span>:&nbsp;{}<br>&nbsp;&nbsp;}<br>}<br><br><span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">#&nbsp;Add&nbsp;&gt;&nbsp;1000&nbsp;documents&nbsp;to&nbsp;logs-000001</span><br><br>POST&nbsp;/logs_write/_rollover&nbsp;<br>{<br>&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"conditions"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"max_age"</span>:&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"7d"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"max_docs"</span>:&nbsp;&nbsp;1000<br>&nbsp;&nbsp;}<br>}<br></code></pre> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">先创建索引并指定别名 logs_write,插入 1000 条数据,然后请求 _rollover api 并指定拆分规则。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果索引中的数据大于规则中指定的数据量或者时间过时,新的索引将被创建,索引名称为 logs-000002,并根据规则套用 Index Template,同时别名 logs_write 也将被变更到 logs-000002。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">注意事项:</span></p> <ul class=" list-paddingleft-2"> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">索引命名规则必须如同:logs-000001。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">索引必须指定 aliases。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Rollover Index API 调用时才去检查索引是否超出指定规则,不会自动触发,需要手动调用,可以通过 Curator 实现自动化。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果符合条件会创建新的索引,老索引的数据不会发生变化,如果你已经插入 2000 条,拆分后还是 2000 条。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">插入数据时一定要用别名,否则你可能一直在往一个索引里追加数据。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><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;">按日期滚动索引:</span></span></p> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs vbscript" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;w

超详细的Elasticsearch高性能优化实践

作者:微信小助手

<section style="box-sizing: border-box;font-size: 16px;"> <section 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;">之前分享的一篇《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824132&amp;idx=1&amp;sn=632d25f325a8de625197b3e3e402b279&amp;chksm=bd74e4d38a036dc5968aca6af5061037c1103115f11cbc27baa992d5d1b5b27968e055e64c82&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">掌握它才说明你真正懂 Elasticsearch</a>》相信让大家对 ES 的原理都有所了解,这篇将从 ES 的 API 应用测试,性能优化,开发使用等方面展开,深入学习 ES。</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="rich_pages" data-copyright="0" data-ratio="0.5083870967741936" data-s="300,640" src="/upload/6b38a9e45d02a9823f02ff7d4d4ddd9f.png" data-type="png" data-w="775" style=""></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">ES 性能调优</p> </section> </section> </section> </section> <p style="white-space: normal;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;">ES 的默认配置,是综合了数据可靠性、写入速度、搜索实时性等因素。实际使用时,我们需要根据公司要求,进行偏向性的优化。</span></p> <p style="line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>写优化</strong></p> </section> </section> </section> </section> <p><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;">假设我们的应用场景要求是,每秒 300 万的写入速度,每条 500 字节左右。</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;"><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;">加大 Translog Flush ,目的是降低 Iops、Writeblock。</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;">增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数。</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;">调整 Bulk 线程池和队列。</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;">优化节点间的任务分布。</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;">优化 Lucene 层的索引建立,目的是降低 CPU 及 IO。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <h5 data-anchor-id="43ez" style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①批量提交</span></strong></h5> <p style="line-height: normal;"><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 提供了 Bulk API 支持批量操作,当我们有大量的写任务时,可以使用 Bulk 来进行批量写入。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">每次提交的数据量为多少时,能达到最优的性能,主要受到文件大小、网络情况、数据类型、集群状态等因素影响。&nbsp;</span></p> <p style="line-height: normal;"><br></p> <p style="margin-left: 8px;margin-right: 8px;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;">Bulk 默认设置批量提交的数据量不能超过 100M。数据条数一般是根据文档的大小和服务器性能而定的,但是单次批处理的数据大小应从 5MB~15MB 逐渐增加,当性能没有提升时,把这个数据量作为最大值。</span></p> <p style="line-height: normal;"><br></p> <p style="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;">我们可以跟着,感受一下 Bulk 接口,如下所示:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs php" 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);">$&nbsp;vi&nbsp;request<br>$&nbsp;cat&nbsp;request<br>{&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"index"</span>&nbsp;:&nbsp;{&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"_index"</span>&nbsp;:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"chandler"</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;">"_type"</span>:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"test"</span>,&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"_id"</span>&nbsp;:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"1"</span>&nbsp;}&nbsp;}<br>{&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"name"</span>&nbsp;:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"钱丁君"</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;">"age"</span>:&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"18"</span>&nbsp;}<br>$&nbsp;curl&nbsp;-s&nbsp;-H&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"Content-Type:&nbsp;application/json"</span>&nbsp;-XPOST&nbsp;localhost:<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">9200</span>/_bulk&nbsp;--data-binary&nbsp;@request;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">echo</span><br>{<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"took"</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;">214</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;">"errors"</span>:<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">false</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;">"items"</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;">"index"</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;">"_index"</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;">"chandler"</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;">"_type"</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;">"test"</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;">"_id"</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;">"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;">"_version"</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>,<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"result"</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;">"created"</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;">"_shards"</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;">"total"</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;">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;">"successful"</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>,<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"failed"</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;">0</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;">"_seq_no"</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;">0</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;">"_primary_term"</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>,<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"status"</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;">201</span>}}]}<br>$&nbsp;curl&nbsp;-XGET&nbsp;localhost:<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">9200</span>/chandler/test/<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(

理解MySQL锁和事务,看这篇如何?

作者:微信小助手

<section style="box-sizing: border-box;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;" powered-by="xiumi.us"> <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;" title="" opera-tn-ra-cell="_$.pages:0.layers:0.comps:0.txt1"> <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;">本文希望帮助读者更加深刻地理解 MySQL 中的锁和事务,从而在业务系统开发过程中更好地优化与数据库的交互。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.6277258566978193" data-s="300,640" src="/upload/611b6074f5cb5645f844d46e40206a4c.png" data-type="png" data-w="642" style=""> </section> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自 Pexels</em></span></p> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-size: 16px;"> <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;" powered-by="xiumi.us"> <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 style="box-sizing: border-box;">锁的分类及特性</p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问时变得有序所设计的一种规则。</span> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于任何一种数据库来说都需要有相应的锁定机制,所以 MySQL 自然也不能例外。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MySQL 数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">MySQL 各存储引擎使用了三种类型(级别)的锁定机制:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">表级锁定</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">行级锁定</span></strong></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">页级锁定</span></strong></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>表级锁定(table-level)</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">表级别的锁定是 MySQL 各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以获取锁和释放锁的速度很快。由于表级锁定一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用表级锁定的主要是 MyISAM,MEMORY,CSV 等一些非事务性存储引擎。  </span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>行级锁定(row-level)</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此外,行级锁定也最容易发生死锁。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">使用行级锁定的主要是 InnoDB 存储引擎。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>页级锁定(page-level)</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">页级锁定是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">使用页级锁定的主要是 BerkeleyDB 存储引擎。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">总的来说,MySQL 这三种锁的特性可大致归纳如下:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;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;">开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;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;">从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如 Web 应用。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。  </span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">表级锁定(MyISAM&nbsp;举例)</p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 MyISAM 存储引擎使用的锁定机制完全是由 MySQL 提供的表级锁定实现,所以下面我们将以 MyISAM 存储引擎作为示例存储引擎。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>MySQL 表级锁的锁模式</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">MySQL 的表级锁有两种模式:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">表共享读锁(Table Read Lock)</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">表独占写锁(Table Write Lock)</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;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=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对 MyISAM 表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">对 MyISAM 表的写操作,则会阻塞其他用户对同一表的读和写操作。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.75em;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MyISAM 表的读操作与写操作之间,以及写操作之间是串行的。当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;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;">表锁,读锁会阻塞写,不会阻塞读。而写锁则会把读写都阻塞。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>如何加表锁</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这个过程并不需要用户干预,因此,用户一般不需要直接用 LOCK TABLE 命令给 MyISAM 表显式加锁。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">显示加锁:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;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;">lock table tableName read</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">lock table tableName write</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">lock table t1 write,t2 read</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;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;">unlock tables</span><br></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" 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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>MyISAM 表锁优化建议</strong></p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于 MyISAM 存储引擎,虽然使用表级锁定在锁定实现的过程中比实现行级锁定或者页级锁定所带来的附加成本都要小,锁定本身所消耗的资源也是最少。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是由于锁定的颗粒度比较大,所以造成锁定资源的争用情况也会比其他的锁定级别都要多,从而在较大程度上会降低并发处理能力。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以,在优化 MyISAM 存储引擎锁定问题的时候,最关键的就是如何让其提高并发度。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于锁定级别是不可能改变的了,所以我们首先需要尽可能让锁定的时间变短,然后就是让可能并发进行的操作尽可能的并发。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①查询表级锁争用情况</span></strong></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MySQL 内部有两组专门的状态变量记录系统内部锁资源争用情况:</span> </section> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs ruby" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">mysql&gt;&nbsp;show&nbsp;status&nbsp;like&nbsp;<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">'table%'</span>;<br>+----------------------------+---------+<br><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;Value&nbsp;&nbsp;&nbsp;<span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+----------------------------+---------+<br>|</span>&nbsp;Table_locks_immediate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span><br><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;Table_locks_waited&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">11</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+----------------------------+---------+<br></span></code></pre> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这里有两个状态变量记录&nbsp;MySQL&nbsp;内部表级锁定的情况,</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">两个变量说明如下:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Table_locks_immediate:</span></strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">产生表级锁定的次数。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Table_locks_waited:</span></strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">出现表级锁定争用而发生等待的次数;此值越高则说明存在着越严重的表级锁争用情况。</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此外,MyISAM 的读写锁调度是写优先,这也是 MyISAM 不适合做写为主表的存储引擎的原因。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永久阻塞。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">两个状态值都是从系统启动后开始记录,出现一次对应的事件则数量加 1。如果这里的 Table_locks_waited 状态值比较高,那么说明系统中表级锁定争用现象比较严重,就需要进一步分析为什么会有较多的锁定资源争用了。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②缩短锁定时间</span></strong></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如何让锁定时间尽可能的短呢?</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">唯一的办法就是让我们的 Query 执行时间尽可能的短:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">尽量减少大的复杂 Query,将复杂 Query 分拆成几个小的 Query 分布进行。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">尽可能的建立足够高效的索引,让数据检索更迅速。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">尽量让 MyISAM 存储引擎的表只存放必要的信息,控制字段类型。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">利用合适的机会优化 MyISAM 表数据文件。</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③分离能并行的操作</span></strong></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">说到 MyISAM 的表锁,而且是读写互相阻塞的表锁,可能有些人会认为在 MyISAM 存储引擎的表上就只能是完全的串行化,没办法再并行了。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">大家不要忘记了,MyISAM 的存储引擎还有一个非常有用的特性,那就是 Concurrent&nbsp;Insert(并发插入)的特性。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MyISAM 存储引擎有一个控制是否打开 Concurrent Insert 功能的参数选项:concurrent_insert,可以设置为 0,1 或者 2。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">三个值的具体说明如下:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">concurrent_insert=2,</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">无论 MyISAM 表中有没有空洞,都允许在表尾并发插入记录。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">concurrent_insert=1,</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果 MyISAM 表中没有空洞(即表的中间没有被删除的行),MyISAM 允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是 MySQL 的默认设置。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">concurrent_insert=0,</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不允许并发插入。</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以利用 MyISAM 存储引擎的并发插入特性,来解决应用中对同一表查询和插入的锁争用。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例如,将 concurrent_insert 系统变量设为 2,总是允许并发插入;同时,通过定期在系统空闲时段执行 OPTIMIZE TABLE 语句来整理空间碎片,收回因删除记录而产生的中间空洞。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;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;"></span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MyISAM 存储引擎的读写是互相阻塞的,那么,一个进程请求某个 MyISAM 表的读锁,同时另一个进程也请求同一表的写锁,MySQL 如何处理呢?</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">答案是写进程先获得锁。不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这是因为 MySQL 的表级锁定对于读和写是有不同优先级设定的,默认情况下是写优先级要大于读优先级。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">所以,如果我们可以根据各自系统环境的差异决定读与写的优先级:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过执行命令 SET LOW_PRIORITY_UPDATES=1,使该连接读比写的优先级高。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果我们的系统是一个以读为主,可以设置此参数,如果以写为主,则不用设置。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过指定 INSERT、UPDATE、DELETE 语句的 LOW_PRIORITY 属性,降低该语句的优先级。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">虽然上面方法都是要么更新优先,要么查询优先的方法,但还是可以用其来解决查询相对重要的应用(如用户登录系统)中,读锁等待严重的问题。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另外,MySQL 也提供了一种折中的办法来调节读写冲突,即给系统参数 max_write_lock_count 设置一个合适的值,当一个表的读锁达到这个值后,MySQL 就暂时将写请求的优先级降低,给读进程一定获得锁的机会。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;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;">一些需要长时间运行的查询操作,也会使写进程“饿死”。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此,应用中应尽量避免出现长时间运行的查询操作,不要总想用一条 SELECT 语句来解决问题,因为这种看似巧妙的 SQL 语句,往往比较复杂,执行时间较长。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在可能的情况下可以通过使用中间表等措施对 SQL 语句做一定的“分解”,使每一步查询都能在较短时间完成,从而减少锁冲突。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果复杂查询不可避免,应尽量安排在数据库空闲时段执行,比如一些定期统计可以安排在夜间执行。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">InnoDB 默认采用行锁,在未使用索引字段查询时升级为表锁。MySQL 这样设计并不是给你挖坑。它有自己的设计目的。</span><br></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即便你在条件中使用了索引字段,MySQL 会根据自身的执行计划,考虑是否使用索引(所以 explain 命令中会有 possible_key 和 key)。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果 MySQL 认为全表扫描效率更高,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此,在分析锁冲突时,别忘了检查 SQL 的执行计划,以确认是否真正使用了索引。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">关于执行计划,第一种情况:</span><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">全表更新。</span></strong><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">事务需要更新大部分或全部数据,且表又比较大。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">若使用行锁,会导致事务执行效率低,从而可能造成其他事务长时间锁等待和更多的锁冲突。</span></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二种情况:</span></strong><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">多表级联。事务涉及多个表,比较复杂的关联查询,很可能引起死锁,造成大量事务回滚。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种情况若能一次性锁定事务涉及的表,从而可以避免死锁、减少数据库因事务回滚带来的开销。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">行级锁定</p> </section> </section> </section> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">行级锁定不是 MySQL 自己实现的锁定方式,而是由其他存储引擎自己所实现的,如广为大家所知的 InnoDB 存储引擎,以及 MySQL 的分布式存储引擎 NDB Cluster 等都是实现了行级锁定。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">考虑到行级锁定均由各个存储引擎自行实现,而且具体实现也各有差别,而 InnoDB 是目前事务型存储引擎中使用最为广泛的存储引擎,所以这里我们就主要分析一下 InnoDB 的锁定特性。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="transform: rotate(0deg);box-sizing: border-box;"> <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="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;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="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>InnoDB&nbsp;锁定模式及实现机制</strong></p> </section> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总的来说,InnoDB 的锁定机制和 Oracle 数据库有不少相似之处。InnoDB 的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB 也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当一个事务需要给自己需要的某个资源加锁的时候,如果遇到一个共享锁正锁定着自己需要的资源的时候,自己可以再加一个共享锁,不过不能加排他锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是,如果遇到自己需要锁定的资源已经被一个排他锁占有之后,则只能等待该锁定释放资源之后自己才能获取锁定资源并添加自己的锁定。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而意向锁的作用就是当一个事务在需要获取资源锁定的时候,如果遇到自己需要的资源已经被排他锁占用的时候,该事务需要在锁定行的表上面添加一个合适的意向锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果自己需要一个共享锁,那么就在表上面添加一个意向共享锁。而如果自己需要的是某行(或者某些行)上面添加一个排他锁的话,则先在表上面添加一个意向排他锁。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">所以,可以说 InnoDB 的锁定模式实际上可以分为四种:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">共享锁(S)</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">排他锁(X)</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">意向共享锁(IS)</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">意向排他锁(IX)</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"> <span style="line-height: 1.6;font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">我们可以通过以下表格来总结上面这四种锁的共存逻辑关系:</span> <span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span> </section> <section style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"> <img class="rich_pages" data-copyright="0" data-ratio="0.23801652892561984" data-s="300,640" src="/upload/4bddf04de72c9b961f5bb0555ee46a0e.png" data-type="png" data-w="605"> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果一个事务请求的锁模式与当前的锁兼容,InnoDB 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。</span></p> <section style="white-space: normal;line-height: normal;"> <br> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">意向锁是 InnoDB 自动加的,不需用户干预:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">对于 UPDATE、DELETE 和 INSERT 语句,InnoDB 会自动给涉及数据集加排他锁(X)。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于普通 SELECT 语句,InnoDB 不会加任何锁。</span></p></li> </ul> <section style="white-space: normal;line-height: normal;"> <br> </section> <section style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"> <span style="line-height: 1.6;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">事务可以通过以下语句显示给记录集加共享锁或排他锁:</span> </section> <section class="output_wrapper" style="letter-spacing: 0px;white-space: normal;font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs sql" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">共享锁(S):<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">SELECT</span>&nbsp;*&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">FROM</span>&nbsp;table_name&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">WHERE</span>&nbsp;...&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">LOCK</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">IN</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">SHARE</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">MODE</span><br>排他锁(X):<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;