Design a high performance cache for multi-threaded environment
如何设计一个支持高并发的高性能缓存库
不 考虑并发情况下的缓存的设计大家应该都比较清楚,基本上就是用map/hashmap存储键值,然后用双向链表记录一个LRU来用于缓存的清理。这篇文章 应该是讲得很清楚http://timday.bitbucket.org/lru.html。但是考虑到高并发的情况,如何才能让缓存保持高性能呢?
高并发缓存需要解决2个问题:1. 高效率的内存分配;2. 高效率的读取,插入和清理数据。关于第一个问题涉及到高效率的内存分配器,使用成熟的jemalloc/tcmalloc足够满足需求。这里探讨下如何解决第二个问题。
由 于缓存系统的特点, 每次读取缓存都需要更新一些访问信息(最后读取时间,访问频率),在清理时会根据这些信息使用不同的策略来进行数据清理,这样看来似乎每次的读操作都变成 了写操作。看过几篇文章都比较集中在如何优化这个读操作修改LRU的行为。例如: http://www.ebaytechblog.com/2011 /08/30/high-throughput-thread-safe-lru-caching/#.UzvEb3V53No 以及 http://openmymind.net/High-Concurrency-LRU-Caching/, 但是这种情况下不论怎么优化,使用链表的LRU始终是个瓶颈, 因为每次读操作只能一个线程来修改这个LRU链表,并且修改都是集中在链表的两端。有些文章甚至使用lock-free的doubled linked list来减少锁竞争。 一些成熟的缓存系统如memcached,使用的是全局的LRU链表锁,而redis是单线程的所以不需要考虑并发的问题。由于这些都是远程的缓存服务 器,因此它们的瓶颈往往是网卡,所以并发上面并不需要什么高要求。
仔 细思考后,发现在并发的情况下使用LRU链表来记录访问信息其实并不合适,会导致严重的锁竞争,这点无法避免。因此,需要彻底放弃使用LRU链表。鉴于缓 存系统的特性,可以做如下假设: 缓存如果达到阀值,可以使插入立即返回失败。这样,我们可以使用一个预分配的数组来记录所有的cache item的访问信息。整个结构如下:
concurrent cache system arch
可 以看到锁粒度是hashmap里面的bucket级别,每次读操作只需对相应的bucket加锁,然后更新bucket对应的访问信息数组元素即可,由于 每个bucket对应的访问信息是独立占据数组的一个元素的,因此bucket锁就保证了访问信息的线程安全。这样就解决了读取操作的并发竞争问题。
接 下来看看如何解决插入问题。从图可以看到,每个bucket的需要分配一个独立的access info位置索引,多线程插入的时候会发生竞争,为了减少竞争,可以预先生成一个目前空闲的位置链表,这样插入的时候,每个线程可以根据当前的 bucket索引选择从不同的free链表里面分配一个位置。这样锁竞争可以分散到多个free list上面,每次插入时把分配过的位置索引从free list 移除。
最 后,清理过程可以放在一个独立线程里面,为了避免插入因为缓存满了而返回失败,每次在缓存快满的时候(free list的size不够用了),进行一次access info array扫描。根据不同的缓存清除策略和访问信息(时间和频率)来决定哪些位置索引是可以重新释放到free list列表。由于扫描过程中无需加锁,扫描对读取和插入操作是没有性能影响的。只有最后进行释放时才会对需要释放的bucket和free list进行加锁,锁竞争大大减少。
如上设计,大大减少了缓存的读取,插入和清理过程中的锁竞争问题,并且读取和插入都是O(1)的,并不会因为缓存系统的增大影响性能(清理后台线程可能会跑的久点,可以选择性清理来优化)。这样一个支持高并发的缓存系统就完成了。
简 单的实现后,实测在8核CPU上面8线程读,8线程写,可以跑到 读写TPS均在1M/S以上,参考官方单线程的redis的benchmark数据 Using a unix domain socket 排除网络瓶颈,SET/GET的TPS大概在200k/s 左右,可以看到这样一个高并发的cache基本上是scalable的。
Design a high performance cache for multi-threaded environment的更多相关文章
- Design Of A Modern Cache
http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html MONDAY, JANUARY 25, 2016 AT ...
- share memory cache across multi web application
Single instance of a MemoryCache across multiple application pools on the same server [duplicate] Yo ...
- Language-Directed Hardware Design for Network Performance Monitoring——Marple
网络监控困难 1.仅仅通过去增加特定的监控功能到交换机是不能满足运营商不断变化的需求的.(交换机需要支持网络性能问题的表达语言) 2.他们缺乏对网络深处的性能问题进行本地化的可见性,间接推断网络问题的 ...
- [转]Magento on Steroids – Best practice for highest performance
本文转自:https://www.mgt-commerce.com/blog/magento-on-steroids-best-practice-for-highest-performance/ Th ...
- Class Loading Deadlocks
By tomas.nilsson on Feb 28, 2010 Mattis keeps going strong, in this installment you get to learn eve ...
- [Angular] Performance Caching Policy - Cache First, Network Last
If you want to cache API response by using angular service-worker, you can do it in: src/ngsw-config ...
- Chapter 6 — Improving ASP.NET Performance
https://msdn.microsoft.com/en-us/library/ff647787.aspx Retired Content This content is outdated and ...
- System and method for dynamically adjusting to CPU performance changes
FIELD OF THE INVENTION The present invention is related to computing systems, and more particularly ...
- CPU cache
cache是一种小而快的缓冲器,用在CPU和main memory之间进行数据读写. 在processor访问主memory时,首先检查cache中是不是有一份copy,如果cache hit,则直接 ...
随机推荐
- python3:定时执行自动化测试脚本
转载请注明出处:https://www.cnblogs.com/shapeL/p/9172990.html 1.windows任务计划定时执行脚本 (1)创建 .bat 文件,执行脚本的命令(inte ...
- magento -- 给Magento提速之缓存上的探索
依然在为Magento提速做努力,除了自带的缓存和编译,之前的所作的很多努力都是从减少JS,Css,图片等载入时间入手,而对页面载入耗时最早有时也是最大的一部分--获取页面数据没有做太多处理,以gap ...
- 好玩的Python库tqdm (转载)
原文地址: https://blog.csdn.net/zejianli/article/details/77915751 可以显示循环的进度条的库,再也不用担心不知道程序跑到哪里还要跑多久了 tqd ...
- 从 Python 第三方进度条库 tqdm 谈起 (转载)
原文地址: https://blog.ernest.me/post/python-progress-bar tqdm 最近一款新的进度条 tqdm 库比较热门,声称比老版的 python-progre ...
- HOG+SVM+INRIAPerson数据集代码
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgu ...
- OpenCV Error: Insufficient memory问题解析
前言 项目程序运行两个月之久之后突然挂了,出现OpenCV Error: Insufficient memory的错误,在此分析一下该问题. 问题的表现形式: 程序内存使用情况: 问题: OpenCV ...
- 在python中的使用
操作步骤: 1. 连接数据库,生成数据库连接对象 conn = pymongo.MongoClient('localhost',27017) 2. 选择要操作的数据库,生成数据库对象 (__setit ...
- Future接口和FutureTask类【FutureTask实现了Runnable和Future接口】
Future API: public interface Future<V> { /** * Attempts to cancel execution of this task. This ...
- ElasticSearch(九):springboot项目集成消息中间件activeMQ
目的:为了将elasticsearch做成单独的服务,那么我们必须解耦,也就是业务逻辑和搜索模块是没有关系的,并且是异步的.那么项目之间通信,使用的选择有限,消息中间件是一个不错的选择. 消息中间件常 ...
- k8s dockerk个人学习(1)
虚拟机部署k8s 1. 创建虚拟机 虚拟机用的是virtualBox和vagrant工具,百度安装virtualBox和vagrant 创建vagrant目录并创建文件Vagrantfile内容为 V ...