hi,大家好,今天给大家分享一篇,如何设计和优化缓存(类redis)系统,希望大家了解真正落地的架构设计方案,类别redis和memcached,了解架构是如何演进的,学习落地经验。

CKV(也称为CKV+)是腾讯新一代自研高性能NoSQL KV数据库,兼容Redis、Memcached、ASN协议,具备高性能、低延时、低成本的优势,能够应对海量数据访问、存储成本敏感、延时敏感等问题。

CKV作为公司应用最广泛的NoSQL存储系统,已接入包括广点通、信鸽、QQ音乐、财付通、微视、看点等业务。

随着业务规模的增长,成本和性能是用户最关注的问题,为了进一步降低用户的成本并提升性能,我们分别从性能成本两个方面对CKV进行了优化。

下面先回顾CKV的架构演进,然后分析当前架构的性能瓶颈,重点介绍性能方面的优化,并在性能上与开源Redis和友商竞品做对比。

一、CKV的架构演进

CKV作为公司有历史的分布式内存系统,下面罗列了一些重要的发展经历。

  • 2009年TMEM(tencent memcached)作为高并发内存级存储解决方案上线,支撑了当时的QZone/朋友网等业务。

  • 2011年更名为CMEM(cloud memcached),开始向云计算靠拢并为海量的第三方游戏用户提供了解决方案,比如偷菜,抢车位等。

  • 2012年随着业务规模的增加,内存的高成本成为了核心问题,通过引入冷数据存储介质降低了业务的使用成本。

  • 2014年CKV支持了微信红包业务。

  • 2017年基于seastar框架开发的CKV上线,兼容了REDIS协议,进一步提升了性能。

1.1 CMEM架构

1.1.1 架构总览

CMem的模块众多,按照模块功能分为核心模块和外围模块。

核心模块采用的是传统的三层架构,包括接入、存储和元数据(master)模块三部分。元数据(master)模块负责管理路由等元数据,元数据是本地文件存储,无备份节点。业务直接访问的是接入模块,接入模块会根据路由转发到后端的存储模块。

外围模块包括备份、搬迁、统计、探测、恢复等模块。

CMem对业务数据通过Presharding的方式进行分片,将所有数据按照 hash(key) % 10000的方式逻辑上分成10000个Slot,每个Slot会对应存储节点的一对主备分片,存储端采用主备的方式来容灾。

1.1.2 架构优缺点

优点

  1. 底层数据存储采用共享内存,在进程异常的时候可以快速恢复。引擎设计上对于用户数据长度比较一致的业务上,可以充分利用内存。

  2. 支持开源memcached协议,可以直接用memcached客户端访问。

  3. 搬迁对用户影响很小,只影响搬迁中的删除操作。

  4. 多租户方式,可以充分利用资源。

缺点

  1. 模块较多,出现异常定位问题链路较长。

  2. 存储模型采用整机主备的设计,一个机器都是主,一个机器都是备,造成主机压力较大,而备机相对比较闲,而且只支持一主一备。

  3. 主备同步用的rsync进程,是单进程单线程模型。在访问量较大的场景下,容易成为瓶颈。

  4. 引擎设计对于随机用户数据长度上不太友好。

  5. 存储模块采用多进程加锁的方式,不能充分利用资源。进程太少,能够利用的CPU就少,进程太多,锁的开销就增大。

  6. 元数据管理模块采用的本地存储,并且是单点。元数据推送采用全量推送,效率较低。

1.2 CKV架构

1.2.1 架构总览

上图中灰色部分是从CMem的架构中移除的模块,只保留了一些必要的模块(蓝色部分)。从功能上也分为核心和外围模块。

核心模块采用的是二层架构,包括存储和元数据管理两个模块。元数据是保存在本地的ETCD服务中,采用ETCD服务于元数据服务1对1模式,至少是三分数据,提升了元数据的可靠性。

存储模块兼顾接入的能力,业务直接访问存储模块,如果根据路由计算出是本机处理就直接处理,非本机请求就转发到目标节点处理。

外围模块主要包括备份和OSS。备份负责数据的冷备与恢复,OSS负责管理整个集群,包括实例的创建/删除/备份/扩缩容等。

1.2.2 核心架构

CKV从设计上为了适应新型的多CPU大内存机型,采用多租户方式,底层引擎采用共享内存,为了兼容REDIS协议,设计支持了string/list/hash/set/zset等数据结构。

存储节点采用单进程多线程模型,实际存储分片按需分配,一个节点上尽量保证主备数量均衡,同时存储兼有转发的能力,也支持独立部署接入层的能力。

元数据管理上基于ETCD保证元数据的可靠性,路由设计上采用增量推送的方式,提高了路由更新的效率。数据模型上跟CMem类似,采用Presharding,hash(key) % 16384,Slot数从10000增加到了16384,跟Redis保持一致,并且支持一主多备。

