㈠ 分布式锁是什么
什么是分布式锁?实现分布式锁的三种方式
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。那具体什么是分布式锁,分布式锁应用在哪些业务场景、如何来实现分布式锁呢?
一 为什么要使用分布式锁
我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的锁进行处理,并且可以完美的运行,毫无Bug!
注意这是单机应用,后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均衡,大致如下图:
六、基于ZooKeeper的实现方式
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:
(1)创建一个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。
优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。
七、总结
上面的三种实现方式,没有在所有场合都是完美的,所以,应根据不同的应用场景选择最适合的实现方式。
在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,这时候使用分布式锁就可以很好地控制资源。
当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想。
㈡ java里volatile关键字有什么特性
Java语言中关键字 volatile 被称作轻量级的 synchronized,与synchronized相比,volatile编码相对简单且运行的时的开销较少,但能够正确合理的应用好 volatile 并不是那么的容易,因为它比使用锁更容易出错,接下来本文主要介绍 volatile 的使用准则,以及使用过程中需注意的地方。
为何使用volatile?
(1)简易性:在某些需要同步的场景下使用volatile变量要比使用锁更加简单
(2)性能:在某些情况下使用volatile同步机制的性能要优于锁
(3)volatile操作不会像锁一样容易造成阻塞
volatile特性
(1)volatile 变量具有 synchronized 的可见性特性,及如果一个字段被声明为volatile,java线程内存模型确保所有的线程看到这个变量的值是一致的
(2)禁止进行指令重排序
(3)不保证原子性
注:
① 重排序:重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段
② 原子性:不可中断的一个或一系列操作
③ 可见性:锁提供了两种主要特性:互斥和可见性,互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。
volatile的实现原理
如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,该Lock指令会使这个变量所在缓存行的数据回写到系统内存,根据缓存一致性协议,每个处理器都会通过嗅探在总线上传输的数据来检查自己缓存的值是否已过期,当处理器发现自己的缓存行对应的地址被修改,就会将当前处理器的缓存行设置成无效状态,在下次访问相同内存地址时,强制执行缓存行填充。
正确使用volatile的场景
volatile 主要用来解决多线程环境中内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,就无法解决线程安全问题。如:
1、不适合使用volatile的场景(非原子性操作)
(1)反例
private static volatile int nextSerialNum = 0;
public static long generateSerialNum() {
return nextSerialNum++;
}
这个方法的目的是要确保每次调用都返回不同的自增值,然而结果并不理想,问题在于增量操作符(++)不是原子操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,如果第二个线程在第一个线程读取旧值和写回新值期间读取这个域,第二个线程与第一个线程就会读取到同一个值。
(2)正例
其实面对上面的反例场景可以使用JDK1.5 java.util.concurrent.atomic中提供的原子包装类型来保证原子性操作
private static AtomicInteger nextSerialNum = new AtomicInteger(0);
public static long generateSerialNum() {
return nextSerialNum.getAndIncrement();
}
2、适合使用volatile的场景
在日常工作当中volatile大多被在状态标志的场景当中,如:
要通过一个线程来终止另外一个线程的场景
(1)反例
private static boolean stopThread;
public static void main(String[] args) throws InterruptedException {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopThread) {
i++;
}
}
});
th.start();
TimeUnit.SECONDS.sleep(2);
stopThread = true;
}
运行后发现该程序根本无法终止循环,原因是,java语言规范并不保证一个线程写入的值对另外一个线程是可见的,所以即使主线程main函数修改了共享变量stopThread状态,但是对th线程并不一定可见,最终导致循环无法终止。
(2)正例
private static volatile boolean stopThread;
public static void main(String[] args) throws InterruptedException {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopThread) {
i++;
}
}
});
th.start();
TimeUnit.SECONDS.sleep(2);
stopThread = true;
}
通过使用关键字volatile修饰共享变量stopThread,根据volatile的可见性原则可以保证主线程main函数修改了共享变量stopThread状态后对线程th来说是立即可见的,所以在两秒内线程th将停止循环。
㈢ 有谁知道cache的发展过程
纵观PC系统和CPU二十年的发展,随着半导体加工工艺水平的不断提高,CPU和存储器的性能都有了很大的提高。
CPU频率的提高,必然要求系统中存储器的存取速度要提高,还要求其容量要增大。主存储器DRAM容量的提高还是比较快的,但是DRAM读取时间的提高却很慢。从而在速度上与CPU主频的提高产生了极不相配的情况,这样会影响整个系统的性能。二十年来,CPU设计的问题之一就是解决高速CPU和低速DRAM之间的平衡或匹配问题,以求系统性能的整体提高。
在它们之间加入高速缓冲存储器Cache,就是这个问题的解决方案之一。
Cache随CPU的发展而不断改变,可以概括为:从无到有,由小到大,先外后内,纵深配备,软硬兼施。初期的CPU没有Cache,在80386时期出现外部Cache;80486时期开始有内部仅8kB的Cache。Cache的分级也由L1和L2级,发展到L0和L3级的纵深配备;Cache的大小由当初的8kB,直到Merced的1~2MB。为了更好地利用Cache,还专门配有缓存控制指令。
本文回顾了在过去的二十年中,Cache技术的发展历程,并对PC其它设备使用Cache技术作了简单陈述。 PC初期无需Cache在八十年代初,由于CPU主频很低,DRAM的存取时间甚至快于CPU存取时间,因此无需Cache。例如,当时PC机采用8088CPU,系统主频为4.77MHz,一个基本总线周期为4拍,即840ns。此时64kB的DRAM存取周期200ns,造成DRAM等待CPU的执行的局面,无需Cache。
在PC/AT机采用80286CPU后,系统主频增加到10MHz,1个基本总线周期为2拍,即200ns。此时必须用读取时间为100ns的DRAM。在采用25MHz的80386DX时,一个基本总线周期为2拍,即80ns,当时已没有速度相匹配的DRAM可用。解决方案有2种:一种是在基本总线周期中插入等待,降低CPU的处理能力;另一种是采用内部和外部Cache,使用SRAM芯片以提高存储器的读取速度。80386没有L1 Cache80386初期主频为20MHz。Intel公司十分重视80386的设计制造,把它定位于“新一代个人电脑架构”,想把一些新技术设计在芯片中。但由于当时工艺所限,内置高速缓存的芯片体积过大,造成成本上升,同时工期有限,几经权衡,最后决定在80386芯片不设置高速缓冲存储器,可以生产另外的Cache,以配合80386运作。
尽管人们意识到CPU主频的增加与内存DRAM存取时间过慢的矛盾已愈加突出,但因条件所限,80386内部没有L1 Cache,只有外部的Cache。80486出现Cache80486是由80386CPU加80387数字协处理器以及8kB Cache构成。
当CPU的时钟频率继续增加时,外部Cache的SRAM芯片速度也要相应提高,这样会增加系统成本,为此在设计80486时采用了内部Cache。
80486芯片内由8kB的Cache来存放指令和数据。同时,80486也可以使用处理器外部的第二级Cache,用以改善系统性能并降低80486要求的总线带宽。Cache可以工作在80486所有的操作模式:实地址模式、保护模式和X86模式。对Cache的操作是由系统自动进行的,对程序员透明。而在多处理器系统中,可能要求系统软件的干预。对于一般的计算机,在系统CMOS设置中均有Cache使用模式的设置。
80486内部Cache是一个4路组相联Cache,在主存储器中给定单元的数据能够存储在Cache内4个单元中的任何一个。这种4路相联方式是高命中率的全相联Cache和快速的直接映像Cache的一种折衷,因而能进行快速查找并获得高的命中率。Peutium的分离L1 Cache和L2 CachePentium处理器采用了超标量结构双路执行的流水线,有分支预测技术。
由于Pentium设计有2条并行整数流水线,可同时执行2条命令。整数单元的潜在处理能力实际可增加一倍,处理器也需要对命令和数据进进双倍的访问。为使这些访问不互相干涉,Intel把在486上共用的内部Cache,分成2个彼此独立的8kB代码Cache和8kB数据Cache,这两个Cache可以同时被访问。这种双路高速缓存结构减少了争用Cache所造成的冲突,提高了处理器效能。Pentium的Cache还采用了回写写入方式,这同486的贯穿写入方式相比,可以增加Cache的命中率。此外,还采用了一种称为MESI高速缓存一致性协议,为确保多处理器环境下的数据一致性提供了保证。Pentium Pro内嵌式L2 Cache为使Pentium Pro的性能超过Pentium,必需使用创新的设计方法。Pentium Pro使用了新的超标量和级流水线技术,包括无序执行、动态分支预测和推测执行的动态执行新技术。它可以使CPU在一个时钟周期执行3条微操作。CPU并行处理速度的加快,意味着它同时处理指令和数据的数量增加,为不使CPU处于等待状态,需要重新设计Cache。
Pentium Pro在片内第一级Cache的设计方案中,使指令Cache与数据Cache分别设置。指令Cache的容量为8kB,采用2路组相联映像方式。数据Cache的容量也为8kB,但采用4路组相联映像方式。Pentium Pro采用MESI(修改、排他、共享、作废)协议来维持Cache和主存储器之间的一致性。通常,人们总以为,像Pentium Pro这样的3路超标量结构的微处理器会采用更大容量的片内第一级Cache和更大的第二级Cache。然而,Intel公司的设计者却选择了另一条设计思路——设计一种Cache存储阶层结构,使得能够从一个Cache流动到另一个Cache,而不用阻塞执行。
Pentium Pro采用了内嵌式或称捆绑式L2Cache,大小为256kB或512kB。此时的L2已经用线路直接连到CPU上,益处之一就是减少了对急剧增多L1 Cache的需求。L2 Cache还能与CPU同步运行。即当L1 Cache不命中时,立刻访问L2 Cache,不产生附加延迟。为进一步减少因要访问的信息不在高速缓冲中时所带来的性能损失,Pentium Pro的L1和L2都设计成非锁定型。即当哪个Cache中没有CPU所需的信息时,它不妨碍后面访问Cache的处理过程。Cache可以直接处理最多4次的Cache缺页情况,借助CPU的内存有序缓冲区可以顺序保存最多12次的内存访问。非锁定型Cache适用于Pentium Pro的乱序执行核心,因为在可能引发流水线延迟的长等待内存操作期间,这些Cache可以让CPU继续运行。
Pentium Pro的如此捆绑封装,带来器件成本提高。一方面专用的L2 cache芯片成本高,另一方面两个不同功能的芯片只有放在一起联结后才能最后测试其性能的完整性。而当其中有一个有缺陷时,两个芯片都被报废。在以后的Pentium Pro产品中,又将L2 Cache从芯片中去掉。Pentium MMX容量增大的L1和L2CachePentium MMX是能运行多媒体指令MMX的高能奔腾处理器。Pentium MMX具有改进的分支预测和增强型流水线技术,并将L1 Cache容量增加到32kB,L2 Cache为512kB。
Pentium MMX的片内L1数据和指令的Cache,每个增到16kB,4路相联。较大的独立内部Cache、减少平均内存存取时间,同时提供对近期所用指令和数据的快速存取,性能因此得到提高。数据Cache支持采用回写方式更新内存。
由于CacheL1容量的增大,使当时的应用程序运行速度提高了10%左右。PentiumⅡ设有双独立总线连接L2 CachePentiumⅡ是Pentium Pro的改进型,具有MMX指令,使用动态执行技术,采用双独立总线结构。PentiumⅡ同样有2级Cache,L1为32kB(指令和数据Cache各16kB)是Pentium Pro的一倍。L2为512kB。
Pentium Ⅱ与Pentium Pro在L2 Cache 的不同是由于制作成本原因。L2 Cache已不在内嵌芯片上,而是与CPU通过专用64位高速缓存总线相联,与其它元器件共同被组装在同一基板上,即“单边接触盒”上。双独立总线结构就是:L2高速缓存总线和处理器至主内存(Processor-to-main-memory)的系统总线。 PentiumⅡ处理器可以同时使用这两条总线,与单一总线结构的处理器相比,该处理器可以进出两倍多的数据,可允许 PentiumⅡ处理器的L2高速缓存比Pentium处理器的L2高速缓存要快1倍。随着 PentiumⅡ处理器主频的提高,L2高速缓存的速度也将加快。最后,流水线型系统总线可允许同时并行传输,而不是单个顺序型传输。改进型的双重独立总线结构,可以产生超过与单总线结构三倍带宽的性能。另外,在PentiumⅡ中,采用了ECC技术,此技术应用到二级高速缓存中,大大提高了数据的完整性和可靠性。
为开发低端市场,曾在 PentiumⅡ的基板上除去L2,牺牲一些性能,制造廉价CPU。这就是最初的Celeron处理器。以后的Celeron仍加有较小的片上L2 Cache,其大小为128kB。PentiumⅢ的L2 Cache增大PentiumⅢ也是基于Pentium Pro结构为核心,在原有MMX多媒体指令的基础上,又增了70多条多媒体指令。它使用动态执行技术,采用双独立总线结构。
PentiumⅢ具有32kB非锁定L1 Cache和512kB非锁定L2 Cache。L2可扩充到1~2MB,具有更合理的内存管理,可以有效地对大于L2缓存的数据块进行处理,使CPU、Cache和主存存取更趋合理,提高了系统整体性能。在执行视频回放和访问大型数据库时,高效率的高速缓存管理使PⅢ避免了对L2 Cache的不必要的存取。由于消除了缓冲失败,多媒体和其它对时间敏感的操作性能更高了。对于可缓存的内容,PⅢ通过预先读取期望的数据到高速缓存里来提高速度,这一特色提高了高速缓存的命中率,减少了存取时间。Merced设有L0即将推出的第7代处理器Merced主频可达1GHz。很明显,对Cache的要求更高了。为此,lntel本着“大力提高执行单元和缓存间数据交换速度”的思想,在芯片内开发新的Cache,并增加L1 Cache的容量,来平衡CPU和DRAM间的速度。
为此,在Merced的片上最接近执行单元旁再设另一处Cache,称为L0缓存,是指令/数据分离型。由于L0Cache在物理位置上比L1离执行单元更近,布线距离的缩短,使它与执行单元间的数据交换速度比L1还快,可以进一步提高工作主频。
同时,还要在芯片内部配置超过1MB的大容量L1 Cache。芯片内部Cache比外部Cache更易于提升与执行单元间的数据传送速度。内部Cache的加大,执行单元不易发生“等待”。现行的内部Cache容量仅为32kB~128kB。内部Cache容量的增加会引起芯片面积增大,提高制造成本。但大部分公司认为,由于内部Cache容量增大而导致成本的上扬,可以用制造技术来弥补。与Cache相配合的缓存控制指令为进一步发挥Cache的作用,改进内存性能并使之与CPU发展同步来维护系统平衡,一些制造CPU的厂家增加了控制缓存的指令。如Intel公司在PentiumⅢ处理器中新增加了70条3D及多媒体的SSE指令集。其中有很重要的一组指令是缓存控制指令。AMD公司在K6-2和K6-3中的3DNow!多媒体指令中,也有从L1数据Cache中预取最新数据的数据预取指令(Prefetch)。
PentiumⅢ处理器的缓存控制指令,用于优化内存连续数据流。针对数据流的应用需要对以前的Cache运作方式进行了改进,减少了一些不必要的中间环节,节省了时间,增加了CPU数据总线的实际可用带宽,也提高了Cache的效率。
有两类缓存控制指令。一类是数据据预存取(Prefetch)指令,能够增加从主存到缓存的数据流;另一类是内存流优化处理(Memory Streaming)指令,能够增加从处理器到主存的数据流。这两类指令都赋予了应用开发人员对缓存内容更大控制能力,使他们能够控制缓存操作以满足其应用的需求。
数据预存取指令允许应用识别出所需的信息,并预先将其从主存中取出存入缓存。这样一来,处理器可以更快地获取信息,从而改进应用性能。为了进一步削减内存延迟,内存访问还可以与计算机周期保持流水操作。例如,如果一个应用需要计算一些数值以供3D图形使用,当它在计算一个值的同时就可以预取下一个需要计算的数值。
内存流优化处理指令允许应用越过缓存直接访问主存。通常情况下,处理器写出的数据都将暂时存储在缓存中以备处理器稍后使用。如果处理器不再使用它,数据最终将被移至主存。然而,对于多媒体应用来就,通常不再需要使用这些数据。因此,这时将数据尽快地移到主存中则显得至关重要。采用了PentiumⅢ处理器的内存流优化处理指令后,应用程序就能让数据搭乘“直达快车”,直接到达主存。当数据流直接到达主存时,处理器负责维护缓存的一致性。因为这种方式避免了为数据流留出空间清空缓存的当前内容,从而也提高了缓存的利用率。
总而言之,缓存控制指令改进了进出处理器的数据据流,使处理器保持其高速率运作。通过这些指令(同时还需要一些专为其设计以使其发挥优势的软件),商业用户可以在操作系统和图形设备驱动程序中感受其性能优势。Cache在PC中其它设备的应用Cache作为一种速度匹配技术,不仅用在提高CPU对内存的读写速度上,而且也用在CPU结构的其它部分和PC系统中。
PC的显示系统中,由于3D应用的迅猛发展,大量的显示内存使用着高速缓存技术,如前台缓存、后台缓存、深度缓存和纹理缓存等。
PC的磁盘系统中,为提高内存对磁盘(主要是硬盘)的读写速度,就要建立磁盘高速缓存。因为DRAM内存的存取速度对CPU来说较慢,但对磁盘的存取速度却是很快的。这是因为磁盘存储系统包含有磁头的机械运动,而机械运动无法跟传送电信号的电子速度相比。此外,磁头中电与磁的信号转换也对速度有影响。这样,为了提高磁盘存取速度而采用Cache也就顺理成章了。硬盘Cache无需使用高速的SRAM,它只需在内存(DRAM)中划出一个区域,作为专用的磁盘缓冲区,采用一定的数据结构,即可实现磁盘存取的Cache技术。它的过程也是把即将访问的数据整块地拷贝到高速缓存区中,然后内存再到高速缓存中去逐个读取数据。由于数据在RAM空间内部传送要比在RAM与磁盘间传送快得多,系统由此提高了存取速度。
硬盘的Cache可以放在常规内存中。不过,为了不占用宝贵的用户程序空间,通常是把它设在扩展内存或扩充内存里。硬盘Cache是由人们共知的SMARTDRIVE.EXE文件自动建立的,用户只需在AUTOEXEC.BAT与CONFIG.SYS中加入相应的命令行就成了。
在较慢速的其它外围设备和内存的数据交换中,在网络通讯中,都需要使用Cache技术。推而广之,凡是在传输速度有较大差异的设备之间,都可以利用Cache的速度匹配技术。结束语PC中的Cache主要是为了解决高速CPU和低速DRAM内存间速度匹配的问题,是提高系统性能,降低系统成本而采用的一项技术。随着CPU和PC的发展,20年来,现在的Cache已成为CPU和PC不可缺少的组成部分,是广大用户衡量系统性能优劣的一项重要指标。据预测,在21世纪初期,CPU主频加快发展的趋势,加上内存DRAM的存取时间也会提高,从系统的性价比考虑,Cache的配备仍然是重要的技术之一。
㈣ 分布式锁的三种实现方式
分布式锁的三种实现方式分别是:基于数据库实现分布式锁、基于缓存(Redis等)实现分布式锁、基于Zookeeper实现分布式锁。
一、基于数据库实现分布式锁
1、悲观锁
利用select … where … for update 排他锁。
注意:其他附加功能与实现一基本一致,这里需要注意的是“where name=lock ”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。
2、乐观锁
所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有update version失败后才能觉察到,抢购和秒杀就是用了这种实现以防止超卖,通过增加递增的版本号字段实现乐观锁。
二、基于缓存(Redis等)实现分布式锁
1、使用命令介绍:
(1)SETNX
SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。
(2)expire
expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
(3)delete
delete key:删除key
在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。
2、实现思想:
(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
三、基于Zookeeper实现分布式锁
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。
基于ZooKeeper实现分布式锁的步骤如下:
(1)创建一个目录mylock。
(2)线程A想获取锁就在mylock目录下创建临时顺序节点。
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁。
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点。
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
(4)缓存一致性锁扩展阅读;
一、数据库分布式锁实现的缺点:
1、db操作性能较差,并且有锁表的风险。
2、非阻塞操作失败后,需要轮询,占用cpu资源。
3、长时间不commit或者长时间轮询,可能会占用较多连接资源。
二、Redis(缓存)分布式锁实现的缺点:
1、锁删除失败,过期时间不好控制。
2、非阻塞,操作失败后,需要轮询,占用cpu资源。
三、ZK分布式锁实现的缺点:
性能不如redis实现,主要原因是写操作(获取锁释放锁)都需要在Leader上执行,然后同步到follower。
㈤ 以道大数据课程体系都讲什么
大数据技术在如今应用非常广泛,许多想入行学习大数据培训的童鞋不知从何学起,从哪儿开始学首先要根据你的基本情况而定,如果你是零基础的也不需要担心,先从基础开始学起就好了,接下来学习基础java开始、数据结构、关系型数据库、linux系统操作,夯实基础之后,再进入大数据的学习,例如:hadoop离线分析、Storm实时计算、spark内存计算的学习,以道教育大数据课程体系可以如下:
第一阶段 WEB 开发基础
HTML基础
1、Html基本介绍
2、HTML语法规范
3、基本标签介绍
4、HTML编辑器/文本文档/WebStrom/elipse
5、HTML元素和属性
6、基本的HTML元素
6.1 标题
6.2 段落
6.3 样式和style属性
6.3 链接 a
6.4 图像 img
6.5 表格 table
6.6 列表 ul/ol/dl
7、 HTML注释
8、表单介绍
9、Table标签
10、DIV布局介绍
11、HTML列表详解
HTML布局和Bootstrap
1、 HTML块元素(block)和行内元素(inline)
2、使用div实现网页布局
3、响应式WEB设计(Responsive Web Design)
4、使用bootstrap实现响应式布局
HTML表单元素
1、HTML表单 form
2、HTML表单元素
3、 HTML input的类型 type
4、 Html input的属性
CSS基础
1、CSS简介及基本语法
2、在HTML文档中使用CSS
3、CSS样式
4、CSS选择器
5、盒子模型
6、布局及定位
CSS高级/CSS3
1、尺寸和对齐
2、分类(clear/cursor/display/float/position/visibility)
3、导航栏
4、图片库
5、图片透明
6、媒介类型 @media
7、CSS3
8、CSS3动画效果
JavaScript基础
1、JavaScript简介
2、基本语法规则
3、在HTML文档中使用JS
4、JS变量
5、JS数据类型
6、JS函数
7、JS运算符
8、流程控制
9、JS错误和调试
JavaScript对象和作用域
1、数字 Number
2、字符串String
3、日期 Date
4、数组
5、数学 Math
6、DOM对象和事件
7、BOM对象
8、Window对象
9、作用域和作用域链
10、JSON
Javascript库
1、Jquery
2、Prototype
3、Ext Js
Jquery
1、Jquery基本语法
2、Jquery选择器
3、Jquery事件
4、Jquery选择器
5、Jquery效果和动画
6、使用Jquery操作HTML和DOM
7、Jquery遍历
8、Jquery封装函数
9、Jquery案例
表单验证和Jquery Validate
1、用Js对HTML表单进行验证
2、Jquery Validata基本用法
3、默认校验规则和提示信息
4、debug和ignore
5、更改错误信息显示位置和样式
6、全部校验通过后的执行函数
7、修改验证触发方式
8、异步验证
9、自定义校验方法
10、radio 和 checkbox、select 的验证
Java基础
1、关于Java
2、Java运行机制
3、第一个Java程序,注释
4、Javac,Java,Javadoc等命令
5、标识符与关键字
6、变量的声明,初始化与应用
7、变量的作用域
8、变量重名
9、基本数据类型
10、类型转换与类型提升
11、各种数据类型使用细节
12、转义序列
13、各种运算符的使用
流程控制
1、选择控制语句if-else
2、选择控制语句switch-case
3、循环控制语句while
4、循环控制语句do-while
5、循环控制语句for与增强型for
6、break,continue,return
7、循环标签
8、数组的声明与初始化
9、数组内存空间分配
10、栈与堆内存
11、二维(多维)数组
12、Arrays类的相关方法
13、main方法命令行参数
面向对象
1、面向对象的基本思想
2、类与对象
3、成员变量与默认值
4、方法的声明,调用
5、参数传递和内存图
6、方法重载的概念
7、调用原则与重载的优势
8、构造器声明与默认构造器
9、构造器重载
10、this关键字的使用
11、this调用构造器原则
12、实例变量初始化方式
13、可变参数方法
访问权限控制
1、包 package和库
2、访问权限修饰符private/protected/public/包访问权限
3、类的访问权限
4、抽象类和抽象方法
5、接口和实现
6、解耦
7、Java的多重继承
8、通过继承来扩展接口
错误和异常处理
1、概念:错误和异常
2、基本异常
3、捕获异常 catch
4、创建自定义异常
5、捕获所有异常
6、Java标准异常
7、使用finally进行清理
8、异常的限制
9、构造器
10、异常匹配
11、异常使用指南
数据库基础(MySQL)
数据库基础(MySQL)
JDBC
1、Jdbc基本概念
2、使用Jdbc连接数据库
3、使用Jdbc进行crud操作
4、使用Jdbc进行多表操作
5、Jdbc驱动类型
6、Jdbc异常和批量处理
7、Jdbc储存过程
Servlet和JSP
1、Servlet简介
2、Request对象
3、Response对象
4、转发和重定向
5、使用Servlet完成Crud
6、Session和Coolie简介
7、ServletContext和Jsp
8、El和Jstl的使用
Ajax
1、什么是Ajax
2、XMLHttpRequest对象(XHR)
3、XHR请求
4、XHR响应
5、readystate/onreadystatechange
6、Jquery Ajax
7、JSON
8、案例:对用户名是否可用进行服务器端校验
综合案例
1、项目开发一般流程介绍
2、模块化和分层
3、DButils
4、QueryRunner
5、ResultSetHandle
6、案例:用户登录/注册,从前端到后端
第二阶段 Java SE
访问权限和继承
1、包的声明与使用
2、import与import static
3、访问权限修饰符
4、类的封装性
5、static(静态成员变量)
6、final(修饰变量,方法)
7、静态成员变量初始化方式
8、类的继承与成员继承
9、super的使用
10、调用父类构造器
11、方法的重写与变量隐藏
12、继承实现多态和类型转换
13、instanceof
抽象类与接口
1、抽象类
2、抽象方法
3、继承抽象类
4、抽象类与多态
5、接口的成员
6、静态方法与默认方法
7、静态成员类
8、实例成员类
9、局部类
10、匿名类
11、eclipse的使用与调试
12、内部类对外围类的访问关系
13、内部类的命名
Lambda表达式与常用类
1、函数式接口
2、Lambda表达式概念
3、Lambda表达式应用场合
4、使用案例
5、方法引用
6、枚举类型(编译器的处理)
7、包装类型(自动拆箱与封箱)
8、String方法
9、常量池机制
10、String讲解
11、StringBuilder讲解
12、Math,Date使用
13、Calendars使用
异常处理与泛型
1、异常分类
2、try-catch-finally
3、try-with-resources
4、多重捕获multi-catch
5、throw与throws
6、自定义异常和优势
7、泛型背景与优势
8、参数化类型与原生类型
9、类型推断
10、参数化类型与数组的差异
11、类型通配符
12、自定义泛型类和类型擦出
13、泛型方法重载与重写
集合
1 、常用数据结构
2 、Collection接口
3 、List与Set接口
4 、SortedSet与NavigableSet
5 、相关接口的实现类
6 、Comparable与Comparator
7、Queue接口
8 、Deque接口
9 、Map接口
10、NavigableMap
11、相关接口的实现类
12、流操作(聚合操作)
13、Collections类的使用
I/O流与反射
1 、File类的使用
2 、字节流
3 、字符流
4 、缓存流
5 、转换流
6 、数据流
7、对象流
8、类加载,链接与初始化
9 、ClassLoader的使用
10、Class类的使用
11、通过反射调用构造器
12、安全管理器
网络编程模型与多线程
1、进程与线程
2、创建线程的方式
3、线程的相关方法
4、线程同步
5、线程死锁
6、线程协作操作
7、计算机网络(IP与端口)
8、TCP协议与UDP协议
9、URL的相关方法
10、访问网络资源
11、TCP协议通讯
12、UDP协议通讯
13、广播
SSM-Spring
1.Spring/Spring MVC
2.创建Spring MVC项目
3.Spring MVC执行流程和参数
SSM-Spring.IOC
1.Spring/Spring MVC
2.创建Spring MVC项目
3.Spring MVC执行流程和参数
SSM-Spring.AOP
1.Spring/Spring MVC
2.创建Spring MVC项目
3.Spring MVC执行流程和参数
SSM-Spring.Mybatis
1.MyBatis简介
2.MyBatis配置文件
3.用MyBatis完成CRUD
4.ResultMap的使用
5.MyBatis关联查询
6.动态SQL
7.MyBatis缓冲
8.MyBatis-Generator
Socket编程
1.网络通信和协议
2.关于Socket
3.Java Socket
4.Socket类型
5.Socket函数
6.WebSocket
7.WebSocket/Spring MVC/WebSocket Ajax
IO/异步
window对象
全局作用域
窗口关系及框架
窗口位置和大小
打开窗口
间歇调用和超时调用(灵活运用)
系统对话框
location对象
navigator对象
screen对象
history对象
NIO/AIO
1.网络编程模型
2.BIO/NIO/AIO
3.同步阻塞
4.同步非阻塞
5.异步阻塞
6.异步非阻塞
7.NIO与AIO基本操作
8.高性能IO设计模式
第三阶段 Java 主流框架
MyBatis
1.mybatis框架原理分析
2.mybatis框架入门程序编写
3.mybatis和hibernate的本质区别和应用场景
4.mybatis开发方法
5.SqlMapConfig配置文件讲解
6.输入映射-pojo包装类型的定义与实现
7.输出映射-resultType、resultMap
8.动态sql
9.订单商品数据模型分析
10.高级映射的使用
11.查询缓存之一级缓存、二级缓存
12.mybatis与spring整合
13. mybatis逆向工程自动生成代码
Spring/Spring MVC
1. springmvc架构介绍
2. springmvc入门程序
3. spring与mybatis整合
4. springmvc注解开发—商品修改功能分析
5. springmvc注解开发—RequestMapping注解
6. springmvc注解开发—Controller方法返回值
7. springmvc注解开发—springmvc参数绑定过程分析
8. springmvc注解开发—springmvc参数绑定实例讲解
9. springmvc与struts2的区别
10. springmvc异常处理
11. springmvc上传图片
12. springmvc实现json交互
13. springmvc对RESTful支持
14. springmvc拦截器
第四阶段 关系型数据库/MySQL/NoSQL
SQL基础
1.SQL及主流产品
2.MySQL的下载与安装(sinux/windows)
3.MySql的基本配置/配置文件
4.基本的SQL操作 DDL
5.基本的SQL操作 DML
6.基本的SQL操作 DCL
7.MySQL客户端工具
8.MySQL帮助文档
MySQL数据类型和运算符
1 数值类型
2 日期时间类型
3 字符串类型
4 CHAR 和 VARCHAR 类型
5 BINARY 和 VARBINARY 类型
6 ENUM 类型
7 SET 类型
8 算术运算符
9 比较运算符
10 逻辑运算符
11 位运算
12 运算符的优先级
MySQL函数
1 字符串函数
2 数值函数
3 日期和时间函数
4 流程函数
5 其他常用函数
MySQL存储引擎
1.MySQL支持的存储引擎及其特性
2.MyISAM
3.InnoDB
4.选择合适的存储引擎
选择合适的数据类型
1 CHAR 与 VARCHAR
2 TEXT 与 BLOB
3 浮点数与定点数
4 日期类型选择
字符集
1 字符集概述
2 Unicode字符集
3 汉字及一些常见字符集
4 选择合适的字符集
5 MySQL 支持的字符集
6 MySQL 字符集的设置 .
索引的设计和使用
1.什么是索引
2.索引的类型
3.索引的数据结构 BTree B+Tree Hash
4.索引的存储
5.MySQL索引
6.查看索引的使用情况
7.索引设计原则
视图/存储过程/函数/触发器
1. 什么是视图
2. 视图操作
3. 什么是存储过程
4. 存储过程操作
5. 什么是函数
6. 函数的相关操作
7. 触发器
事务控制/锁
1. 什么是事务
2. 事务控制
3. 分布式事务
4. 锁/表锁/行锁
5. InnoDB 行锁争用
6. InnoDB 的行锁模式及加锁方法7
7 InnoDB 行锁实现方式7
8 间隙锁(Next-Key 锁)
9 恢复和复制的需要,对 InnoDB 锁机制的影响
10 InnoDB 在不同隔离级别下的一致性读及锁的差异
11 表锁
12 死锁
SQL Mode和安全问题
1. 关于SQL Mode
2. MySQL中的SQL Mode
3. SQL Mode和迁移
4. SQL 注入
5. 开发过程中如何避免SQL注入
SQL优化
1.通过 show status 命令了解各种 SQL 的执行频率
2. 定位执行效率较低的 SQL 语句
3. 通过 EXPLAIN 分析低效 SQL 的执行计划
4. 确定问题并采取相应的优化措施
5. 索引问题
6.定期分析表和检查表
7.定期优化表
8.常用 SQL 的优化
MySQL数据库对象优化
1. 优化表的数据类型
2 散列化
3 逆规范化
4 使用中间表提高统计查询速度
5. 影响MySQL性能的重要参数
6. 磁盘I/O对MySQL性能的影响
7. 使用连接池
8. 减少MySQL连接次数
9. MySQL负载均衡
MySQL集群
MySQL管理和维护
MemCache
Redis
在Java项目中使用MemCache和Redis
第五阶段:操作系统/Linux、云架构
Linux安装与配置
1、安装Linux至硬盘
2、获取信息和搜索应用程序
3、进阶:修复受损的Grub
4、关于超级用户root
5、依赖发行版本的系统管理工具
6、关于硬件驱动程序
7、进阶:配置Grub
系统管理与目录管理
1、Shell基本命令
2、使用命令行补全和通配符
3、find命令、locate命令
4、查找特定程序:whereis
5、Linux文件系统的架构
6、移动、复制和删除
7、文件和目录的权限
8、文件类型与输入输出
9、vmware介绍与安装使用
10、网络管理、分区挂载
用户与用户组管理
1、软件包管理
2、磁盘管理
3、高级硬盘管理RAID和LVM
4、进阶:备份你的工作和系统
5、用户与用户组基础
6、管理、查看、切换用户
7、/etc/...文件
8、进程管理
9、linux VI编辑器,awk,cut,grep,sed,find,unique等
Shell编程
1、 SHELL变量
2、传递参数
3、数组与运算符
4、SHELL的各类命令
5、SHELL流程控制
6、SHELL函数
7、SHELL输入/输出重定向
8、SHELL文件包含
服务器配置
1、系统引导
2、管理守护进程
3、通过xinetd启动SSH服务
4、配置inetd
5、Tomcat安装与配置
6、MySql安装与配置
7、部署项目到Linux
第六阶段:Hadoop生态系统
Hadoop基础
1、大数据概论
2、 Google与Hadoop模块
3、Hadoop生态系统
4、Hadoop常用项目介绍
5、Hadoop环境安装配置
6、Hadoop安装模式
7、Hadoop配置文件
HDFS分布式文件系统
1、认识HDFS及其HDFS架构
2、Hadoop的RPC机制
3、HDFS的HA机制
4、HDFS的Federation机制
5、 Hadoop文件系统的访问
6、JavaAPI接口与维护HDFS
7、HDFS权限管理
8、hadoop伪分布式
Hadoop文件I/O详解
1、Hadoop文件的数据结构
2、 HDFS数据完整性
3、文件序列化
4、Hadoop的Writable类型
5、Hadoop支持的压缩格式
6、Hadoop中编码器和解码器
7、 gzip、LZO和Snappy比较
8、HDFS使用shell+Java API
MapRece工作原理
1、MapRece函数式编程概念
2、 MapRece框架结构
3、MapRece运行原理
4、Shuffle阶段和Sort阶段
5、任务的执行与作业调度器
6、自定义Hadoop调度器
7、 异步编程模型
8、YARN架构及其工作流程
MapRece编程
1、WordCount案例分析
2、输入格式与输出格式
3、压缩格式与MapRece优化
4、辅助类与Streaming接口
5、MapRece二次排序
6、MapRece中的Join算法
7、从MySQL读写数据
8、Hadoop系统调优
Hive数据仓库工具
1、Hive工作原理、类型及特点
2、Hive架构及其文件格式
3、Hive操作及Hive复合类型
4、Hive的JOIN详解
5、Hive优化策略
6、Hive内置操作符与函数
7、Hive用户自定义函数接口
8、Hive的权限控制
Hive深入解读
1 、安装部署Sqoop
2、Sqoop数据迁移
3、Sqoop使用案例
4、深入了解数据库导入
5、导出与事务
6、导出与SequenceFile
7、Azkaban执行工作流
Sqoop与Oozie
1 、安装部署Sqoop
2、Sqoop数据迁移
3、Sqoop使用案例
4、深入了解数据库导入
5、导出与事务
6、导出与SequenceFile
7、Azkaban执行工作流
Zookeeper详解
1、Zookeeper简介
2、Zookeeper的下载和部署
3、Zookeeper的配置与运行
4、Zookeeper的本地模式实例
5、Zookeeper的数据模型
6、Zookeeper命令行操作范例
7、storm在Zookeeper目录结构
NoSQL、HBase
1、HBase的特点
2、HBase访问接口
3、HBase存储结构与格式
4、HBase设计
5、关键算法和流程
6、HBase安装
7、HBase的SHELL操作
8、HBase集群搭建
第七阶段:Spark生态系统
Spark
1.什么是Spark
2.Spark大数据处理框架
3.Spark的特点与应用场景
4.Spark SQL原理和实践
5.Spark Streaming原理和实践
6.GraphX SparkR入门
7.Spark的监控和调优
Spark部署和运行
1.WordCount准备开发环境
2.MapRece编程接口体系结构
3.MapRece通信协议
4.导入Hadoop的JAR文件
5.MapRece代码的实现
6.打包、部署和运行
7.打包成JAR文件
Spark程序开发
1、启动Spark Shell
2、加载text文件
3、RDD操作及其应用
4、RDD缓存
5、构建Eclipse开发环境
6、构建IntelliJ IDEA开发环境
7、创建SparkContext对象
8、编写编译并提交应用程序
Spark编程模型
1、RDD特征与依赖
2、集合(数组)创建RDD
3、存储创建RDD
4、RDD转换 执行 控制操作
5、广播变量
6、累加器
作业执行解析
1、Spark组件
2、RDD视图与DAG图
3、基于Standalone模式的Spark架构
4、基于YARN模式的Spark架构
5、作业事件流和调度分析
6、构建应用程序运行时环境
7、应用程序转换成DAG
Spark SQL与DataFrame
1、Spark SQL架构特性
2、DataFrame和RDD的区别
3、创建操作DataFrame
4、RDD转化为DataFrame
5、加载保存操作与Hive表
6、Parquet文件JSON数据集
7、分布式的SQL Engine
8、性能调优 数据类型
深入Spark Streaming
1、Spark Streaming工作原理
2、DStream编程模型
3、Input DStream
4、DStream转换 状态 输出
5、优化运行时间及内存使用
6、文件输入源
7、基于Receiver的输入源
8、输出操作
Spark MLlib与机器学习
1、机器学习分类级算法
2、Spark MLlib库
3、MLlib数据类型
4、MLlib的算法库与实例
5、ML库主要概念
6、算法库与实例
GraphX与SparkR
1、Spark GraphX架构
2、GraphX编程与常用图算法
3、GraphX应用场景
4、SparkR的工作原理
5、R语言与其他语言的通信
6、SparkR的运行与应用
7、R的DataFrame操作方法
8、SparkR的DataFrame
Scala编程开发
1、Scala语法基础
2、idea工具安装
3、maven工具配置
4、条件结构、循环、高级for循环
5、数组、映射、元组
6、类、样例类、对象、伴生对象
7、高阶函数与函数式编程
Scala进阶
1、 柯里化、闭包
2、模式匹配、偏函数
3、类型参数
4、协变与逆变
5、隐式转换、隐式参数、隐式值
6、Actor机制
7、高级项目案例
Python编程
1、Python编程介绍
2、Python的基本语法
3、Python开发环境搭建
4、Pyhton开发Spark应用程序
第八阶段:Storm生态系统
storm简介与基本知识
1、storm的诞生诞生与成长
2、storm的优势与应用
3、storm基本知识概念和配置
4、序列化与容错机制
5、可靠性机制—保证消息处理
6、storm开发环境与生产环境
7、storm拓扑的并行度
8、storm命令行客户端
Storm拓扑与组件详解
1、流分组和拓扑运行
2、拓扑的常见模式
3、本地模式与stormsub的对比
4、 使用非jvm语言操作storm
5、hook、组件基本接口
6、基本抽象类
7、事务接口
8、组件之间的相互关系
spout详解 与bolt详解
1、spout获取数据的方式
2、常用的spout
3、学习编写spout类
4、bolt概述
5、可靠的与不可靠的bolt
6、复合流与复合anchoring
7、 使用其他语言定义bolt
8、学习编写bolt类
storm安装与集群搭建
1、storm集群安装步骤与准备
2、本地模式storm配置命令
3、配置hosts文件、安装jdk
4、zookeeper集群的搭建
5、部署节点
6、storm集群的搭建
7、zookeeper应用案例
8、Hadoop高可用集群搭建
Kafka
1、Kafka介绍和安装
2、整合Flume
3、Kafka API
4、Kafka底层实现原理
5、Kafka的消息处理机制
6、数据传输的事务定义
7、Kafka的存储策略
Flume
1、Flume介绍和安装
2、Flume Source讲解
3、Flume Channel讲解
4、Flume Sink讲解
5、flume部署种类、流配置
6、单一代理、多代理说明
7、flume selector相关配置
Redis
1、Redis介绍和安装、配置
2、Redis数据类型
3、Redis键、字符串、哈希
4、Redis列表与集合
5、Redis事务和脚本
6、Redis数据备份与恢复
7、Redis的SHELL操作
㈥ 英伟达nvidia如何设置效果最好
鼠标在桌面单击右键,点击配置可交换NVIDIA卡。
㈦ volitate 原理
volatile保证多线程可见性,volatile修饰的变量不会引起上下文切换和调度
cpu缓存,cpu运算速度与内存读写不匹配,因为cpu运算速度比内存读写快的多
从主内存中获取或者写入数据会花费很长时间,现在大多数cpu都不会直接访问内存,而是访问cpu缓存,cpu缓存是cpu与主内存之间的临时存储器,容量小,交换速度快,缓存中的数据是内存中的一小部分数据,是cpu即将访问的。当cpu调用大量数据时候,就先从缓存中读取从而加快读取速度
按照读取顺序与cpu结合的紧密程度,cpu缓存分为
一级缓存:L1位于cpu内核旁边,是与cpu结合最为紧密的cpu缓存
二级缓存:L2分为内部和外部两种芯片,内部芯片二级缓存运行速度与主频相同,外部芯片二级缓存运行速度则只有主频的一半
三级缓存,只有高端的cpu才有
每一级缓存中所存储的数据都是下一级缓存中存储的数据的一部分
cpu要读取一个数据的时候,首先从一级缓存中查找,如果没有就从二级中查找,如果还没有就从三级缓存中或者是内存总进行查找,一般来说,每级缓存的命中率大概有0.8左右,也就是全部数据量的0.8可以在一级缓存中查到,只有剩下的0.2总数据量从二级缓存中或者是三级缓存或者是内存中读取
缓存行:缓存是分line的,一个段对应一个缓存行,是cpu缓存种可分配的最小存储单元,通常是64字节:当cpu看到一条读取内存的指令的时候,会把内存地址传递给一级缓存,一级缓存会检查它是否有这个内存地址对应的缓存段,如果没有就把整个缓存段从内存共或者更高级的缓存种加载进来。
cpu执行计算的过程为:程序和数据被加载到主内存中,指令和数据被加载到cpu缓存中,cpu执行指令将结果写入cpu缓存中,cpu缓存中的数据写回到主内存中,但是这种方式仅限于单核cpu的时候
如果服务器是多核cpu呢,
多核处理器中主内存核处理器一样是分开的,这时候,L3作为统一的高速缓存共享,处理器1拥有自己的L1 L2
这个时候当核0读取了一个字节根据局部性原理,与他相邻的字节同样会被读入核0的缓存中
核3也读取了同样的一个字节,根据局部性原理,与他相邻的字节同样会被读入到核3的数据中
此时,核0和核3的缓存中拥有同样的数据
核0修改了那个字节之后,被修改后那个字节被回写到了核0的缓存中,但是该信息并没有回写到主内存
当核3访问该数据的时候,造成该数据不同步
为了解决这个问题**,当一个cpu修改缓存中的字节的时候,**服务器中其他cpu的会被通知他们的缓存将是为无效,这样核1在修改缓存中的数据的时候,核3会发现自己的缓存中的数据已经无效,核0将自己的写回到主内存中,然后核3将重新读取该数据
将代码转化为汇编指令的时候发现在汇编指令add之前有一个lock指令,lock指令就是关键。
lock指令的作用:在修改内存的时候使用lock前缀指令调用加锁的读修改写操作,保证多处理器系统总处理器之间进行可靠的通讯
1.锁总线,其他cpu对内存的读写请求会被阻塞,直到锁释放,不过实际候来的处理器都采用了缓存缓存代替锁总线,因为总线开销过大,锁总线的时候其他cpu没办法访问内存
2.lock后的写操作会回写已经修改的数据,同时让其他cpu相关缓存行失效,从而重新从内存中加载最新的数据
3.不是内存屏障却能完成内存屏障的功能,阻止屏障两边的指令重排序
嗅探式的缓存一致性协议:所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线,缓存本身是独立的,但是内存是共享的。所有的内存访问都要进行仲裁,即同一个指令周期种只有一个cpu缓存可以读写数据。cpu缓存不仅在内存传输的时候与总线打交道,还会不断的在嗅探总线上发生数据交换跟踪其他缓存在做什么,所以当一个cpu缓存代表它所属的处理器读写内存的时候,其他的处理器都会得到通知(主动通知),他们以此使自己的缓存保存同步。只要某个处理器写内存,其他处理器就马上直到这块内存在他们的缓存段种已经失效。。
MESI协议是缓存一致性协议,在MESI协议中每个缓存行有四个状态,Modified修改的,表示这行数据有效,数据被修改了和内存中的数据不一致,数据只存在当前cache中,Exclusive独有的,这行数据有效,数据和内存中的数据一致,数据只存在在本cache中,Shared共享的,这行数据有效,数据和内存中的数据一致,数据存在很多cache中,Invalid这行数据无效,这里的Invalid shared modified都符合我们的嗅探式的缓存一致性协议,但是Exclusive表示独占的,当前数据有效并且和内存中的数据一致,但是只在当前缓存中,Exclusive状态解决了一个cpu缓存在读写内存的之前我们要通知其他处理器这个问题,只有当缓存行处于Exclusive和modified的时候处理器才能写,也就是说只有在这两种状态之下,处理器是独占这个缓存行的,当处理器想写某个缓存行的时候,如果没有独占权就必须先发送一条我要独占权的请求给总线,这个时候会通知处理器把他们拥有同一缓存段的拷贝失效,只要在获得独占权的时候处理器才能修改数据并且此时这个处理器直到这个缓存行只有一份拷贝并且只在它的缓存里,不会有任何冲突,反之如果其他处理器一直想读取这个缓存行(马上就能直到,因为一直在嗅探总线),独占或已修改的缓存行必须要先回到共享状态,如果是已经修改的缓存行,还要先将内容回写到内存中。
volatile变量的读写
工作内存其实就是cpu缓存,当两条线程同时操作主内存中的一个volatile变量时候,A线程写了变量i,此时A线程发出lock指令,发出的lock指令锁总线或者锁缓存行,同时线程b的高速缓存中的缓存行内容失效,线程A想内存中回写最新的i。当线程B读取变量的时候,线程发现对应地址的缓存行被锁了等待锁释放,锁的一致性协议会保证它读取到最新的值。㈧ oracle 11g rac 哪个版本能装在2012
RAC
在Grid Infrastructure安装完以后,我们把注意力转移到集群上的Oracle软件的安装上来。我们看到,Grid Infrasctructure提供了运行RAC的框架,包括集群通讯链接、节点分离、节点成员关系等服务。ASM是Oracle存储数据库的首选方式。RAC利用这些概念并扩展了需要的基本服务。
安装选项
成功安装了Grid Infrastructure/Clusterware以后,Oracle Universal Installer检测到集群环境的建立,然后提供安装整个集群上或是用户指定其中几个节点的RAC选项。使用集群检验工具cluvfy来为RDBMS的安装检测是否满足先决条件是良好的做法。和安装集群一样,Oracle Universal Installer将首先在第一个节点上对软件进行拷贝和链接,然后将Oracle主目录push到指定的其他节点中。和Grid Infrastructure不同的是,Oracle RDBMS可以被安装在共享文件系统上(例如OCFS2或ACFS上),在集群中增加新节点被简化,因为不需要在新的节点上重新安装软件,打补丁同样被简化了--只有一个Oracle主目录需要打补丁。但是补丁不能以rolling方式安装,因此停机时间不可避免。
在安装过程中,Oracle Universal Installer将提醒管理员安装或升级数据库,或只安装软件。如果安装的时候有新的版本发布,那么仅仅安装软件,打补丁升级后再创建数据库是比较好的做法。
单实例和RAC数据库
RAC和单实例数据库在很多重要方面都有所不同。
在RAC中,一个数据库在共享存储中为多个服务器上的实例所访问。数据库文件、在线redo文件、控制文件和服务器参数文件(spfile)都必须共享。此外,闪回日志、已归档的redo日志、数据泵转储文件、和dataguard broker配置文件也可以共享,根据你的配置来决定(这是可选的,但还是强烈建议这么做)。在使用ASM的时候,你同样可以在每个RAC的节点中找到一个本地的pfile文件,这个文件指向对应磁盘组中的spfile。另一个存储在本地的文件是Oracle密码文件。集群文件系统中的用户常常把这些文件放在一个共享的位置,通过符号链接指向$ORACLE_HOME/dbs数据库文件
数据库文件包含数据库中的所有数据,包括表、索引、数据字典和经过编译的PL/SQL代码,不胜枚举。在RAC中,每个数据文件都只有一个拷贝,位于共享存储中,并为所有实例所访问。Oracle默认不为数据文件提供镜像,大部分用户选择在存储层面来做冗余,避免介质故障导致的数据丢失。在存储阵列没有这个功能时,可以使用Oracle ASM来提供冗余。
控制文件
控制文件储存数据库的物理结构的相关信息,包括它们的状态。如果你使用RMAN且没有专门的RMAN catalog数据库,控制文件中也可以储存RMAN备份的信息。在单实例数据库和RAC中,控制文件应该做镜像以防止损坏或存储故障。当同时使用ASM和闪回恢复区时,会自动做多路复用。默认情况下,Oracle在db_create_file_dest和db_recovery_file_dest指定的磁盘组中对控制文件做多路复用。这种情况下,若你使用spfile,control_files参数将自动更新。要知道控制文件会成为RAC中的一个争用点,因为它们会被频繁更新。因此不要对控制文件做过多的镜像拷贝,而且应该把它们放置在高速存储上。
REDO和归档
在RAC中,每个实例有它自己的联机日志文件,称为线程(thread)。线程的信息可以在V$LOG和相关的视图中查看。
每个线程中你需要两组redo日志,而且若没有使用ASM和闪回恢复区,你应该考虑手动对组中的成员做多路复用。由spfile负责实例和线程间的映射(通过初始化参数thread)。当添加一个新的实例到集群中时,就需要一个相应的redo线程,这可以使用两种方式来做到:第一种,执行SQL语句alter database add logfile group x thread y; 第二种,在使用策略管理的数据库(policy-managed)中,会自动创建。然后由Oracle来启用。
lgwr后台进程将redo buffer刷新到redo log中。online redo log需要放在高速存储中,否则它可能会成为一个争用的点,特别是在一个高频率提交的系统中。通常对设计不合理的应用的优化是减少commit的频率,并至少将redo log和控制文件移到高速存储中,以减少一些性能瓶颈。在日志切换频繁的系统中,增加每个线程的重做日志组数会有所帮助,它能给归档进程更多的时间来归档redo日志。这种方法在归档进程需要将归档的redo传送到standby数据库中时也能获益,但是,现在的大部分系统采用Log Network Service(LNSn)进程来异步传送redo给standby数据库的Remote File Server(RFS)进程。在Oracle 10.2和11.1中,每个destination有一个LNS进程,到了11.2,LNSn进程被NSSn何NSAn后台进程所代替。NSSn进程被用来同步传送redo,NSAn用来异步传送redo。redo log大小设置的原则是,日志切换不会太频繁(AWR和statspack能够帮助定义一个合适的大小)。Oracle 11.2还允许管理员来选择redo log的块大小,现代存储单元使用4kb扇区大小代替了原先的512b。
当RAC中的一个实例发生故障,所有线程被合并来帮助建立恢复集,由服务器监控进程来执行前滚或回滚操作。
在lgwr进程将一个redo log写满以后,其中一个归档进程会将该文件拷贝到指定的目录中。
闪回恢复区在Oracle 10.1中引入,看是来是归档日志的最佳存放位置。如果你没有使用闪回恢复区,建议将归档日志放在一个共享文件系统中,以便每个节点都可以访问到。与单实例数据库不同,RAC需要所有线程的归档日志。当一个实例执行介质恢复时,你可以从它的alter日志上看到,Oracle使用了每个线程的所有日志文件。
Undo表空间
和redo线程类似,每个集群数据库的实例由它自己的undo表空间。spfile中配置了实例和undo表空间之间的一对一映射关系。但这个映射并不代表该undo表空间就长期绑定在该实例上,所有的其他实例同样可以访问该undo表空间来创建块的读一致前镜像。
当添加一个实例到集群中时,需要添加新的undo表空间并映射到该实例,和redo log一样。在policy-managed数据库中,Oracle可以自己来做这件事。
虽然还是可以使用手动的undo管理,但是强烈建议使用自动undo管理(AUM)。
RAC数据库的存储选项
管理员可以在如下的选项中选择:
ASM 这是Oracle的首选存储选项,而且是RAC标准版中支持的唯一配置OCFS2
裸设备 不推荐使用,不仅是因为被新版的linux内核弃用,在Oracle 11.2中同样不支持网络文件系统(NFS)
Red Hat Global File System 只在红帽和Oracle Enterprise Linux中支持,可以用在闪回恢复区和数据库文件上RAC实例
一个RAC数据库包含2个或更多的实例,一般每个实例都在不同的节点上,由一些共享内存结构和后台进程组成。
每个实例都有自己的SGA,在实例启动的时候分配。Oracle在10g中引入了自动共享内存管理(ASMM),在11g中引入了自动内存管理(AMM)。但是AMM与linux的大页面不兼容,这对大内存的系统来说是个问题。
Oracle需要同步访问本地共享内存和整个集群。所有实例都能访问其他实例的SGA。
在RAC中Oracle内核对共享内存的保护措施和单实例中是一样的,同样使用了闩和锁。闩是一个低级别、轻量级的串行装置。请求闩的进程不会排队,如果进程不能获得闩,它就会进入spin状态。spin的意思是,这个进程会进入一个紧密循环来预防被操作系统的调度程序从CPU中取下。如果一个进程长时间得不到闩,它会进入睡眠,在一个时间间隔后再次尝试申请。闩是实例级别的,没有集群范围的闩。
另一方面,锁在更长的时间请求一次,比闩更为复杂。锁可以是共享或独占的,请求锁的进程以先进先出(FIFO)的机制来等待,由队列来控制锁的访问,这个队列是集群范围内的。
缓存一致性的需求意味着锁和闩在RAC中比单实例要更加复杂。和单实例中一样,对buffer cache中数据库的访问和队列必须在本地实例中管理,但是,远程实例的访问也需要管理。因为这个原因,Oracle使用全局资源目录(GRD)和一些额外的后台进程。
(Oracle将V$视图加上实例标识组合起来形成GV$视图,一个GV$视图包含了集群中所有实例的动态性能视图)全局资源目录 (GRD)
RAC中使用了一些附加的后台进程来做缓存间的同步——记住RAC使用cache fusion结构来模拟一个横跨集群内所有节点的全局SGA。访问buffer cache中的块需要在读一致和写的访问间进行协调,共享资源的队列现在也是在集群全局上的。全局缓存服务(Global Cache Service GCS)用来对公共buffer cache的访问,全局队列服务(Global Enqueue Service GES)用来管理集群中的队列。
GCS和GES对应用而言都是透明的。内部使用的原结构就是先前提到的GRD,由GCS和GES进程来维护。GRD分布在集群的所有节点上,是SGA的一部分,这就是为什么一个RAC数据库的SGA比同等情况下的单实例数据库要来得大。资源管理由GCS和GES来协商。特定的资源完全由一个实例来管理,这个实例就是resource master。但它并是不固定的,Oracle 9.2以后的版本实现了动态的资源管理(DRM),在9.2以前,资源的remastering只发生在实例故障、GRD重建的时候。新的版本中,如果Oracle检测到一个resource master以外的实例在一个给定的时间间隔中对一个特定的资源的访问过于频繁,就会发生resource mastering。在这种情况下,该资源就会被remaster到其他节点上,也就是说,频繁访问该资源的另一个节点将成为resource master。很多用户反馈了动态remastering的一些问题,当它过于频繁发生的时候会造成一些不必要的开支。这种情况下,可以禁用DRM。
(GRD还记录了哪些资源由哪些实例来管理,当一个实例发生故障时,恢复起来将非常方便)下图说明GCS如何与GES协同工作来维护GRD
全局缓存服务(GCS)
LMSn后台进程使用GCS在全局buffer cache中维护缓存的一致性,SGA中可以存在同一个数据块的多份拷贝(当前版本只有一个),GCS对数据块的状态和位置进行跟踪,并通过内部连接将块传输到其他节点的实例中。
全局队列服务(GES)
和GCS类似,GES工作在块级别,管理集群中的全局队列。根据经验,如果一个操作没有涉及在全局buffer cache中控制/移动数据块,那么很可能是经过了GES的处理。全局队列服务负责所有的实例中的资源操作,比如对数据字典和库缓存的访问或事务的全局管理。它同样可以检测集群中的死锁。它跟踪多个实例同时访问资源时Oracle队列机制的状态。全局队列服务监控(LMON)和全局队列服务后台进程(LMD)组成全局队列服务的一部分。锁进程LCK0负责无缓存方式的访问,比如library和row cache请求。
缓存融合(Cache Fusion)
缓存融合是实例间数据传送的最新演变。Oracle 8i中曾使用block ping的机制,作为替代,Oracle使用高速的内部连接来为所有节点间的读写转移数据块。
实例间使用block ping方法来转移数据块代价是很昂贵的,它建议你将工作量与实例联系在一起来使实例间的数据块转移达到最少。在Oracle并行服务器(Oracle Parallel Server)中,当一个实例请求一个数据块来进行修改,而这个数据块当前正由别的实例所持有,它将发出一个信号给持有该块的实例,使其将块写入到磁盘,再发回该块已经可读的信号。这种方法的通讯和对磁盘的读写操作的量很不尽如人意。
缓存融合的块转移依赖于全局资源目录,不需要超过3次跳跃(hop),这与安装及节点的个数有关。很明显,如果一个集群只有两个节点,那么有一个双向的缓存转移。如果多于2个节点,将跳跃数限制在3次是必要的。Oracle使用专用的等待事件来衡量涉及缓存的通讯,根据实际情况来决定做一个双向或是三向的缓存转移。
当一个实例通过缓存融合来请求一个数据块时,它首先与资源的master联系来确定这个资源的当前状态,如果这个资源没有正在被使用,那么它可以通过从本地磁盘读取以获得这个块。如果这个资源正在被使用,那么该资源master将把这个资源传递到发出请求的实例中。如果这个资源紧接着收到1个或多个实例的修改请求,将会把该资源添加进GRD,资源的master、请求者和持有者都可以不同,这种情况下最多需要三次跳跃来获得这个块。
上面提到的双向和三向的块转移与资源的管理方式有关。当资源的master持有被请求的资源,那么对数据块的请求就能马上得到满足并开始块的传输,这就是双向的通讯。在三向的情况中,请求者、master和持有者都不相同,那么该资源master需要转发这个请求,引发了新的跳跃。
从刚才的讨论中,你可以知道在全局buffer cache中对块和它们影像间协调是不能低估的。在RAC数据库中,缓存融合常常代表了最大的利益和最高的成本。好处是缓存融合理论上运行按比例增大,并可能取得近乎线性的扩展性。然而,缓存融合强加的额外工作量可能会在10%-20%的范围内。
读一致性
Oracle数据库的主要特征之一就是能同时提供不同视野下的数据,这个特征叫做多版本读一致。查询是读一致的,写不会阻塞读,反之亦然。当然,多版本读一致对RAC一样有效,但涉及到一点其他的工作。
System Change Number(SCN)是一个Oracle的内部时间戳,对读一致非常重要。如果本地实例请求一个块的读一致版本,它需要联系这个块的resource master来确定这个块是否有相同SCN的版本,或者更新的版本存在于某个远程实例的buffer cache中。如果这个块存在,那么resource master会发送请求给相应的远程实例来转发这个块的读一致版本给本地实例。如果远程实例持有这个块的请求时间SCN的版本,那么它会马上发送这个块。如果远程实例持有的是这个块更新的版本,它将创建这个块的拷贝(称为前镜像),并对这个拷贝应用回滚使其回到对应的SCN的状态,然后将其通过内部连接发送。
System Change Number(SCN)
SCN是Oracle数据库产生和使用的内部时间戳,数据库中发生的所有事件都以SCN标记,事务也一样。Oracle的读一致严重依赖于SCN和undo表空间中的信息。SCN需要在集群中同步,RAC中用来使SCN在所有节点间通用的方案有两个:broadcast-on-commit和Lamport。
broadcast-on-commit是10.2以后的默认方案,它解决了Lamport方案的一个问题:在以前,这个默认方案是Lamport,它承诺了更好的扩展性,让SCN像集群其他通讯一样来传播,但并不是在一个节点中commit后马上发生。这在大部分情况下都能满足要求,但是,Lamport方案有一个问题:一个节点的SCN相对另一个节点的SCN有延时是可能的,特别是在在消息传送不活跃的时候。这种SCN的延时意味着在一个节点上提交的事务从另一个延时的实例上“看起来”会有点太新了。
另一方面,broadcast-on-commit方案更加资源集中一点。LGWR进程在每次commit之后更新全局的SCN并将其广播到所有的其他实例中。在RAC11.1中,初始化参数max_commit_propagation_delay允许数据库管理员来修改默认的设定,这个参数在11.2中被移除。㈨ 为何Redis用乐观锁,而MySQL数据库却没有
简单来说,Redis使用乐观锁,相对于悲观锁,在实现中更加简单,在某些场景中的性能也更好。Redis作为一个轻量级的、快速的缓存引擎,而不是一个全功能的关系型数据库,既没有使用悲观锁的必要,也难以承受使用悲观锁的成本。
详细来说,要深入到Redis和MySQL的事务处理机制。Redis关于事务的文档见此:
Transactions(事务)
Redis对于事务只提供了非常有限的支持,其实更多地是试图绕过问题。
首先,Redis对于同一事务中的一组操作,而不是立即执行,而是放入一个queue中,当执行到EXEC时,再一起执行。事务执行是全局独占的,也就是同一时间只有一个事务被执行,中途不能被其它事务打断。Redis用这种最简单的、也是性能最差的方式避免了race
condition。
其次,在Redis的事务中,如果有一个或多个操作失败,其它操作仍然会成功,也就是说它根本没有回滚机制。
这种方式会带来很多严重的问题,其中之一是,无法先读取某个数值后再进行依赖这个值的操作,因为放在一个事务里会被在同一个瞬间执行,不放在同一个事务里又会导致race
condition。解决方法是使用WATCH,它会监视一个或多个变量,如果变量的值在调用WATCH以后和事务提交之前被别的事务修改过了,整个事务都会失败。这类似于操作系统中的CAS(Compare
and
Set)。我不知道WATCH具体是怎么实现的,但是我推测它监控了指定变量的版本号。
即使有了WATCH,Redis的事务也是受到严重限制的。第一,它没有实现读数据时的一致性,因为WATCH对于读操作不起作用。第二,它不支持回滚。第三,在对同一变量存在大量并发写操作时,性能会非常差,因为每次提交事务时,WATCH监控的变量都已经被修改了,导致事务将多次提交失败。但是,Redis本来就是一个KV类型的缓存引擎,要处理的是大量读少量写的场景,对一致性也没有要求。
MySQL就完全不一样了,作为一个典型的关系型数据库,它需要完整地实现ACID,所以Redis的方式是解决不了它的问题的。
MySQL中的MVCC机制(Oracle的也是),通过undo
日志来获取某个行记录的历史快照,从而实现了所谓的读一致性。它的目的是读取某个时间点上的历史数据(而不是可能已经被修改了的数据),而不是避免悲观锁的使用。严格地说这不能称之为乐观锁,因为它既不Compare当前版本和历史版本,也不进行Set。事实上,在读取记录的历史快照时,当前记录有可能(由于并发的写操作)已经被加上独占锁。
进一步的问题是:有没有可能使用乐观锁来实现RDBMS中的写一致性?有没有可能使用乐观锁实现完整的ACID特性?
回答是可以。例如,MS
SQL
SERVER的Hekaton引擎通过一套基于时间戳的多版本管理系统,实现了不使用了悲观锁的ACID。
但是,这并不意味着乐观锁必然优于悲观锁。除了维护多版本的开销以外,乐观锁无法避免的一个问题是,当多个写操作试图更新同一个对象时,只有第一个操作可以成功,其它的操作都会在Compare时失败然后回滚,从而造成极大的性能问题。在这种情况下,乐观锁的性能会低于悲观锁。
目前的趋势似乎是,大规模的分布式数据库更倾向于使用乐观锁来达到所谓的external
consistency,因为基于传统悲观锁的分布式锁在集群大到一定程度以后(从几百台扩展到成千上万台时),性能开销就大得无法接受了。Google的Spanner就是基于乐观锁。当然这完全是另外一个问题了。㈩ 什么是云存储技术
云存储是在云计算(cloud computing)概念上延伸和发展出来的一个新的概念,是指通过集群应用、网格技术或分布式文件系统等功能,将网络中大量各种不同类型的存储设备通过应用软件集合起来协同工作,共同对外提供数据存储和业务访问功能的一个系统。 当云计算系统运算和处理的核心是大量数据的存储和管理时,云计算系统中就需要配置大量的存储设备,那么云计算系统就转变成为一个云存储系统,所以云存储是一个以数据存储和管理为核心的云计算系统。
云存储的两个层面
云存储的两个层面是作为云计算支撑的存储计算,主要涉及分布式存储(如分布式文件系统、IPSAN、数据同步、复制)、数据存储(如重复数据删除、数据压缩、数据编码)和数据保护(如RAID、CDP、快照、备份与容灾)等技术领域,如图8-30所示,这在第6章中已有所介绍。和云安全技术一样,云存储技术也需要利用现有的所有存储技术针对云计算三层架构的各个环节采用适当的存储技术,才能取得最佳效果,例如,对应不同需求,有时应该使用数据库技术但有时却应该使用LDAP技术,有些性能要求高的系统不能使用SAN或NAS,需直接使用基于RPC或Socket技术的并发文件系统,有些应用使用SAN成本太高等,这里不再做深入介绍。如图8-31所示是一个采用NetApp FAS、思科 UCS和 VMWare vShpere 4技术打造的存储系统,对上述两种云存储技术和应用都有促进作用。云存储架构
分为两类:一种是通过服务来架构;另一种是通过软件或硬件设备来架构。
传统的系统利用紧耦合对称架构,这种架构的设计旨在解决HPC(高性能计算、超级运算)问题,现在其正在向外扩展成为云存储从而满足快速呈现的市场需求。下一代架构已经采用了松弛耦合非对称架构,集中元数据和控制操作,这种架构并不非常适合高性能HPC,但是这种设计旨在解决云部署的大容量存储需求。各种架构的摘要信息如下:
紧耦合对称(TCS)架构
构建TCS系统是为了解决单一文件性能所面临的挑战,这种挑战限制了传统NAS系统的发展。HPC系统所具有的优势迅速压倒了存储,因为它们需要的单一文件I/O操作要比单一设备的I/O操作多得多。业内对此的回应是创建利用TCS架构的产品,很多节点同时伴随着分布式锁管理(锁定文件不同部分的写操作)和缓存一致性功能。这种解决方案对于单文件吞吐量问题很有效,几个不同行业的很多HPC客户已经采用了这种解决方案。这种解决方案很先进,需要一定程度的技术经验才能安装和使用。
松弛耦合非对称(LCA)架构
LCA系统采用不同的方法来向外扩展。它不是通过执行某个策略来使每个节点知道每个行动所执行的操作,而是利用一个数据路径之外的中央元数据控制服务器。集中控制提供了很多好处,允许进行新层次的扩展:
● 存储节点可以将重点放在提供读写服务的要求上,而不需要来自网络节点的确认信息。
● 节点可以利用不同的商品硬件CPU和存储配置,而且仍然在云存储中发挥作用。
● 用户可以通过利用硬件性能或虚拟化实例来调整云存储。
● 消除节点之间共享的大量状态开销也可以消除用户计算机互联的需要,如光纤通道或infiniband,从而进一步降低成本。
● 异构硬件的混合和匹配使用户能够在需要的时候在当前经济规模的基础上扩大存储,同时还能提供永久的数据可用性。
● 拥有集中元数据意味着,存储节点可以旋转地进行深层次应用程序归档,而且在控制节点上,元数据经常都是可用的。