QPS的提升之路

QPS是指每秒钟能够处理的请求数,通常用来衡量单个接口的处理能力。在分布式系统中一个事务往往会有多个请求,所以,TPS则用作衡量系统整体处理能力。

说明

QPS是指每秒钟能够处理的请求数。如果一次请求处理耗时1ms,一秒的QPS为1000。PV指页面被访问的次数,TPS指每秒的事务数量,TPS与并发数不同之处在于,前者为单位时间内的处理量,后者为同一时间下的处理量。

1000QPS通常是一个分水岭,纯粹的单机应用很难处理这个级别,应用横向扩展是常见做法;单机的关系型数据库对这种级别的QPS也尽显疲态,通常会引入缓存和NoSQL,单机NoSQL的QPS通常能达到万级,再大就需要做横向扩展,NoSQL的横向扩展也不是没有极限,所以,通常会在架构层面引入多级缓存。

QPS定义

QPS = 1000ms/RT

决定QPS关键因素RT(响应时间),暗含两种关键优化点,(线程)等待时间和执行时间。

应用层面QPS的提升

应用层面提升QPS最终落在了,降低执行链路所用的时间,以及如何提高CPU利用率这两个点上。

1.降低链路所用的时间加入缓存最为直接,对于计算型的处理则可以采用分段并行计算。

2.提高CPU利用率则集中在I/O解决方案上,避免I/O阻塞引发的CPU利用率问题。

SEDA思想

SEDA思想是将任务分割成对独立的多个阶段,每个阶段都有独立的一组线程负责,每个阶段之间使用队列进行交互。如undertow、netty这类高性能服务器和通信框架都采用了这种设计。

Disruptor无锁设计提升TPS

SEDA提高了执行效率,但所使用的交互队列在出栈入栈时,免不了要使用锁机制,Disruptor无锁设计从一定程度上避开了这个问题。Apache Storm、Log4j2等框架使用Disruptor在性能方面都得到了较大的提升。

Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,初衷为了解决内存队列的延迟问题(基于Disruptor开发的系统单线程能支撑每秒600万订单)。Disruptor是一个BlockingQueue,目的是让同一个处理器上的不同线程可以相互的移动数据(message or event)。相对于一般的queue,Disruptor具有一些重要的特性:为事件预分配内存、根据消费依赖路径多路发送事件给消费者,并有可选的lock-free。

Disruptor无锁设计的核心

Disruptor的RingBuffer之所以能够做到完全无锁,重点在于“单线程写“,每个生产者或消费者线程,会先申请可操作的数组下标,申请得到之后,直接在该位置写入或者读取数据。这个过程是一个get-and-increment操作,如:当一个线程获取到一个可用下标后,同时会把下一个位置定义为可用下标,这个过程是基于CAS的操作。

Disruptor特性

Disruptor使用一个环形数组做为队列,避免了垃圾回收,并且数组对处理器的缓存机制也更为友好。

队列元素定位

数组的长度为2^n,下标采用递增方式,通过位运算快速定位,不必担心下标越界的问题。如,Seq & (QueueSize - 1) ,当QueueSize大小为8,Seq为10,通过计算二进制1010 & 0111 = 2,直接获取index=2的元素。

无锁设计

每个生产者或消费者线程,会先申请可操作元素所在数组中的位置,申请得到之后,直接在该位置写入或者读取数据。

缓存优化

Disruptor通过增加补全来确保ring buffer的序列号,不会和其它数据同时存在于一个缓存行中。详情请查看内存对齐对性能的影响

数据库层面QPS提升

解决数据库查询压力

单机数据库QPS有限,通常借助主从读写分离暂时解决来自读取方面的压力,随即会引发数据延迟问题,对于延迟时间通过show slave status查看Seconds_Behind_Master列的值便一目了然。对主从延迟解决,一般会从加大数据缓存和解决I/O利用率方面的参数入手,如:调整innodb_buffer_pool_size,使用SSD、磁盘阵列、使用物理主机等等。

解决数据写入压力

数据库垂直拆分在分布式系统中尤为常见,能够分摊一部分数据库写入操作的压力,同时,应用对数据库的操作也会受到相应的限制,如联表查询,多表更新。

来自单表的写入压力往往成了痛点,引入水平分库显得很有必要,水平分库通常可以按业务数据拆分,如,按地区,也可以根据主键拆分;前者,数据具有很强的聚合能力,后者,数据分布会比较均匀。

根据主键做水平拆分首要解决ID生成问题,可以借鉴Instagram的ID生成算法,前36位使用时间戳,保证ID是升序递增(能够影响MySQL的性能),中间13位为分库标识,后15位是自增序列(由各个库自行维护的自增序列,避免并发问题)。

结束语

QPS的优化涉及到应用的方方面面,毕竟大部分情况下,我们都不准备重写一个服务器或某一协议,所以,从应用和数据库层面对QPS优化做一个简单的介绍。