1.2.3 线程模型

CKV的Cache作为核心的存储模块,在设计的时候,性能是我们最看重的,同时为了充分利用当前多CPU大内存的机型资源,我们基于Seastar这个高性能网络通信框架来开发。

http://seastar.io/

Seastar是一个面向现代硬件多核架构的高性能异步网络框架,它是KVM作者Avi Kivity领衔开发的开源项目。Seastar采用Share-nothing架构、全异步编程、用户态任务调度器、支持内核协议栈和用户态协议栈、独立的内存管理等提供了极致的性能。

Cache的设计采用对等模式,每个CPU可以理解为一个线程,所有线程的功能可以认为是一样的,不区分IO线程和worker线程。

CKV的最小存储单元是分片,每个分片是独立的共享内存,采用按需分配的方式。CKV存储访问采用无锁设计,每个分片只会由一个CPU来管理,这样访问就不需要加锁。

在上图中可以看到,一共有四个线程,其中第一个线程管理两个分片,第二个线程管理三个分片,后面两个线程分别只管理一个分片。

每个线程接收到请求之后,会根据路由计算是不是当前线程处理,如果不是的话,会转发到其他线程来执行请求。

同时每个线程都可以处理网络IO,一旦接受到数据包,在当前核完成编解码的工作,然后会根据路由将请求转发到指定的线程或者其他节点运行。这样就把网络IO和编解码的工作分摊到所有线程,可以充分利用CPU资源。

Cache上大部分操作都是基于future/promise实现的异步,包括逻辑处理、网络IO和磁盘IO等,只有底层存储引擎是同步操作。这样的好处是所有的IO不需要等待,可以让出CPU,提高吞吐量。

2、性能瓶颈及分析

2.1 性能数据

基于Seastar框架开发的CKV整机测试性能数据。

2.2 CKV性能分析

前面已经给出了CKV的性能数据,虽然相对CMem来说,性能提升了不少,为了进一步提高性能,我们对于整机场景了做了性能分析。

从火焰图上看没有明显的集中,CPU主要消耗是

seastar::reactor::run_tasks 和 seastar::reactor:poll_once。

其中run_tasks负责执行命令以及网络收包,而poll_once主要是负责网络包的发送。

然后通过mpstat分析CPU消耗。

上图中,第2列是CPU,第3列是%usr 用户态占比,第5列是%sys 内核态占比,第8列是%si 软中断占比。可以看出软中断已经消耗了30%的CPU

这里的软中断主要是网卡收包导致的。网卡收到网络包后,会通过硬件中断通知内核有新的数据到了,内核会调用对应的中断处理程序来响应该事件,先将网卡的数据读到内存中,然后更新硬件寄存器的状态,

然后内核会触发一个软中断,需要从内存中找到网络数据,再按照网络协议栈,对数据逐层解析和处理,最后将数据给到应用程序。

2.3 REDIS性能分析

在同样的硬件条件下,测试机器包含72个物理核,在机器上启动了72个redis实例,然后对72个实例进行了整机压测。perf统计如下

从上图可以看出,redis的CPU消耗主要是网络收发包。通过mpstat查看CPU消耗。

从上图可以看出,CPU消耗主要集中也是在软中断。在整机测试的场景下可以看出网络成为了性能的瓶颈。

在整机大压力访问场景中,无论是CKV还是REDIS,最终性能瓶颈都在网络上,因此传统的内核协议栈不能能充分利用最新硬件的能力,DPDK通过用户态协议栈则可以解决这个问题。

3、DPDK

DPDK全称Intel Data Plane Development Kit,是intel提供的数据平面开发工具集,是一个用来进行包数据处理加速的软件库。DPDK专注于网络应用中数据包的高性能处理,具体体现在DPDK应用程序是运行在用户态上利用自身提供的数据平面库来收发数据包,绕过了Linux内核协议栈对数据包处理过程。

DPDK通过UIO、poll-mode网卡驱动、内存池、大页内存管理、无锁数据结构等数据手段来提升性能。

3.1 初始化SEASTAR

1.seastar初始化的时候,先在cpu 0调用smp::configure 初始化配置。

2.在cpu 0调用rte_eal_init 初始化DPDK的EAL。

3.通过rte_eal_remote_launch 在非0 cpu上调用reactor::configure 初始化引擎,在初始化引擎的时候会根据用户配置初始化协议栈

4.等待其他核初始化完成后,在cpu 0上初始化引擎和协议栈。完成框架的初始化。

3.2 初始化协议栈(DPDK)

协议栈的初始化分成三大步。其中上图中设置成橙色的 设置隔离模式 和 设置流规则 是CKV增加的。

