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,则直接 ...
随机推荐
- screen 命令 http://man.linuxde.net/screen
http://man.linuxde.net/screen -A 将所有的视窗都调整为目前终端机的大小. -d <作业名称> 将指定的screen作业离线. -h <行数> 指 ...
- Git内网服务搭建全过程
看到一篇搭建git服务器的文章,主要是公司内网搭建的,讲得非常详细,比廖雪峰的要完整,必须赞! http://developer.51cto.com/art/201507/483448.htm
- art.dialog 使用说明
Js代码 2. 传入HTMLElement 备注:1.元素不是复制而是完整移动到对话框中,所以原有的事件与属性都将会保留 2.如果隐藏元素被传入到对话框,会设置display:block属性显示该 ...
- JAVA中static什么作用?
是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,也就是只 ...
- SVN 将主干的代码合并到分支上
来源:http://blog.csdn.net/u012701023/article/details/50978154 问题:开发有了项目主干,再次基础上起了一个分支,开发新的功能:因为业务需要,在上 ...
- 从 Python 第三方进度条库 tqdm 谈起 (转载)
原文地址: https://blog.ernest.me/post/python-progress-bar tqdm 最近一款新的进度条 tqdm 库比较热门,声称比老版的 python-progre ...
- 当ORACLE归档日志满后如何正确删除归档日志
当ORACLE 归档日志满了后,将无法正常登入ORACLE,需要删除一部分归档日志才能正常登入ORACLE. 一.首先删除归档日志物理文件,归档日志一般都是位于archive目录下,AIX系统下文件格 ...
- CodeForces - 457C:Elections(三分)
You are running for a governor in a small city in Russia. You ran some polls and did some research, ...
- ubuntu journalctl — 检索 systemd 日志
常用: 查看最近1000行log sudo journalctl -f --lines=1000 -u server.$PROJECT_NAME --no-full, --full, -l 如果字段内 ...
- MVC的好处 演示
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...