作者:じ☆ve宝贝
如果您在创建实例时选择了数据盘,在登录实例后,系统需要先格式化数据盘,然后挂载数据盘。 **注意**:云服务器 ECS 仅支持对 数据盘 进行二次分区,而不支持对 系统盘 进行二次分区(不管是 Windows 还是 Linux 系统)。如果您强行使用第三方工具对系统盘进行二次分区操作,可能引发未知风险,如系统崩溃、数据丢失等。 1.使用远程连接工具,输入用户名 root 和密码登录到实例。 2.运行 fdisk -l 命令查看数据盘。注意:在没有分区和格式化数据盘之前,使用 df -h 命令是无法看到数据盘的。在下面的示例中,有一个 5 GB 的数据盘需要挂载。 如果执行了 fdisk -l 命令后,没有发现 /dev/xvdb,则表示您的实例没有数据盘  3.运行 fdisk /dev/xvdb,对数据盘进行分区。根据提示,依次输入 n,p,1,两次回车,wq,分区就开始了。  4.运行 fdisk -l 命令,查看新的分区。新分区 xvdb1 已经创建好。如下面示例中的/dev/xvdb1。  5.运行 mkfs.ext3 /dev/xvdb1,对新分区进行格式化。格式化所需时间取决于数据盘大小。您也可自主决定选用其他文件格式,如 ext14 等。  6.运行 echo ‘/dev/xvdb1 /mnt ext3 defaults 0 0’>> /etc/fstab 写入新分区信息。完成后,可以使用 cat /etc/fstab 命令查看。  统正确的命令是:echo ‘/dev/xvdb1 /mnt ext3 barrier=0 0 0’>>/etc/fstab如果需要把数据盘单独挂载到某个文件夹,比如单独用来存放网页,可以修改以上命令中的 /mnt 部分。 7.运行 mount /dev/xvdb1 /mnt 挂载新分区,然后执行 df -h 查看分区。如果出现数据盘信息,说明挂载成功,可以使用新分区了。 ``` mount /dev/xvdb1 /mnt df -h Filesystem Size Used Avail Use% Mounted on /dev/xvda1 40G 1.5G 36G 4% / tmpfs 498M 0 498M 0% /dev/shm /dev/xvdb1 5.0G 139M 4.6G 3% /mnt ```
作者:微信小助手
<p><span style="font-size: 16px;"><img data-backh="219" data-backw="395" data-before-oversubscription-url="http://mmbiz.qpic.cn/mmbiz_png/l89kosVutomBBhUdGiaFEc5TdZKIdF6G73bN9zTcvYbnqGDiaBQSfOuemS4nWLosEGdEIuYsFB8XNfRFyvCPSfUA/0?wx_fmt=png" data-ratio="0.5544303797468354" src="/upload/f7c758a039c89ce33b7bf85a93f204f1.png" data-type="png" data-w="395" style="width: 100%;height: auto;"></span></p> <h4 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 18px;text-align: center;white-space: normal;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;line-height: 2em;"><span style="font-size: 20px;color: rgb(61, 170, 214);"><strong>导读</strong></span></h4> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">最近面试,你又被volatile关键字虐了吗?这个问题,是不是问得有点扎心了!的确,有很多朋友反馈面试中在涉及考察Java并发编程知识的时候,经常会被问到volatile关键字。对于有些公司如果你能回答出volatile关键字的基本作用及原理,如:"<strong>volatile关键字可以实现线程间的可见性,之所以可以实现这一点,原因在于JVM会保证被volatile修饰的变量,在线程栈中被线程使用时都会主动从共享内存(堆内存/主内存)中以实时的方式同步一次;</strong><strong>另一方面,如果线程在工作内存中修改了volatile修饰的变量,也会被JVM要求立马刷新到共享内存中去。因此,即便某个线程修改了该变量,其他线程也可以立马感知到变化从而实现可见性</strong><strong>"</strong>也基本上能够pass这个问题。<span style="font-size: 15px;"></span></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">但是如果你去阿里这样喜欢刨根问底的公司面试的话,可能这点料就不够用了,因为面试官很可能会问到你更深层次的原理,如果没有彻底理解volatile关键字的话,在这个问题上迟早还是会栽跟头。因此,小码哥决定好好刨一下volatile关键字的根,希望能够对你起到帮助!</span></p> <h4 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 18px;text-align: center;white-space: normal;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;line-height: 2em;"><span style="font-size: 20px;color: rgb(61, 170, 214);"><strong>初识volatile</strong></span></h4> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">下面就让我们循序渐进地逐步了解volatile关键字吧!先了解下volatile的关键字都用在代码的什么地方:"<strong>volatile关键字只能修饰类变量和实例变量。</strong><strong>方法参数、局部变量、实例常量以及类常量都是不能用volatile关键字进行修饰的</strong>"。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><span style="color: rgb(255, 76, 0);">问题(1)</span>:“为什么volatile关键字只能修饰类变量和实例变量呢?”关于问题,我们可以先进行思考,然后在文章的结尾集中探讨答案。<br></span></p> <h3 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 20px;text-align: start;white-space: normal;line-height: 2em;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;"><span style="font-size: 18px;">机器硬件CPU&JAVA内存模型</span></h3> <p style="line-height: 2em;"><span style="font-size: 16px;">在深入理解volatile关键字之前,让我们先来回顾下<strong>并发问题产生的根本原因</strong>,这一点对于我们理解volatile关键字的存在意义是一个基础性问题。我们知道在计算机系统中所有的运算操作都是由CPU来完成的,而CPU运算需要从内存中加载数据,计算完后再将结果同步回内存,但是由于现代计算机的CPU的处理速度要比内存的访问速度牛逼N倍,如果CPU在进行数据运算时直接访问内存的话,由于内存的访问速度慢,这样就会拖慢CPU的运算效率。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">为了解决这一问题,伟大的计算机科学家们就想到了一个办法,通过在CPU和内存之间架设一层缓存,CPU不直接访问物理内存,而是将需要运算的数据从主内存中拷贝一份到缓存,运算的结果也通过缓存同步给主内存。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">通过这种方法CPU的运行速度就大大提高了,目前主流的CPU都有L1、L2、L3三级缓存。但是,这样的方式也带来了新的问题,那就是<strong>在多线程情况下同一份主内存中的数据值,会被拷贝多个副本放入CPU的缓存中,如果两个线程同时对一个变量值进行赋值操作的话,就会产生数据不一致的问题</strong>,例如:”变量i的初始值为0,两个线程同时加载到CPU缓存后,同时执行i+1的操作,按照道理说i的值此时应该是变成2,而实际情况主内存的值可能还是1,因为两个线程彼此是不知道对方已经改动了这个变量的值的“。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">而为了解决这样一个问题,一些CPU制造商如Intel开发了诸如<strong>MESI协议</strong>这样的<strong>缓存一致性控制协议</strong>来解决CPU缓存与主内存之间的数据不一致问题,其基本操作大概就是在某个线程通过CPU缓存写入主内存时,会通过信号的方式通知其他线程中CPU缓存中的值变为失效,从而让其他线程再次从主内存中同步一份数据到CPU缓存中。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">以上关于CPU缓存与内存的介绍,并不是为了探讨关于CPU的原理,而是为了说明并发数据不一致问题产生的基本缘由是什么!同理,JAVA内存模型中的定义中,也进行了类似的设计。在JAVA内存模型中,线程与主内存的关系是,线程并不直接操作主内存,而是通过将主内存中的变量拷贝到自己的工作内存中进行计算,完成后再将变量的值同步回主内存这样的方式进行工作。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">JAVA内存模型定义了线程与主内存之间的抽象关系,如下:</span></p> <ul style="list-style-type: disc;" class=" list-paddingleft-2"> <li><p style="line-height: 2em;"><span style="font-size: 16px;">共享变量(<strong>类变量以及对象的全局实例变量等都是共享变量</strong>)存储于主内存中,每个线程都可以访问,这里的主内存可以看成是堆内存。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">每个线程都有私有的工作内存,这里的工作内存可以看成是栈内存。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">工作内存只存储该线程对共享变量的副本。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">线程不能直接操作主内存,只有先操作了工作内存之后才能通过工作内存写入主内存。</span></p></li> </ul> <p style="line-height: 2em;"><span style="font-size: 16px;">以上关于工作内存及Java内存模型的概述,只是便于我们去理解JVM内存管理机制的一个抽象的概念,物理上并不是具体的存在。从具体情况上来讲因为Java程序是运行在JVM之上的,并没有直接调用到操作系统的底层接口去操作硬件,所以线程操作数据进行运算最终还是通过JVM调用了受操作系统管理的CPU资源去进行计算。而计算中涉及的CPU缓存与主内存的缓存一致性问题,则是操作系统层面的一层抽象,与Java工作内存彧主内存的划分并没有直接关系,它们是不同层次的设计。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">如果非要用一张图来进行下类比,以便于大家好理解的话,那就来一张图吧:</span></p> <p style="line-height: 2em;"><br><span style="font-size: 16px;"></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"></span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.43506493506493504" data-s="300,640" src="/upload/22d34a3a6c5c4a59bf49bfc2f79505ec.jpg" data-type="jpeg" data-w="770" style=""></p> <p style="text-align: center;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">根据图中的描述,Java内存模型的区分的堆、栈内存只是虚拟机对自身使用的物理内存的内部划分,它们对于操作系统管理来说就是一块被JVM使用的物理内存,而这个物理内存如果涉及CPU的运算操作,CPU就会通过硬件指令对数据进行加载运算,最终更改物理内存中相应程序变量所对应的内存区块的值。<br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"></span></p> <h3 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 20px;text-align: start;white-space: normal;line-height: 2em;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;"><span style="font-size: 18px;">并发编程三大特性</span></h3> <p style="line-height: 2em;"><span style="font-size: 16px;">volatile关键字说到底是Java中对多线程并发问题提供语法机制之一,而要正确地理解Java多线程问题,要求我们必须深刻的理解<strong>“原子性”、“有序性”、“可见性”</strong>这三个非常重要和关键的特性。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="color: rgb(0, 128, 255);font-size: 17px;"><strong>原子性</strong></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"></span><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">所谓原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到执行,要么所有的操作都不执行。</span><span style="font-size: 16px;">这个<strong>原子性与数据库事务的原子性特性是一致的</strong>。</span><span style="font-size: 16px;">Java内存模型只保证了基本的读取和赋值的原子性操作,其他操作均不保证。</span><br></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">简单操作如:</span><span style="font-size: 16px;">"<strong>x=10</strong>",这个操作就是原子性的。</span><span style="font-size: 16px;">因为从操作上看执行线程会将x=10这个动作写入工作内存,然后再将其写入主内存;</span><span style="font-size: 16px;">即便在该线程进行数值刷新的过程中,也有其他线程对其进行刷新操作(如x=11)的情况x的最终结果也没有什么不一致的问题,因为最后要么是10,要么是11,两个线程谁先刷新都无所谓,那么在这种情况下我们就是这个操作是原子性的。</span><br></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">其他操作如:</span><span style="font-size: 16px;"><strong>"x++"</strong>,这个操作就是非原子性的。</span><span style="font-size: 16px;">为啥呢?</span><span style="font-size: 16px;">我们来分析下x++这个动作都经过了哪些步骤:</span><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.609984399375975" data-s="300,640" src="/upload/b57564046817f80c9b5494e6c310050c.jpg" data-type="jpeg" data-w="641" style=""></p> <p style="line-height: 2em;"><span style="font-size: 16px;"></span><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">此时y的正确值应该是6,但是线程A最终将y=2同步给了主内存,从而导致主内存中y的值变成了一个脏数据,从而产生了线程安全问题,所以我们说y++的操作不具备原子性,因为它分了三个步骤来执行一个操作。</span></p> <p style="line-height: 2em;"><br><span style="font-size: 16px;"></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">从上面的例子可以看到<strong>原子性是一个排他性的特性</strong>,如果需要保证y++具备原子性就需要确保y++动作的三个步骤完成前,不允许其他线程对y变量进行操作。</span><span style="font-size: 16px;">因为Java的内存模型并不确保此类操作的原子性,所以有时候写Java代码时会让人感到代码中处处都充斥着线程不安全的操作,只是现在觉得多数程序员都在从事业务开发,面向的都是数据库编程,加上工程上都是集成了现成的开发框架,所以对于这一点感受并不是特别深刻,只有在少数场景下手动实现多线程编程时才会通过<strong>synchronized</strong>关键字进行加锁同步操作。</span><br></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">然而,这个特性与volatile关键字有什么关系呢?<strong>事实上volatile关键字并不保证被修饰的类变量和实例变量具有原子性。</strong>这是因为被volatile关键字修饰的变量并不具备排他性,关于这一点,我们在下面说完另外两个特性后再分析下原因。</span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="color: rgb(0, 128, 255);font-size: 17px;"><strong>可见性</strong></span></p> <p style="line-height: 2em;"><br><span style="color: rgb(0, 128, 255);"><strong><span style="font-size: 16px;"></span></strong></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">可见性是指,<strong>当一个线程对共享变量进行了修改,那么其他线程可以立刻看到修改后的最新值</strong>。在Java多线程环境下,线程首次读取要操作的变量时,是先到主内存中获取该变量,然后将其放入工作内存,以后关于该变量的操作都是在以工作内存中的变量值为基准的。之后如果要修改该变量的值,也是直接修改工作内存中的变量,最后会在某一时刻将工作内存中该变量的值刷新同步回主内存,之后其他线程就能感知到该变量的变化,实现可见性了!<br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 16px;">只是<strong>什么时候将工作内存中的值同步会主内存,这个时间点在自然情况下是不确定的</strong>,所以假设线程A修改了变量的值之后,在正式将其同步会主内存之前,线程B获取了主内存中变量的原先值,而过了一会后线程A刷新了主内存,但是此时主内存中的变量值与线程B工作内存中的变量值已经不一致了,这个时候就出现不可见的问题了!</span><br></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">在Java中本文的主角<strong>volatile关键字就可以解决变量在线程间不可见的问题</strong>。当一个变量被volatile关键字修饰之后,对于共享资源的操作会时刻保持与主内存的数据一致。因为被volatile关键字修饰的变量,如果某个线程对其进行了更改,它就会立马进行一次工作内存刷新同步至主内存的操作;同理,如果某个线程读取volatile关键字修饰的变量,那么该线程返回自己工作内存中的变量时,每次都会被要求从主内存再同步一次到工作内存中。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><strong><span style="font-size: 16px;">除此之外synchronized关键字以及JUC包中提供的显示锁Lock也可以保证可见性。</span></strong><span style="font-size: 16px;">原因在于它们可以保证在同一时刻只有一个线程获得锁可以操作共享变量,完成工作内存中的运算在释放锁之前会确保工作内存中变更的变量新值会被刷新到主内存中。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;"><strong>我们再回过头来分析下volatile关键字修饰的变量为什么在保证可见性以后还是不能确保原子性,实现完全的线程安全呢?</strong></span></p> <p style="line-height: 2em;"><br><span style="font-size: 16px;"></span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.609984399375975" data-s="300,640" src="/upload/c9e25abce97f675fa5886bcaadef1b2e.jpg" data-type="jpeg" data-w="641" style=""></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">我们还是以y++举例,这次变量y被volatile修饰了,有什么变化呢?假设<strong>第1步</strong>线程A从主内存拷贝了y的副本到工作内存后,此时线程B直接操作了y=5这个动作,那么此时线程A中的副本y的值为1就不对了,因为被volatile修饰了,所以在<strong>第2步</strong>线程A使用y进行运算时,会再次从主内存中同步一次y的副本(y=5),然后线程A执行y=y+1后,会立马执行<strong>第3步</strong>把y的值6立马同步刷新会主内存。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">初一看感觉volatile的关键字好像解决了y++这个操作的原子性问题,但实际上我们再看看,如果此时线程A已经执行完第2步了,此时线程B更改了变量y的值,虽然此时线程A知道变量y发生了变化,但是由于操作已经执行完,所以还是只能执行第3步把变量y的值覆盖回主内存,从而又造成了错误数据。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">所以从这个例子分析,volatile只是解决了y++第1步和第2步的原子性,并没有解决3个步骤的原子性,所以我们说volatile关键字并不能保证解决原子性问题,就是这个道理!</span><br><span style="font-size: 16px;"></span></p> <p style="line-height: 2em;"><br><span style="font-size: 16px;"></span></p> <p style="line-height: 2em;"><span style="color: rgb(0, 128, 255);font-size: 17px;"><strong>有序性</strong></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">有序性是指程序代码在执行的过程中要确保有数据依赖关系的代码要有先后顺序。由于代码编译存在<strong>指令重排</strong>的问题,所以程序编写的顺序与最后实际执行的指令可能先后顺序时错乱的,如果代码编写的先后顺序存在数据依赖关系,那么就有可能导致依赖于某条代码指令在它所依赖的代码指令执行之前就被执行了,从而导致程序出现错误的情况。<br></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">在Java中的有序性就是要通过对指令重排的干预,避免掉因为指令重排导致的这种程序错误问题。<strong>volatile关键字</strong>就可以通过增加内存屏障的方式禁止指令重排,从而实现程序执行的有序性。除此之外的<strong>synchronized关键字以及JUC包中提供的显示锁Lock也可以保证有序性,</strong>因为同步所以与单线程的情况一样自然能够保证有序性。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">此外,Java内存模型本身也会通过一些<strong>happens-before原则</strong>的推导来确保在进行指令重排时程序代码执行的有序性。这里的happens-before原则有:程序次序规则、锁定规则、volatile变量规则、传递规则、线程启动规则、线程中断规则、线程终结规则、对象的终结规则等。</span></p> <h4 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 18px;text-align: center;white-space: normal;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;line-height: 2em;"><span style="font-size: 20px;color: rgb(61, 170, 214);"><strong>volatile实现机制</strong></span></h4> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">通过上面内容的阅读,详细你对volatile关键字已经有了一定深入的了解了,下面我们再深入分析下volatile的实现机制。通过对OpenJDK中的unsafe.cpp源码的分析,会发现被volatile关键字修饰的变量会存在一个“lock:”的前缀。</span><br><span style="font-size: 16px;"></span><img class="" data-croporisrc="/upload/81c49420bb1a0287c96659bdfc579262.png" data-cropx1="0" data-cropx2="1360" data-cropy1="0" data-cropy2="494.1007194244604" data-ratio="0.36171875" src="https://mmbiz.qpic.cn/mmbiz_jpg/l89kosVutomAA3ia3nYtmWo986mnrAwu6H7a4atEGcasVTmJRef5YiaZEHNBKxdey1sZicxyFdfb5Ek4pyvWpUn7w/640?wx_fmt=jpeg" data-type="jpeg" data-w="1280" style="width: 556px;height: 202px;"></p> <p style="line-height: 2em;"><span style="font-size: 16px;">这个实际上相当于是一个内存屏障,该内存屏障会为指令的执行提供如下保障:</span></p> <ul style="list-style-type: square;" class=" list-paddingleft-2"> <li><p style="line-height: 2em;"><span style="font-size: 16px;">确保指令重排序时不会将其后面的代码排到内存屏障之前。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">同样也会确保重排序是不会将其前面的代码排到内存屏障之后。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">确保在执行到内存屏障修饰的指令时前面的代码全部执行完成。</span></p></li> <li><p style="line-height: 2em;"><span style="font-size: 16px;">强制将线程的工作内存中值的修改刷新至主内存中。</span></p></li> </ul> <h4 style="box-sizing: border-box;margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-size: 18px;text-align: center;white-space: normal;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;line-height: 2em;"><span style="font-size: 20px;color: rgb(61, 170, 214);"><strong>思考题</strong></span></h4> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">以上就是关于volatile关键字的全部要说的内容了!在结束之前,我们先来看看之前文章中提到的一个问题:为什么volatile关键字只能修饰类变量和实例变量?“<strong>关于这个问题类似于就是要回答Java语言的语法设计问题了。</strong><strong>因为volatile关键字是要解决多线程间共享变量的可见性问题的,只有类变量及实例变量才是Java中的共享变量的类型,方法参数因为时线程私有不存在共享的问题,而常量本身的值是固定的所以不需要被volatile这样的机制修饰”。</strong></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">再给大家留一个问题:“<strong>如果volatile的修饰的是一个引用类型的对象变量,那么对象中的定义的一些变量及方法会收到什么影响呢</strong>”?</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">大家可以先思考下,欢迎发消息留言及加微信入群一起讨论!</span><br><span style="font-size: 16px;"></span></p> <p style="line-height: 2em;"><strong><span style="font-size: 16px;"></span></strong><br></p> <p><span style="font-size: 16px;">推荐阅读:</span></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247484751&idx=1&sn=834ba56bcffa7d3575aef32a802dcc30&chksm=fd2fd48dca585d9b61931908dcfa0a5141b6cd9d3e9de9d45b1b85ff81c2cd07ac30168acda8&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 15px;">线程池的设计原理是什么?</span></a></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247484781&idx=1&sn=37410495074f5700b99ae2d93ac66b94&chksm=fd2fd4afca585db94c1c5f15ecff72f8c767af3ae3253f108c9f6564d3ebec7a0191bfad1be7&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 15px;">谁要是再问你单例模式,那就抛给他这7种写法吧!</span></a></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247484247&idx=1&sn=911fa9ba564548d6aea06e1c5ff55a84&chksm=fd2fd295ca585b83f1d674734299464e34689c60e131574708fb89e4c676549eaa5e8a0c5df9&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 15px;">一张图看懂JVM之类装载系统</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247484016&idx=1&sn=d1e1ccef1760a5567d3d0709d3986c35&chksm=fd2fd3b2ca585aa49d636ad369f1cc2206cd87ece94d056d1784e49bcdb7e94bfd0e2b307d7c&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 15px;">一张图看懂JVM之垃圾回收器详解</span></a></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247483820&idx=1&sn=8418f0f6a618bb0f0ca0980af09a816f&chksm=fd2fd06eca5859786ab124dd204a7ec9b1ad3ed230b9b531086cc6729a277a05d3e8307b7e0d&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"><span style="text-decoration: underline;font-size: 15px;">一张图看懂JVM(升级版)</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MzU3NDY4NzQwNQ==&mid=2247484781&idx=1&sn=37410495074f5700b99ae2d93ac66b94&chksm=fd2fd4afca585db94c1c5f15ecff72f8c767af3ae3253f108c9f6564d3ebec7a0191bfad1be7&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="text-decoration: underline;" data-linktype="2"></a></p> <p><br></p> <p style="padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;white-space: normal;letter-spacing: 2px;text-align: center;background-color: rgb(254, 254, 254);color: rgb(62, 62, 62);font-size: 16px;list-style-type: none;line-height: 25.6px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><img class="__bg_gif" data-ratio="1" data-type="gif" data-w="19" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 19px !important;height: auto !important;" src="/upload/dd153ab34eb13de7d968f500ef94e526.null" width="19px"></p> <p class="" style="padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;white-space: normal;letter-spacing: 2px;text-align: center;background-color: rgb(254, 254, 254);color: rgb(62, 62, 62);font-size: 16px;list-style-type: none;line-height: 25.6px;box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;text-align: center;background-color: rgb(254, 254, 254);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;letter-spacing: 0.544px;color: rgb(137, 135, 145);font-size: 16px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">—————END—————</span></p> <p class="" style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;text-align: center;background-color: rgb(254, 254, 254);box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;text-indent: 0em;white-space: normal;text-align: center;background-color: rgb(254, 254, 254);word-spacing: 2px;font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;line-height: 25.6px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><img class="" data-copyright="0" data-ratio="1" data-s="300,640" data-type="jpeg" data-w="258" style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-indent: 0em;border-radius: 4px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 258px !important;height: auto !important;" src="/upload/b5b5c2f05535c1a82d6645d73092e98f.jpg"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p style="margin-top: 15px;max-width: 100%;min-height: 1em;font-variant-numeric: normal;font-variant-east-asian: normal;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;font-size: 16px;line-height: 1.75em;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(127, 127, 127);line-height: 1.75em;">识别图片二维码,关注“</span><span style="max-width: 100%;line-height: 1.75em;color: rgb(0, 176, 240);">无敌码农</span><span style="max-width: 100%;color: rgb(127, 127, 127);line-height: 1.75em;">”获取精彩内容</span></span></p>
作者:じ☆ve宝贝
## 4.建造者模式(Builder) 工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码: 还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下: ``` public class Builder { private List<Sender> list = new ArrayList<Sender>(); public void produceMailSender(int count){ for(int i=0; i<count; i++){ list.add(new MailSender()); } } public void produceSmsSender(int count){ for(int i=0; i<count; i++){ list.add(new SmsSender()); } } } ``` 测试类: ``` public class Test { public static void main(String[] args) { Builder builder = new Builder(); builder.produceMailSender(10); } } ``` 从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。
作者:じ☆ve宝贝
humbnailator 是一个用来生成图像缩略图的 Java 类库,通过很简单的代码即可生成图片缩略图,也可直接对一整个目录的图片生成缩略图。 支持:图片缩放,区域裁剪,水印,旋转,保持比例。 示例代码: ``` Thumbnails.of(new File("original.jpg")) .size(160, 160) .rotate(90) .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f) .outputQuality(0.8f) .toFile(new File("image-with-watermark.jpg")); ``` gitHub地址 ` https://github.com/coobird/thumbnailator `
作者:仲夏时节的梦想
Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了, log4j.rootLogger=DEBUG,CONSOLE,A1,im log4j.addivity.org.apache=true # 应用于控制台 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.Threshold=DEBUG log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n #log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n #应用于文件 log4j.appender.FILE=org.apache.log4j.FileAppender log4j.appender.FILE.File=file.log log4j.appender.FILE.Append=false log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n # Use this layout for LogFactor 5 analysis # 应用于文件回滚 log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=10KB log4j.appender.ROLLING_FILE.MaxBackupIndex=1 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n #应用于socket log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender log4j.appender.SOCKET.RemoteHost=localhost log4j.appender.SOCKET.Port=5001 log4j.appender.SOCKET.LocationInfo=true # Set up for Log Facter 5 log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n # Log Factor 5 Appender log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000 # 发送日志给邮件 log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender log4j.appender.MAIL.Threshold=FATAL log4j.appender.MAIL.BufferSize=10 log4j.appender.MAIL.From=kengking@163.com log4j.appender.MAIL.SMTPHost=smtp.163.com log4j.appender.MAIL.Subject=Log4J Message log4j.appender.MAIL.To=kengking@163.com log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n # 用于数据库 log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver log4j.appender.DATABASE.user=root log4j.appender.DATABASE.password= log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n') log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=SampleMessages.log4j log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout #自定义Appender log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender log4j.appender.im.host = mail.cybercorlin.net log4j.appender.im.username = username log4j.appender.im.password = password log4j.appender.im.recipient = corlin@cybercorlin.net log4j.appender.im.layout=org.apache.log4j.PatternLayout log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
作者:じ☆ve宝贝
### 1.准备jar包 https://github.com/mybatis/generator/releases ### 2.连接数据库的jar包 mysql-connector-java-5.1.6-bin.jar ### 3.编辑generatorConfig.xml ``` <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <classPathEntry location="mysql-connector-java-5.1.12-bin.jar" /> <context id="studyjava" targetRuntime="MyBatis3"> <!-- xml文件是否在原有基础上合并,默认是false --> <property name="mergeable" value="false"></property> <plugin type="org.mybatis.generator.plugins.MysqlPagePlugin" /> <!-- generate entity时,生成hashcode和equals方法 --> <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" /> <!-- generate entity时,生成serialVersionUID --> <plugin type="org.mybatis.generator.plugins.SerializablePlugin" /> <!-- 这个插件只会增加字符串字段映射到一个JDBC字符的方法 --> <plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin" /> <!-- genenat entity时,生成toString --> <plugin type="org.mybatis.generator.plugins.ToStringPlugin" /> <!-- 抑制生成代码的注释 --> <commentGenerator> <property name="suppressAllComments" value="true" /> </commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://192.168.10.20:3306/studyjava" userId="root" password="root"> </jdbcConnection> <javaModelGenerator targetPackage="cn.studyjava.model" targetProject="../../project" > <!-- 去除model中String的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="cn.studyjava.mapper" targetProject="../../project" /> <javaClientGenerator type="XMLMAPPER" targetPackage="cn.studyjava.mapper" targetProject="../../project" /> <table schema="" tableName="wm_admin"> <property name="useActualColumnNames" value="true" /> </table> <!-- 返回主键id --> <!-- <table schema="" tableName="cia_app_charge"> <property name="useActualColumnNames" value="true" /> <generatedKey column="id" sqlStatement="MySql" identity="true" /> </table> --> </context> </generatorConfiguration> ``` 4.ant执行脚本build.xml ``` <project default="genfiles" basedir="."> <property name="generated.source.dir" value="${basedir}" /> <target name="genfiles" description="Generate the files"> <taskdef name="mbgenerator" classname="org.mybatis.generator.ant.GeneratorAntTask" classpath="mybatis-generator-core-1.3.2.jar" /> <mbgenerator overwrite="true" configfile="generatorConfig.xml" verbose="false"> <propertyset> <propertyref name="generated.source.dir" /> </propertyset> </mbgenerator> </target> </project> ``` 到此为止,大功告成!!! ## 生成的Example类使用说明 Example类指定如何构建一个动态的where子句. 表中的每个non-BLOB列可以被包括在where子句中. 例子是展示此类用法的最好方式. Example类可以用来生成一个几乎无限的where子句. Example类包含一个内部静态类 Criteria 包含一个用 anded 组合在where子句中的条件列表. Example类包含一个 List<Criteria> 属性,所有内部类Criteria中的子句会用 ored组合在一起. 使用不同属性的 Criteria 类允许您生成无限类型的where子句. 创建 Criteria 对象 可以使用Example类中的 createCriteria() 或者 or() . 如果 Criteria 对象是用 createCriteria() 创建的,它会自动为 List<Criteria> 属性添加一个 Criteria 对象 - 这使得它更容易写一个简单的where子句, 如果您不需要 or 或者其他几个子句组合的话. 用 or(Criteria criteria) 方法创建 Criteria 对象, 方法里的 criteria 对象会被添加进 Criteria 对象的列表中. **重要** 我们推荐您只使用 or() 方法创建 Criteria 对象. 我们相信这种方法使代码更有可读性. ### 简单查询 这个例子展示了如何用生成后的Example类去生成一个简单的where子句: ``` TestTableExample example = new TestTableExample(); example.createCriteria().andField1EqualTo(5); ``` 作为另一种选择, 下面的方式也是可以的: ``` TestTableExample example = new TestTableExample(); example.or().andField1EqualTo(5); ``` 在上面的例子中, 动态生成的where子句是: ``` where field1 = 5 ``` ###复杂查询 下面的例子展示了如何用生成后的Example类去生成一个复杂的where子句 (用到了 JSE 5.0 的泛型): ``` TestTableExample example = new TestTableExample(); example.or() .andField1EqualTo(5) .andField2IsNull(); example.or() .andField3NotEqualTo(9) .andField4IsNotNull(); List<Integer> field5Values = new ArrayList<Integer>(); field5Values.add(8); field5Values.add(11); field5Values.add(14); field5Values.add(22); example.or() .andField5In(field5Values); example.or() .andField6Between(3, 7); ``` 在上面的例子中, 动态生成的where子句是: ``` where (field1 = 5 and field2 is null) or (field3 <> 9 and field4 is not null) or (field5 in (8, 11, 14, 22)) or (field6 between 3 and 7) ``` 将会返回满足这些条件的记录结果. ###去重复查询 您可以在所有的Example类中调用 **setDistinct(true)** 方法进行强制去重复查询. ###Criteria类 Criteria 内部类的每个属性都包含 andXXX 方法,以及如下的标准的SQL查询方法: **IS NULL** - 指相关的列必须为NULL **IS NOT NULL** - 指相关的列必须不为NULL **= (equal)** - 指相关的列必须等于方法参数中的值 **<> (not equal)** - 指相关的列必须不等于方法参数中的值 **> (greater than)** - 指相关的列必须大于方法参数中的值 **>= (greater than or equal)** - 指相关的列必须大于等于方法参数中的值 < (less than) - 指相关的列必须小于于方法参数中的值 **<= (less than or equal)** - 指相关的列必须小于等于方法参数中的值 **LIKE** - 指相关的列必须 "like" 方法参数中的值. 这个方法不用必须加入 '%', 您必须设置方法参数中的值. **NOT LIKE** - 指相关的列必须 "not like" 方法参数中的值. 这个方法不用必须加入 '%', 您必须设置方法参数中的值. **BETWEEN** - 指相关的列必须在 "between" 方法参数中的两个值之间. **NOT BETWEEN** - 指相关的列必须不在 "not between" 方法参数中的两个值之间. **IN** - 指相关的列必须在传入的方法参数的list中. **NOT IN** - 指相关的列必须不在传入的方法参数的list中.
作者:微信小助手
<p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 0, 0);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(点击</span><span style="max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">上方公众号</span><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">,可快速关注)</span></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <blockquote style="max-width: 100%;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">来源:Rainstorm ,</span></p> <p style="max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">github.com/c-rainstorm/blog/blob/master/java/谈谈Java类加载机制.md</span></p> </blockquote> <p><br></p> <p>最近在学习 Tomcat 架构,其中很重要的一个模块是类加载器,因为以前学习的不够深入,所以趁这个机会好好把类加载机制搞明白。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">概述</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-30568" data-ratio="0.5587467362924282" src="/upload/d7d6f388b9074a0630574ad1716bd4e5.png" data-type="png" data-w="766" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;text-align: center;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="Class-loader"></p> <p><br></p> <p><strong>类加载器主要分为两类</strong>,一类是 JDK 默认提供的,一类是用户自定义的。 JDK 默认提供三种类加载器:</p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>Bootstrap ClassLoader 启动类加载器:每次执行 java 命令时都会使用该加载器为虚拟机加载核心类。该加载器是由 native code 实现,而不是 Java 代码,加载类的路径为 <JAVA_HOME>/jre/lib。特别的 <JAVA_HOME>/jre/lib/rt.jar 中包含了 sun.misc.Launcher 类, 而 sun.misc.Launcher$ExtClassLoader 和 sun.misc.Launcher$AppClassLoader 都是 sun.misc.Launcher 的内部类,所以拓展类加载器和系统类加载器都是由启动类加载器加载的。</p></li> <li><p>Extension ClassLoader, 拓展类加载器:用于加载拓展库中的类。拓展库路径为 <JAVA_HOME>/jre/lib/ext/。实现类为 sun.misc.Launcher$ExtClassLoader</p></li> <li><p>System ClassLoader 系统类加载器:用于加载 CLASSPATH 中的类。实现类为 sun.misc.Launcher$AppClassLoader</p></li> </ol> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">用户自定义的类加载器</span></strong></p> <p><br></p> <p>1. Custom ClassLoader, 一般都是 java.lang.ClassLoder 的子类</p> <p><br></p> <p>正统的类加载机制是基于双亲委派的,也就是当调用类加载器加载类时,首先将加载任务委派给双亲,若双亲无法加载成功时,自己才进行类加载。</p> <p><br></p> <p>在实例化一个新的类加载器时,我们可以为其指定一个 parent,即双亲,若未显式指定,则 System ClassLoader 就作为默认双亲。</p> <p><br></p> <p>具体的说,类加载任务是由 ClassLoader 的 loadClass() 方法来执行的,他会按照以下顺序加载类:</p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>通过 findLoadedClass() 看该类是否已经被加载。该方法为 native code 实现,若已加载则返回。</p></li> <li><p>若未加载则委派给双亲,parent.loadClass(),若成功则返回。</p></li> <li><p>若未成功,则调用 findClass() 方法加载类。java.lang.ClassLoader 中该方法只是简单的抛出一个 ClassNotFoundException 所以,自定义的 ClassLoader 都需要 Override findClass() 方法。</p></li> </ol> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">类加载API</span></strong></p> <p><br></p> <p><strong>java.lang.ClassLoader</strong></p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>ClassLoader 是一个抽象类。</p></li> <li><p>待加载的类必须用 The Java™ Language Specification 定义的全类名,全类名的定义请查阅 The Form of a Binary。</p></li> <li><p>给定一个全类名,类加载器应该去定位该类所在的位置。通用的策略是将全类名转换为类文件路径,然后通过类文件路径在文件系统中定位。</p></li> <li><p>每一个加载到内存的类都由一个 Class 对象来表示,每一个 Class 对象都有一个指向加载该类的类加载器的引用。但是数组的 Class 对象是由 Java 运行时环境创建的,通过 Class.getClassLoader() 方法返回的是数组元素的类加载器,若数组元素是基本类型,则返回 null,若类是由 Bootstrap ClassLoader 加载的话也是返回 null。</p></li> </ul> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Main {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> public static void main(String[] args) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // Object 类在 <java_home>/jre/lib/rt.jar 中,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 由 Bootstrap ClassLoader 加载,由于该类加载器是由 native code 编写</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 所以输出为 null</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Object[] objects = new Object[5];</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println(objects.getClass().getClassLoader());</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // ZipFileAttributes 类在 <java_home>/jre/lib/ext/zipfs.jar 中,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 由 Extension ClassLoader 加载,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 输出为 sun.misc.Launcher$ExtClassLoader@4b67cf4d</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> ZipFileAttributes[] attributes = new ZipFileAttributes[5];</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println(attributes.getClass().getClassLoader());</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // Main 类是自定义的类,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 默认由 System ClassLoader 加载,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // 输出为 sun.misc.Launcher$AppClassLoader@18b4aac2</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Main[] array = new Main[5];</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> array[0] = new Main();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println(array.getClass().getClassLoader());</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>ClassLoader 默认支持并行加载,但是其子类必须调用 ClassLoader.registerAsParallelCapable() 来启用并行加载</p></li> <li><p>一般来说,JVM 从本地文件系统加载类的行为是与平台有关的。</p></li> <li><p>defineClass() 方法可以将字节流转换成一个 Class 对象。然后调用 Class.newInstance() 来创建类的实例</p></li> </ul> <p><br></p> <p><strong>java.security.SecureClassLoader</strong></p> <p>增加了一层权限验证,因为关注点不在安全,所以暂不讨论。</p> <p><br></p> <p><strong>java.net.URLClassLoader</strong></p> <p>该类加载器用来加载 URL 指定的 JAR 文件或目录中的类和资源,以 / 结尾的 URL 认为是目录,否则认为是 JAR 文件。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">// 尝试通过 URLClassLoader 来加载桌面下的 Test 类。</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Main {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> public static void main(String[] args) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> try {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> URL[] urls = new URL[1];</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> URLStreamHandler streamHandler = null;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> File classPath = new File("/home/chen/Desktop/");</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> String repository = (new URL("file", null,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> classPath.getCanonicalPath() + File.separator))</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> .toString();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> urls[0] = new URL(null, repository, streamHandler);</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> ClassLoader loader = new URLClassLoader(urls);</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Class testClass = loader.loadClass("Test");</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> // output: java.net.URLClassLoader@7f31245a</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> System.out.println(testClass.getClassLoader());</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> } catch (MalformedURLException e) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> e.printStackTrace();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> } catch (IOException e) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> e.printStackTrace();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> } catch (ClassNotFoundException e) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> e.printStackTrace();</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">Tomcat 8.5.15类加载机制</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-30569" data-ratio="1.0178571428571428" src="/upload/8c4e09b3b53fe291434c7f9a52f437f8.png" data-type="png" data-w="560" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;text-align: center;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="tomcat-classloader"></p> <p><br></p> <p>Tomcat 使用正统的类加载机制(双亲委派),但部分地方做了改动。</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>Bootstrap classLoader 和 Extension classLoader 的作用不变。</p></li> <li><p>System classLoader 正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使用该变量,而是从以下仓库下加载类:</p></li> </ul> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>$CATALINA_HOME/bin/bootstrap.jar 包含了 Tomcat 的启动类。在该启动类中创建了 Common classLoader、Catalina classLoader、shared classLoader。因为 $CATALINA_BASE/conf/catalina.properties 中只对 common.loader 属性做了定义,server.loader 和 shared.loader 属性为空,所以默认情况下,这三个 classLoader 都是 CommonLoader。具体的代码逻辑可以查阅 org.apache.catalina.startup.Bootstrap 类的 initClassLoaders() 方法和 createClassLoader() 方法。</p></li> <li><p>$CATALINA_BASE/bin/tomcat-juli.jar 包含了 Tomcat 日志模块所需要的实现类。</p></li> <li><p>$CATALINA_HOME/bin/commons-daemon.jar。</p></li> </ol> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>Common classLoader 是位于 Tomcat 应用服务器顶层的公用类加载器。由其加载的类可以由 Tomcat 自身类和所有应用程序使用。扫描路径由 $CATALINA_BASE/conf/catalina.properties 文件中的 common.loader 属性定义。默认是 $CATALINA_HOME/lib。</p></li> <li><p>catalina classLoader 用于加载服务器内部可见类,这些类应用程序不能访问。</p></li> <li><p>shared classLoader 用于加载应用程序共享类,这些类服务器不会依赖。</p></li> <li><p>Webapp classLoader 。每个应用程序都会有一个独一无二的 webapp classloader,他用来加载本应用程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。</p></li> </ul> <p><br></p> <p>特别的:</p> <p><br></p> <p>Webapp classLoader 的默认行为会与正常的双亲委派模式不同:</p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>从 Bootstrap classloader 加载。</p></li> <li><p>若没有,从 /WEB-INF/classes 加载。</p></li> <li><p>若没有,从 /WEB-INF/lib/*.jar 加载。</p></li> <li><p>若没有,则依次从 System、Common、shared 加载(该步骤使用双亲委派)。</p></li> </ol> <p><br></p> <p>当然了,我们也可以通过配置来使 Webapp classLoader 严格按照双亲委派模式加载类:</p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>通过在工程的 META-INF/context.xml(和 WEB-INF/classes 在同一目录下) 配置文件中添加 <Loader delegate="true"/></p></li> <li><p>因为 Webapp classLoader 的实现类是 org.apache.catalina.loader.WebappLoader,他有一个属性叫 delegate, 用来控制类加载器的加载行为,默认为 false,我们可以使用 set 方法,将其设为 true 来启用严格双亲委派加载模式。</p></li> </ol> <p><br></p> <p>严格双亲委派模式加载步骤:</p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>从 Bootstrap classloader 加载。</p></li> <li><p>若没有,则依次从 System、Common、shared 加载。</p></li> <li><p>若没有,从 /WEB-INF/classes 加载。</p></li> <li><p>若没有,从 /WEB-INF/lib/*.jar 加载。</p></li> </ol> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">参考资料</span></strong></p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p>The Java Class Loading Mechanism</p><p>https://docs.oracle.com/javase/tutorial/ext/basics/load.html<br></p></li> <li><p>Java Classloader</p><p>https://en.wikipedia.org/wiki/Java_Classloader<br></p></li> <li><p>Class Loader HOW-TO – Apache Tomcat 8</p><p>https://tomcat.apache.org/tomcat-8.5-doc/class-loader-howto.html<br></p></li> <li><p>《Tomcat 架构解析》</p><p>https://github.com/c-rainstorm/blog/blob/master/java<br></p></li> <li><p>《深入理解 Java 虚拟机》</p><p>https://github.com/c-rainstorm/blog/blob/master/java<br></p></li> </ol> <p><br></p> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;text-align: left;overflow-wrap: break-word !important;"> <section class="" style="padding: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 668px;border-width: 1px;border-style: solid;border-color: rgb(226, 226, 226);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;color: rgb(93, 93, 93);overflow-wrap: break-word !important;"> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;">【关于投稿】</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;">如果大家有原创好文投稿,请直接给公号发送留言。</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">① 留言格式:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">【投稿】+《 文章标题》+ 文章链接</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">② 示例:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">③ 最后请附上您的个人简介哈~</span></span></p> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> </section> </section> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="" data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p>
作者:微信小助手
<section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">在前面的文章中已经介绍了 Redis 的几种高可用技术:<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655817009&idx=1&sn=1b28414b42179d506d4216112dfc36c7&chksm=bd74c0e68a0349f0506ea75dccad96e4c302764b45e2b5c6869d5d1e5d1fd3bb5c4931caf2a6&scene=21#wechat_redirect" target="_blank">持久化</a>、<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655817476&idx=1&sn=0a50d49549e3efbeb7a7cf4903e410f3&chksm=bd74c2d38a034bc53b808d9dcba840f958c2c856a256cb21702a2945b7e02f86b9e2702eb671&scene=21#wechat_redirect" target="_blank">主从复制</a>和<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655818849&idx=1&sn=1e1409612bfcb57bd4ab20a0d7fc43ad&chksm=bd74d9b68a0350a02093cd6b6b2925769ef2f364a411822fc3070ba0ebde6d24377190d5c886&scene=21#wechat_redirect" target="_blank">哨兵</a>,但这些方案仍有不足,其中最主要的问题是存储能力受单机限制,以及无法实现写操作的负载均衡。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5606860158311345" data-s="300,640" src="/upload/470c519ea16941d5cb3fe62766b4919d.png" data-type="png" data-w="758" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">本文将详细介绍集群,主要内容包括:</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;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群的作用</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群的搭建方法及设计方案</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群的基本原理</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">客户端访问集群的方法</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">实践须知(集群伸缩、故障转移、参数优化等)</span></strong></p></li> </ul> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">集群的作用</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群,即 Redis Cluster,是 Redis 3.0 开始引入的分布式存储方案。集群由多个节点(Node)组成,Redis 的数据分布在这些节点中。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><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="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> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>数据分区</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据分区(或称数据分片)是集群最核心的功能。</span><span style="font-size: 15px;letter-spacing: 1px;line-height: 1.75em;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="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">一方面突破了 Redis 单机内存大小的限制,存储容量大大增加。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 单机内存大小受限问题,在介绍持久化和主从复制时都有提及。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例如,如果单机内存太大,bgsave 和 bgrewriteaof 的 fork 操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>高可用</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群支持主从复制和主节点的自动故障转移(与哨兵类似),当任一节点发生故障时,集群仍然可以对外提供服务。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">本文内容基于 Redis 3.0.6。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">集群的搭建</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们将搭建一个简单的集群:共 6 个节点,3 主 3 从。方便起见,所有节点在同一台服务器上,以端口号进行区分,配置从简。</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;">3个主节点端口号:7000/7001/7002;对应的从节点端口号:8000/8001/8002。</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;">手动执行 Redis 命令,一步步完成搭建</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;">使用 Ruby 脚本搭建</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">两者搭建的原理是一样的,只是 Ruby 脚本将 Redis 命令进行了打包封装;在实际应用中推荐使用脚本方式,简单快捷不容易出错。下面分别介绍这两种方式。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>执行 Redis 命令搭建集群</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">集群的搭建可以分为四步:</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;"><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="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">节点握手:</span></strong><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;"><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;">将 16384 个槽分配给主节点。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">指定主从关系:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为从节点指定主节点。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">实际上,前三步完成后集群便可以对外提供服务;但指定从节点后,集群才能够提供真正高可用的服务。</span></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">启动节点</span></strong></span></h3> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群节点的启动仍然是使用 redis-server 命令,但需要使用集群模式启动。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">下面是 7000 节点的配置文件(只列出了节点正常工作关键配置,其他配置,如开启 AOF,可以参照单机节点进行):</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs nginx" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">#redis-7000.conf</span><br><span class="hljs-attribute" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">port</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;">7000</span><br>cluster-enabled <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br>cluster-config-file <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"node-7000.conf"</span><br>logfile <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-7000.log"</span><br>dbfilename <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"dump-7000.rdb"</span><br>daemonize <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中的 cluster-enabled 和 cluster-config-file 是与集群相关的配置。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">cluster-enabledyes:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 实例可以分为单机模式(standalone)和集群模式(cluster);cluster-enabledyes 可以启动集群模式。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom
作者:微信小助手
<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="" data-tools="135编辑器" data-id="91525"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;word-wrap: break-word !important;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="76" data-cropsely1="0" data-cropsely2="63" data-ratio="0.9893162393162394" src="/upload/bb1903af6a3791d92ded49687ea8d37e.png" data-type="png" data-w="468" style="letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 76px;height: 75px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;"></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="max-width: 100%;font-family: 宋体, SimSun;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="">Java编程精选</strong></span></strong></span> <span style="max-width: 100%;font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"></span></strong><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"></span></strong><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"></span></strong></span> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="">点击右侧关注,免费入门到精通!</span></span> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;box-sizing: border-box !important;word-wrap: break-word !important;background-image: url(https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif);background-size: 100% 100%;background-repeat: no-repeat;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzI4MDYwMDc3MQ==&mid=2247484909&idx=2&sn=b6319708cc2ed28833109f2434d3d043&scene=21#wechat_redirect" target="_blank"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="55" data-cropsely1="0" data-cropsely2="63" data-ratio="1" src="/upload/3d12f8975dc35b7b5a03068e64f94bbb.jpg" data-type="jpeg" data-w="258" style="margin: 0px;top: auto;left: auto;right: auto;bottom: auto;width: 63px;height: 63px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <p><br></p> <blockquote data-mpa-powered-by="yiban.io" style="white-space: normal;color: rgb(51, 51, 51);"> <p><span style="color: rgb(136, 136, 136);font-size: 10px;">作者:兵小志大</span></p> <p><span style="color: rgb(136, 136, 136);font-size: 10px;">https://www.cnblogs.com/try-better-tomorrow/p/4987620.html</span></p> </blockquote> <section style="white-space: normal;"> <section> <section> <section data-mpa-template-id="692363" data-mpa-color="null" data-mpa-category="fav"> <section> <section> <section> <section class="output_wrapper" style="font-size: 15px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.为什么要分表:</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">mysql中有一种机制是表锁定和行锁定,是为了保证数据的完整性。表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行。行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作。</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.mysqlproxy:amoeba</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">做mysql集群,利用amoeba。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">从上层的java程序来讲,不需要知道主服务器和从服务器的来源,即主从数据库服务器对于上层来讲是透明的。可以通过amoeba来配置。</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">3.大数据量并且访问频繁的表,将其分为若干个表</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">比如对于某网站平台的数据库表-公司表,数据量很大,这种能预估出来的大数据量表,我们就事先分出个N个表,这个N是多少,根据实际情况而定。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">某网站现在的数据量至多是5000万条,可以设计每张表容纳的数据量是500万条,也就是拆分成10张表,</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">那么如何判断某张表的数据是否容量已满呢?可以在程序段对于要新增数据的表,在插入前先做统计表记录数量的操作,当<500万条数据,就直接插入,当已经到达阀值,可以在程序段新创建数据库表(或者已经事先创建好),再执行插入操作。</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">4.利用merge存储引擎来实现分表</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">如果要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码,因为程序里面的sql语句已经写好了。用merge存储引擎来实现分表,这种方法比较适合.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">举例子:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="1.1171875" src="/upload/e156112e7b384f1b8cd0f551f999eed6.jpg" data-type="jpeg" data-w="640"></p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">-----------------------------华丽的分割线--------------------------------------</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">数据库架构</span></h3> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1、简单的MySQL主从复制:</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">MySQL的主从复制解决了数据库的读写分离,并很好的提升了读的性能,其图如下:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="0.8006230529595015" src="/upload/b5c6cc8de91449b56bfcfb992301e276.jpg" data-type="jpeg" data-w="642"></p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">其主从复制的过程如下图所示:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="0.7385103011093502" src="/upload/54d8eecc997bd80c8633cb5eeef4fd1d.jpg" data-type="jpeg" data-w="631"></p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">但是,主从复制也带来其他一系列性能瓶颈问题:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">1.写入无法扩展</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">2.写入无法缓存</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">3.复制延时</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">4.锁表率上升</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">5.表变大,缓存率下降</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">那问题产生总得解决的,这就产生下面的优化方案,一起来看看。</p> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2、MySQL垂直分区</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。经过垂直分区后的数据库架构图如下:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="0.6746478873239437" src="/upload/e9dd9ddde52fc36ade405fb5e899dea7.jpg" data-type="jpeg" data-w="710"></p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">然而,尽管业务之间已经足够独立了,但是有些业务之间或多或少总会有点联系,如用户,基本上都会和每个业务相关联,况且这种分区方式,也不能解决单张表数据量暴涨的问题,因此为何不试试水平分割呢?</p> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">3、MySQL水平分片(Sharding)</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">这是一个非常好的思路,将用户按一定规则(按id哈希)分组,并把该组用户的数据存储到一个数据库分片中,即一个sharding,这样随着用户数量的增加,只要简单地配置一台服务器即可,原理图如下:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="0.6089466089466089" src="/upload/d612069e9e080f93320f9bdafd66288b.jpg" data-type="jpeg" data-w="693"></p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">如何来确定某个用户所在的shard呢,可以建一张用户和shard对应的数据表,每次请求先从这张表找用户的shardid,再从对应shard中查询相关数据,如下图所示:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><img class="" data-ratio="0.6652542372881356" src="/upload/fad42ba90049fdaf29e189c1beb66bed.jpg" data-type="jpeg" data-w="708"></p> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">单库单表</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到。</p> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">单库多表</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">随着用户数量的增加,user表的数据量会越来越大,当数据量达到一定程度的时候对user表的查询会渐渐的变慢,从而影响整个DB的性能。如果使用mysql,还有一个更严重的问题是,当需要添加一列的时候,mysql会锁表,期间所有的读写操作只能等待。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">可以通过某种方式将user进行水平的切分,产生两个表结构完全一样的user_0000,user_0001等表,user_0000+user_0001+…的数据刚好是一份完整的数据。</p> <h4 style="margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;line-height: inherit;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">多库多表</span></h4> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">随着数据量增加也许单台DB的存储空间不够,随着查询量的增加单台数据库服务器已经没办法支撑。这个时候可以再对数据库进行水平区分。</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">分库分表规则</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">设计表的时候需要确定此表按照什么样的规则进行分库分表。例如,当有新用户时,程序得确定将此用户信息添加到哪个表中;同理,当登录的时候我们得通过用户的账号找到数据库中对应的记录,所有的这些都需要按照某一规则进行。<br>路由</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">通过分库分表规则查找到对应的表和库的过程。如分库分表的规则是user_idmod4的方式,当用户新注册了一个账号,账号id的123,我们可以通过idmod4的方式确定此账号应该保存到User_0003表中。当用户123登录的时候,我们通过123mod4后确定记录在User_0003中。<br>分库分表产生的问题,及注意事项</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">1.分库分表维度的问题</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">假如用户购买了商品,需要将交易记录保存取来,如果按照用户的纬度分表,则每个用户的交易记录都保存在同一表中,所以很快很方便的查找到某用户的购买情况,但是某商品被购买的情况则很有可能分布在多张表中,查找起来比较麻烦。反之,按照商品维度分表,可以很方便的查找到此商品的购买情况,但要查找到买人的交易记录比较麻烦。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">所以常见的解决方式有:</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">a.通过扫表的方式解决,此方法基本不可能,效率太低了。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">b.记录两份数据,一份按照用户纬度分表,一份按照商品维度分表。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">c.通过搜索引擎解决,但如果实时性要求很高,又得关系到实时搜索。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">2.联合查询的问题</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">联合查询基本不可能,因为关联的表有可能不在同一数据库中。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">3.避免跨库事务</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">避免在一个事务中修改db0中的表的时候同时修改db1中的表,一个是操作起来更复杂,效率也会有一定影响。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">4.尽量把同一组数据放到同一DB服务器上</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">例如将卖家a的商品和交易信息都放到db0中,当db1挂了的时候,卖家a相关的东西可以正常使用。也就是说避免数据库中的数据依赖另一数据库中的数据。</p> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">一主多备</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">在实际的应用中,绝大部分情况都是读远大于写。Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在Master和Slave机器上进行,Slave与Master的结构完全一样,一个Master可以有多个Slave,甚至Slave下还可以挂Slave,通过此方式可以有效的提高DB集群的QPS.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">此外,可以看出Master是集群的瓶颈,当写操作过多,会严重影响到Master的稳定性,如果Master挂掉,整个集群都将不能正常工作。</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">所以,1.当读压力很大的时候,可以考虑添加Slave机器的分式解决,但是当Slave机器达到一定的数量就得考虑分库了。2.当写压力很大的时候,就必须得进行分库操作。</p> <hr style="margin-top: 1.5rem;margin-bottom: 1.5rem;font-size: inherit;color: inherit;line-height: inherit;height: 1px;border-right: none;border-bottom: none;border-left: none;border-top-style: dashed;border-top-color: rgb(165, 165, 165);"> <h3 style="margin-top: 1.5em;margin-right: 5px;margin-bottom: 2em;padding: 8px 15px;font-weight: bold;font-size: 1.3em;line-height: inherit;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">MySQL使用为什么要分库分表</span></h3> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">可以用说用到MySQL的地方,只要数据量一大,马上就会遇到一个问题,要分库分表.<br>这里引用一个问题为什么要分库分表呢?MySQL处理不了大的表吗?<br>其实是可以处理的大表的.我所经历的项目中单表物理上文件大小在80G多,单表记录数在5亿以上,而且这个表<br>属于一个非常核用的表:朋友关系表.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">但这种方式可以说不是一个最佳方式.因为面临文件系统如Ext3文件系统对大于大文件处理上也有许多问题.<br>这个层面可以用xfs文件系统进行替换.但MySQL单表太大后有一个问题是不好解决:表结构调整相关的操作基<br>本不在可能.所以大项在使用中都会面监着分库分表的应用.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">从Innodb本身来讲数据文件的Btree上只有两个锁,叶子节点锁和子节点锁,可以想而知道,当发生页拆分或是添加<br>新叶时都会造成表里不能写入数据.<br>所以分库分表还就是一个比较好的选择了.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">那么分库分表多少合适呢?<br>经测试在单表1000万条记录一下,写入读取性能是比较好的.这样在留点buffer,那么单表全是数据字型的保持在<br>800万条记录以下,有字符型的单表保持在500万以下.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">如果按100库100表来规划,如用户业务:<br>500万<em style="font-size: inherit;color: inherit;line-height: inherit;">100</em>100=50000000万=5000亿记录.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;">心里有一个数了,按业务做规划还是比较容易的.</p> <p style="margin-top: 1.5em;margin-bottom: 1.5em;font-size: inherit;color: inherit;line-height: inherit;"><span style="color: inherit;font-size: inherit;letter-spacing: 0px;">用知识的力量武装,把生活的绚烂点亮!</span><br></p> <section class="" powered-by="xiumi.us" style="font-variant-numeric: normal;font-variant-east-asian: normal;white-space: normal;max-width: 100%;box-sizing: border-box;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;line-height: 27.2px;text-align: justify;widows: 1;background-color: rgb(255, 255, 255);word-wrap: break-word !important;"> <section> <section> <br> </section> <section> <img class="" data-ratio="0.1950355" src="/upload/eff4f5b18fe6d3f5e82fb3f14d7e2839.png" data-type="png" data-w="282" style="box-sizing: border-box;vertical-align: middle;width: 199.5px;word-wrap: break-word !important;visibility: visible !important;" width="100%"> </section> </section> </section> <section class="" powered-by="xiumi.us" style="white-space: normal;"> <section> <section> <p><span style="font-size: 14px;color: rgb(89, 89, 89);">最近听说,微信公众号又要改版了……</span></p> <p><span style="font-size: 14px;color: rgb(89, 89, 89);">我顿时慌了。<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></span></p> <p line="9uQV" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);">好怕改版后,我们在茫茫公众号中走散啊啊啊……</span></p> <p line="J9c4" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);">所以,大家快把 </span><strong><span style="font-size: 14px;color: rgb(0, 122, 170);">程序员大咖 </span></strong><span style="font-size: 14px;color: rgb(89, 89, 89);">公众号“设置星标” 。</span></p> <p line="v3GU" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);">(已经设置过的同学,不用重复操作了~~)</span></p> <p line="LnwK" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);">不然的话,等你更新了微信,我怕你们会找不到我~~!</span></p> <p line="XrRp" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);">赶快跟着示意图,设置一下吧。</span></p> <p line="XrRp" class=""><span style="font-size: 14px;color: rgb(89, 89, 89);"><br></span></p> <p line="XrRp" class="" style="text-align: center;"><img class="" data-copyright="0" data-ratio="1.7180722891566265" src="/upload/7183fe8a25647799af1225f5d98a36af.gif" data-type="gif" data-w="415" style="width:65%;height:auto;"><br></p> <p><br></p> <section> <section class="" powered-by="xiumi.us"> <section> <section> <span style="font-size: 14px;color: rgb(89, 89, 89);">之后,就能在订阅号消息的顶部,快速找到我哦。</span> </section> <section> <span style="font-size: 14px;color: rgb(89, 89, 89);"><br></span> </section> </section> </section> </section> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.6863905325443787" data-s="300,640" src="/upload/d4fe22f8d3fa034f9a5e5a546fe99453.png" data-type="png" data-w="338"></p> <p><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p><span style="font-size: 14px;color: rgb(89, 89, 89);">想告诉你们,不管微信怎么改版,我都想在你最触手可及的位置。</span></p> <p><span style="font-size: 14px;color: rgb(89, 89, 89);"><br></span></p> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> <p><strong style="white-space: normal;font-size: 16px;color: rgb(51, 51, 51);text-align: justify;font-family: 微软雅黑;letter-spacing: 1px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=MzI0NjYxMDQ4OQ==&mid=2247484655&idx=4&sn=b63f0a1e32df2fed7ba7e0f0e838e3f9&scene=21#wechat_redirect" target="_blank"><span style="max-width: 100%;color: rgb(67, 149, 245);outline: none 0px;line-height: normal;text-indent: 2em;top: auto;left: auto;right: auto;bottom: auto;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;right: auto;bottom: auto;"><img class="__bg_gif" data-ratio="0.48936170212765956" src="/upload/6f7a0016c2a7d885c35da32a06291359.null" data-type="gif" data-w="47" width="auto" style="border-width: 0px;border-style: initial;border-color: initial;line-height: 38.4384px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: auto !important;"></span></span><span style="max-width: 100%;color: rgb(255, 41, 65);outline: none 0px;font-size: 20px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">【点击成为源码大神】</span></a></strong></strong></strong></strong></p>
作者:Happy生龙
Spring的 事务传播行为类型 ------------ | 事务传播行为类型 | 说明 | |-----|-----| | PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择。 | | PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 | |PROPAGATION_MANDATORY |使用当前的事务,如果当前没有事务,就抛出异常。| |PROPAGATION_REQUIRES_NEW|新建事务,如果当前存在事务,把当前事务挂起。| |PROPAGATION_NOT_SUPPORTED|以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。| |PROPAGATION_NEVER|以非事务方式执行,如果当前存在事务,则抛出异常。| |PROPAGATION_NESTED|如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。| 当使用 PROPAGATION_NESTED 时, 底层的数据源必须基于 JDBC 3.0 ,并且实现者需要支持保存点事务机制。 **readOnly ** 事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这 是一个最优化提示 。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具 (如:hibernate或TopLink)时避免dirty checking(试图“刷新”)。 **Timeout** 在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的 解释。 在xml中的设置应该是 timeout_11 表示超时为11秒。。 事务隔离级别 数据库并发操作存在的异常情况: **1. 更新丢失(Lost update)**: 两个事务都同时更新一行数据但是第二个事务却中途失败退出导致对数据两个修改都失效了这是系统没有执 行任何锁操作因此并发事务并没有被隔离开来。 **2. 脏读取(Dirty Reads)**: 一个事务开始读取 了某行数据但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险很可能所有操作都被回滚。 **3. 不可重复读取(Non-repeatable Reads)**: 一 个事务对同一行数据重复读取两次但是却得到了不同结果。例如在两次读取中途有另外一个事务对该行数据进行了修改并提交。 **4. 两次更新问题(Second lost updates problem)**: 无法重复读取特例,有两个并发事务同时读取同一行数据然后其中一个对它进行修改提交而另一个也进行了修改提交这就会造成 第一次写操作失效。 **5. 幻读(Phantom Reads)**: 也称为幻像(幻 影)。事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有 另外一个事务插入数据造成的。 为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。 **1.未授权读取(Read Uncommitted)**: 也称 未提交读。允许脏读取但不允许更新丢失,如果一个事务已经开始写数据则另外一个数据则不允许同时进行写操作但允许其他事务读此行数据。该隔离级别可以通过 “排他写锁”实现。事务隔离的最低级别,仅可保证不读取物理损坏的数据。与READ COMMITTED 隔离级相反,它允许读取已经被其它用户修改但尚未提交确定的数据。 **2. 授权读取(Read Committed)**: 也称提交 读。允许不可重复读取但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现,读取数据的事务允许其他事务继续访问该行数据,但是未提交写事务将 会禁止其他事务访问该行。SQL Server 默认的级别。在此隔离级下,SELECT 命令不会返回尚未提交(Committed) 的数据,也不能返回脏数据。 **3. 可重复读取(Repeatable Read)**: 禁止 不可重复读取和脏读取。但是有时可能出现幻影数据,这可以通过“共享读锁”和“排他写锁”实现,读取数据事务将会禁止写事务(但允许读事务),写事务则禁 止任何其他事务。在此隔离级下,用SELECT 命令读取的数据在整个命令执行过程中不会被更改。此选项会影响系统的效能,非必要情况最好不用此隔离级。 **4. 串行(Serializable)**: 也称可串行读。提 供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机 制保证新插入的数据不会被刚执行查询操作事务访问到。事务隔离的最高级别,事务之间完全隔离。如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠 事务均是串行的。