2017QCon分享:从淘宝到云端的高可用架构演进

写在前面:由于QCon现场分享时间只有40分钟,所以和最初的slides相比后来缩减了2/3,因此下文可能看起来会比较『跳跃』。


大家好,我今天分享的题目是《高可用实践:从淘宝到上云的差异》,取这个标题是因为会涉及到两个方面内容,一方面以淘宝为例子,传统的IDC的时候,我们稳定性是怎么做的,另外在云计算背景下,有很多创业公司是基于阿里云这样的公有云基础设施做研发,在公有云的环境下怎么做好我们系统的高可用。 继续阅读

流水线中的大冒险

上一节说到,流水线中的分支预测本身是为了提高整条流水线的并行度,为此,CPU做了很多努力,例如乱序执行,甚至于流水线本身也是为了这个目的而诞生的。

和我们编写一般程序一样,顺序执行总是最简单、最安全的,指令被一条接着一条地顺序执行,没有人会思考任何有关并发的隐患。但是一旦踏入并发编程的范畴,似乎就开始变得一团糟,你需要考虑数据竞争、锁、内存等等一系列问题。和分支预测一样,有时候你会采用一些试探性的方法去处理并发中产生的问题,例如经典的CAS(Compare And Swap)算法,可能成功可能失败,喜忧参半。

在流水线中也存在着类似的大冒险,典型的有三种:

数据冒险

结构冒险

控制冒险

对应的,也有一些方法去辅助CPU在这些冒险的过程中,尽可能地达到我们期望的结果。 继续阅读

流水线中的分支预测

在现代CPU中,为了提高执行的性能,CPU的多个单元会同时执行多条指令。例如当取址单元正在寻找下一条指令前,上一条指令的译码和执行已经在进行中了,这一套机制被称作CPU流水线(pipeline)。

CPU流水线架构把指令的执行分为了多个阶段,每个单元只负责完成指令执行过程中的一个阶段,而中间结果由专门的流水线寄存器暂存。这样理论上,一条指令的执行假设被分为5个阶段,那么当5个单元同时运行一段时间后,理论上相同时间可以同时执行5条指令,当然这只是最简单的情况,实际的情况要复杂得多。

400px-5_Stage_Pipeline.svg

流水线的引入相当于程序中引入了并发,相应的,会带来很多额外的问题。例如为了更好地让指令流水般地执行,不涉及顺序一致性的指令会被重排序。这里不详细讨论太多流水线的技术细节,只要知道指令并不是一条一条顺序执行的,那样会严重阻碍处理器的性能。

CPU流水线引入的目的在于,希望能够在每个CPU的时钟周期都发射一条新的指令,这样理论上可以达到最高效率。但这有一个前提:如果指令的执行是每个时钟周期一条,那么指令的取值也必须达到每个时钟周期一条,如此,当你在取址阶段拿到要执行的指令时,下一条指令的地址必须被确定了,否则下一个时钟周期便无法取出对应的指令。 继续阅读

理解重排序

重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类:编译期重排序运行期重排序,分别对应编译时和运行时环境。

在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。

但是在单线程程序中,通常我们容易假设指令是顺序执行的,否则可以想象程序会发生什么可怕的变化。理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型。当然,这种假设本身是合理的,在实践中也鲜有异常发生,但事实上,没有哪个现代多处理器架构会采用这种模型,因为它是在是太低效了。而在编译优化和CPU流水线中,几乎都涉及到指令重排序。 继续阅读

高性能可伸缩系统构建的简要思想

为了支持高并发访问和海量数据的场景,在搭建应用时需要努力构建可伸缩的系统,这样在后期系统遇到瓶颈时可以简单地通过垂直伸缩或水平伸缩扩展系统。本文整理借鉴了林昊老师对构建可伸缩系统的文章。

一、垂直伸缩

垂直伸缩指通过增加或升级单台机器的硬件来获得对高并发访问和大数据量的更好支持。

1、支持高并发访问

可以增加CPU和内存,同时需要对系统进行一定调整,尽可能使得软件性能随硬件性能线性增长。

增加CPU时,需要进行如下调整:

首先需要减小锁的开销。当Java程序中线程比较多时,线程的上下文切换开销会增大,对锁的竞争也会变得更激烈,所以通常意义上线程并不是越多越好。当系统因为锁的竞争而导致CPU利用率较低时,可以采取以下策略减少锁带来的开销:

a、使用Java API中的并发类库

可以采用java.util.concurrent等包下面的并发类,通常它们已经经过了充分的优化,能有效地支持高并发环境下的操作,并发类中大量采用了非阻塞算法,有些利用了CAS实现无锁。这里有一个小提示:使用并发哈希表时应优先采用ConcurrentHashMap而不是Hashtable,前者通过分解锁的方法使得效率更高。 继续阅读