1.网卡初始化。首先通过rte_eth_dev_info_get 获取网卡信息,然后通过rte_flow_isolate设置网卡成隔离模式,最后调用rte_eth_dev_configure 配置网卡

2.队列初始化。通过调用rte_eth_rx_queue_setup 和 rte_eth_tx_queue_setup 分别初始化入队列和出队列。然后会轮询 rte_eth_rx_burst 和 rte_eth_tx_burst分别处理收包和发包。

3.网卡启动。先调用rte_eth_dev_start启动网卡,然后调用rte_flow_create设置流规则,最后调用rte_eth_link_get_nowait检查网卡是否正确启动。

3.3 数据包处理

在前面队列初始化完成后,我们会轮询RX和TX两个方向上的包。RX代表接受数据,TX代表传输数据。具体是通过注册一个poller不停的轮询处理。

  • RX注册的是 reactor::poller::simple([&] { return poll_rx_once(); })

  • TX注册的是 reactor::poller::simple([this] { return poll_tx(); }))

对于Server端,首先会通过RSS(Receive-Side Scaling, also known as multi-queue receive) 将包分发给指定的队列。将不同的流分发给不同的CPU,同一个流始终会在同一个CPU上,避免TCP的顺序和CPU的并行发生冲突。

在协议栈实现中,在二三层都有RX方向的rx_stream,高层会调用低层stream的listen方法,注册包处理的回调函数,从dpdk_qp收到包,会将包从dpdk的rte_mbuf转化成packet格式,然后往上交给L2的stream,再往上交给L3的stream。

包传递到二三层是通过stream/subscription的回调方式,送到四层直接调用l4→receive方法直接处理。

目前Seastar的用户态协议栈已经支持了ARP、DHCP、ICMP、IP、TCP、UDP协议。四层主要是TCP/UDP层,我们重点分析TCP层。协议栈抽象了TCB对应内核协议栈的连接概念。每一个TCP连接对应一个TCB对象。当数据从三层传递给TCP层的时候,根据IP/PORT四元组可以找到对应的TCB,然后会将数据保存到TCB的接受队列里。

上层的应用通过调用READ访问从TCB的接受队列读取数据,这里协议栈的实现为了zero-copy,单次读取是一个packet,我们优化成批量读取,单个连接的通信速度整体提升了一倍,从100MB/s提高到200MB/s.

发送数据通过上层调用WRITE方法,首先将数据保存到对应TCB的unsent队列,在发送的时候会将数据先放到packetq的循环buffer,发送成功后然后再保存到发送的data队列里,等到对端ACK后才会将发送队列data的数据删除。

3.4 隔离模式和流规则

DPDK的应用程序默认需要接管网卡,一旦程序运行,网卡的所有数据都通过DPDK处理,比如常见的SSH登录可能就会失败,还有一些其他公司的Agent探测和上报都会失效。

对于这个问题我们通过DPDK提供的isolate模式来解决,也就是上文提到的设置隔离模式。isolate模式是指所有通过DPDK的数据默认还是走内核协议栈,然后通过配置一些流规则,可以将指定的流指向DPDK。

4、性能数据

单分片六核

对比结果可见,CKV写性能比Tair高60%,读性能与Tair基本一致,value比较大时性能略低于Tair,是CKV未来优化方向。

六分片六核

在六分片六核场景下,基于DPDK的CKV相对内核协议栈CKV,整体提升明显,提升了100%左右

整机

从测试结果看,CKV在引入DPDK后,通过DPDK代替了内核协议栈,优化了网络收发包的性能瓶颈,达到了单机千万的QPS

总结

我们从性能方面对CKV做了一系列优化。引入DPDK,通过优化网络瓶颈,将整机性能提升到千万QPS。目前适配部分网卡,后续会适配更多类型的网卡。

在压测的过程中,基于DPDK的原生协议栈在单个连接上收发包目前只能达到250MB/s的传输速度,对于CKV目前来说是可以应对绝大部分场景,但是还需要持续地优化提升单个连接上的传输性能。

未来CKV会持续优化迭代,成本上会采用全类型数据下沉到SSD介质,进一步降低成本,性能上面会持续优化原生协议栈,持续挖掘性能。

- END -


看完一键三连在看转发点赞

是对文章最大的赞赏,极客重生感谢你

推荐阅读

我们究竟为谁打工?

大厂后台开发基本功修炼路线和经典资料

如何从0搭建公司的后端技术栈

