来自于Aerospike的技术人员Rajkumar Iyer最近在highscalability上发表了一篇博文,介绍了Aerospike在探索数据库扩展、实现1M TPS挑战的过程中所遵循的3个设计点和5个要避免的常见瓶颈。如果想要查看英文原文,可以点击这里:The Quest For Database Scale。
Rajkumar Iyer介绍说他们大约在1年之前就已经开始探索提高内存数据库性能的方法——在一台廉价的商用服务器上实现1MTPS。那个时候他们实现了支持约200K TPS的Aerospike,通过提升无缓存架构的延迟和吞吐量他们将性能提升到了500K TPS,并且发布了Aerospike 2.0社区版。在那之后他们通过内核调节技术发布了在5k$的硬件上实现1M TPS的方法。
进入2014年之后Aerospike依然在探索,他们的目标是在一台服务器上实现每秒钟1百万数据库事务,比之前的性能快2倍多。与Cassandra夸耀的在Google计算引擎中通过300多台服务器实现1百万TPS相比,Aerospike并没有对内核进行调整就实现了该目标。
运营这种强度的任何分布式数据库系统都必须在架构中牢记三个设计点:
1) 稳定性——通过简短、可理解的代码路径实现。保持所有的函数简洁优雅是确保整个系统平稳运行的重要途径,哪怕是在将多个这样的层堆叠在一起的时候。任何复杂难懂的逻辑都会让系统变得难以维护,应该或者最好尽可能地少用这种逻辑,不得不用的时候需要将其隔离。
2) 可预见性 ——通过使用C语言编写数据库并仅用少数外部类库来实现。预测代码的行为意味着你能够对系统正在做的事情进行精确、全面的控制,并且尽力确保所有的资源都得到了有效地利用。Aerospike并没有使用“libeio”、“libev”或者任何其他的类库处理网络。此外,Aerospike也没有使用内存映射——相反地,它调用读、写操作本身。因此虽然Java和Erlang这样的语言可能会缩短整体的开发时间,但是C依然是最佳选择。
3) 可扩展性——通过向上和向外扩展实现。按照旧的RDBMS的方式进行垂直扩展并不能满足所有的用例场景,也无法平滑地使用资源没有被充分利用的大规模向外扩展的集群。成本的增加和管理是一个挑战——即使是在最好的情况下一个分布式系统也永远不可能正确或者可预测,因为它会增长,错误率也会上升。平衡的方法是最好的。从一开始Aerospike就被设计为可以向上和向外扩展的,它能够最大化服务器利用率让每台服务器实现10倍于其他NoSQL数据库的事务处理速度,同时还允许热增加或者删除服务器。
另外,数据库不要孤立存在,它们必须作为整个堆栈的一部分。一个向外扩展的数据库必须考虑所有的集群功能,不要把负载推给应用,例如分片或者负载均衡。任何将复杂性转移给应用的数据库都不能算做是真正高可扩展运营系统的一部分。
为了成功地在一台服务器上实现超过百万的TPS,任何数据库系统都必须有效地避免5个常见的瓶颈。
1)网络开销
对任何客户端—服务器架构而言,第一个瓶颈就是在网络上移动数据包,从网卡移动到内核再移动到用户空间的开销。Aerospike之前的努力需要将irq绑定到NIC中的多个队列上进行负载均衡。在那之后,这个irqbalance变得更加智能,减少了Aerospike的工作。
英特尔数据面开发工具套件(DPDK)这样的选项完全消除了TCP栈的开销,让用户空间函数处理网络数据包。但是DPDK并不会处理TCP/IP,也不会处理大的请求,因此数据库系统必须实现自己的流控制。除非DPDK被支持并且被广泛地接受,否则优化通用数据库从而使用DPDK依然是将来的事情。
2) NUMA开销
第二个瓶颈是在NUMA区域之间移动数据的开销。理想情况下,数据库服务器应该是一个单线程的事件循环,不需要上下文切换——但是这种方式有两个重大的弊端。首先数据库系统必须管理大量不同的任务——长时间运行的工作,短时间运行的工作、磁盘I/O、网络通信以及处理偶然峰值的缓冲请求。构建这样一个单事件循环的系统会让事情变得非常复杂。其次,从单核到多核并行运行这样的系统需要将大量的逻辑转移到客户端。
无论如何,让多线程进程运行在多核多socket机器上都有需要在多个NUMA区域之间移动共享数据的问题。为了避免单核实现和NUMA开销,平衡的方法是构建一个系统,通过每个CPU socket分组多线程而不是像单线程系统那样按照每个核。这样就能够确保共享数据不会被多个NUMA区域访问,同时也允许系统使用更简单的逻辑线程。
3) 上下文切换的开销
在一个多线程系统中,我们很容易就会创建大量的线程,所以如果不谨慎处理那么上下文切换就会成为系统瓶颈。应该尽可能地编写单线程操作,不要在线程间传递请求。
4) 内存总线延迟
下一个大的瓶颈在CPU上——由于需要等待数据或者因为分支预测错误而导致的CPU空闲。对这类问题的处理方式是:
对一个线程而言辅助性的数据始终应该是本地的,同时应该在查询的时候合并从而避免不必要的数据移动。
5)跨集群的动作
向上向外扩展的基本原则是,系统之间几乎没有共享——最小化跨集群的同步动作。在Aerospike中,数据使用了一个简单的算法分割,同时访问路径按级组织,因此每个线程都能独立运行而没有竞争。高竞争的第一个迹象就是低CPU利用率——类似于MongoDB在多核机器上处理写负载时的运行情况。
在使用2个节点、5000万条记录同时每一条拥有128个字节的数据都复制2份的情况下运行同样的测试,结果显示:与之前的结果相比,新的方法(在CentOS 6.3上运行Aerospike社区版3.2.8)性能提升显著。