你好,这里是极客重生,我是阿荣,大家都叫我荣哥,从华为->外企->到互联网大厂,目前是大厂资深工程师,多次获得五星员工,多年职场经验,技术扎实,专业后端开发和后台架构设计,热爱底层技术,丰富的实战经验,分享技术的本质原理,希望帮助更多人蜕变重生,拿BAT大厂offer,培养高级工程师能力,成为技术专家,实现高薪梦想,期待你的关注!点击蓝字查看我的成长之路

校招/社招/简历/面试技巧/大厂技术栈分析/后端开发进阶/优秀开源项目/直播分享/技术视野/实战高手等, 极客星球希望成为最有技术价值星球,尽最大努力为星球的同学提供技术和成长帮助!详情查看->极客星球

求点赞,在看,分享三连

深入理解缓存系统|单机QPS突破千万优化之路相关推荐

  1. 缓存系统MemCached的Java客户端优化历程

    來源:http://www.infoq.com/cn/articles/memcached-java 作者 岑文初 发布于 2008年9月27日 上午12时38分 社区 Java 主题 集群与缓存 M ...

  2. memcached搭建缓存系统

    概念 Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能. 适用场合 分布式应用.由于memcac ...

  3. 千万级并发!如何设计一个多级缓存系统?

    作者:不清不慎,目前在杭州蘑菇街公司任职,Java大数据开发工程师一枚,热爱研究开源技术! 架构师社区合伙人! 首先我们需要明白,什么是一个多级缓存系统,它有什么用.所谓多级缓存系统,就是指在一个系统 ...

  4. fifo页面置换算法设计思路_千万级并发!如何设计一个多级缓存系统?

    什么是一个多级缓存系统?它有什么用?我们又如何设计一个多级缓存系统? 图片来自 Pexels 所谓多级缓存系统,就是指在一个系统的不同的架构层级进行数据缓存,以提升访问效率. 我们都知道,一个缓存系统 ...

  5. Memcache,Redis,MongoDB(数据缓存系统)方案对比与分析

    一.问题:     数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求. 二.解决方案:      1.通过高速服务器Cache缓存数据库数据      2.内存数据库   (这里仅从 ...

  6. 分布式缓存系统Memcached简介与实践

    缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...

  7. 分布式缓存系统Memcached简介与实践(.NET memcached client library)

    原文:分布式缓存系统Memcached简介与实践(.NET memcached client library) 缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加 ...

  8. 同程旅游缓存系统(凤凰)打造Redis时代的完美平台实践

    https://blog.csdn.net/qiansg123/article/details/80128077 缓存大家比较熟悉,在各种场景下也用的很多,在同程旅游也一样,缓存是一个无处不在的精灵, ...

  9. 深入理解linux系统的目录结构

    深入理解linux系统的目录结构(总结的非常详细) 作者:佚名 字体:[增加 减小] 来源:互联网 时间:04-09 14:34:20 我要评论 对于每一个Linux学习者来说,了解Linux文件系统 ...

最新文章

  1. controller接收json数据_答疑 | 前后端分离,如何接收json数据?
  2. Feature Map of Pytorch示例
  3. vue 在进入某一个页面的时候,created方法不执行,----亲测,通俗易懂
  4. jQuery-对Select的操作集合
  5. SAP Spartacus注入自定义的CurrentProductService
  6. 【ROI 2019 Day2】课桌【贪心】【决策单调性】【分治】
  7. Django model 字段类型及选项解析(一)
  8. 斐波那契数的皮萨诺周期
  9. java 创建动态int数组_在Scala中创建动态增长数组的最佳方法是什么?
  10. Tomcat相关总结
  11. splay详解(三)
  12. 总结(5)--- Numpy和Pandas库常用函数
  13. eclipse删除mysql数据库_在Eclipse中测试MySQL-JDBC(4)删除数据库中的数据【D】
  14. deepin linux 安装jdk,deepin安装JDK
  15. SQL中COUNT的用法
  16. airtest获取设备号和获取设备宽度、高度、绝对坐标 相对坐标、滑动屏幕
  17. 支付二维码整合 - 三码合一支持支付宝、QQ、微信
  18. Java网课笔记整理
  19. 每日一译:上述报盘以我方最后确认为准
  20. 岁月温柔-13 妈妈担心回到山村里的大姨冻着

热门文章

  1. 设置某个元素的标签内容、设置元素的样式、层次选择器、总结选择器
  2. LinkedBlockingDeque源码
  3. java1121123211234321_使用for 语句打印显示下列数字形式:n=4 1 1 2 1 1 2 ,使用for 语句打印显示下列数字形式:n=4...
  4. Windows 中的环境变量 Path 与 XXXX_HOME 的区别
  5. Java基础查漏补缺(2)
  6. 第二十一章:变换(三)
  7. 【转】Info.plist中常用的key简介
  8. Linux bridge-utils tunctl 使用
  9. 英语考试(最小生成树)
  10. bash特性之四、五