消息队列高手课
李玥
京东零售技术架构部资深架构师
立即订阅
8426 人已学习
课程目录
已完结 41 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | 优秀的程序员,你的技术栈中不能只有“增删改查”
免费
预习 | 怎样更好地学习这门课?
基础篇 (8讲)
01 | 为什么需要消息队列?
02 | 该如何选择消息队列?
03 | 消息模型:主题和队列有什么区别?
04 | 如何利用事务消息实现分布式事务?
05 | 如何确保消息不会丢失?
06 | 如何处理消费过程中的重复消息?
07 | 消息积压了该如何处理?
08 | 答疑解惑(一) : 网关如何接收服务端的秒杀结果?
进阶篇 (21讲)
09 | 学习开源代码该如何入手?
10 | 如何使用异步设计提升系统性能?
11 | 如何实现高性能的异步网络传输?
12 | 序列化与反序列化:如何通过网络传输结构化的数据?
13 | 传输协议:应用程序之间对话的语言
14 | 内存管理:如何避免内存溢出和频繁的垃圾回收?
加餐 | JMQ的Broker是如何异步处理消息的?
15 | Kafka如何实现高性能IO?
16 | 缓存策略:如何使用缓存来减少磁盘IO?
17 | 如何正确使用锁保护共享数据,协调异步线程?
18 | 如何用硬件同步原语(CAS)替代锁?
19 | 数据压缩:时间换空间的游戏
20 | RocketMQ Producer源码分析:消息生产的实现过程
21 | Kafka Consumer源码分析:消息消费的实现过程
22 | Kafka和RocketMQ的消息复制实现的差异点在哪?
23 | RocketMQ客户端如何在集群中找到正确的节点?
24 | Kafka的协调服务ZooKeeper:实现分布式系统的“瑞士军刀”
25 | RocketMQ与Kafka中如何实现事务?
26 | MQTT协议:如何支持海量的在线IoT设备?
27 | Pulsar的存储计算分离设计:全新的消息队列设计思路
28 | 答疑解惑(二):我的100元哪儿去了?
案例篇 (7讲)
29 | 流计算与消息(一):通过Flink理解流计算的原理
30 | 流计算与消息(二):在流计算中使用Kafka链接计算任务
31 | 动手实现一个简单的RPC框架(一):原理和程序的结构
32 | 动手实现一个简单的RPC框架(二):通信与序列化
33 | 动手实现一个简单的RPC框架(三):客户端
34 | 动手实现一个简单的RPC框架(四):服务端
35 | 答疑解惑(三):主流消息队列都是如何存储消息的?
测试篇 (2讲)
期中测试丨10个消息队列热点问题自测
免费
期末测试 | 消息队列100分试卷等你来挑战!
结束语 (1讲)
结束语 | 程序员如何构建知识体系?
消息队列高手课
登录|注册

16 | 缓存策略:如何使用缓存来减少磁盘IO?

李玥 2019-08-29
你好,我是李玥。这节课,我们一起来聊一聊缓存策略。
现代的消息队列,都使用磁盘文件来存储消息。因为磁盘是一个持久化的存储,即使服务器掉电也不会丢失数据。绝大多数用于生产系统的服务器,都会使用多块儿磁盘组成磁盘阵列,这样不仅服务器掉电不会丢失数据,即使其中的一块儿磁盘发生故障,也可以把数据从其他磁盘中恢复出来。
使用磁盘的另外一个原因是,磁盘很便宜,这样我们就可以用比较低的成本,来存储海量的消息。所以,不仅仅是消息队列,几乎所有的存储系统的数据,都需要保存到磁盘上。
但是,磁盘它有一个致命的问题,就是读写速度很慢。它有多慢呢?一般来说 SSD(固态硬盘)每秒钟可以读写几千次,如果说我们的程序在处理业务请求的时候直接来读写磁盘,假设处理每次请求需要读写 3~5 次,即使每次请求的数据量不大,你的程序最多每秒也就能处理 1000 次左右的请求。
而内存的随机读写速度是磁盘的 10 万倍!所以,使用内存作为缓存来加速应用程序的访问速度,是几乎所有高性能系统都会采用的方法。
缓存的思想很简单,就是把低速存储的数据,复制一份副本放到高速的存储中,用来加速数据的访问。缓存使用起来也非常简单,很多同学在做一些业务系统的时候,在一些执行比较慢的方法上加上一个 @Cacheable 的注解,就可以使用缓存来提升它的访问性能了。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《消息队列高手课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(32)

  • 樱花落花
    LRU算法最经典的我觉得还是MySQL的bufferpool的设计,里面按比例分为young和old区,能很好的解决预读问题和老师讲的“挖坟”问题;
    2019-09-02
    11
  • leslie
    一路跟着三位老师在同时学习相关知识:我想我大致明白了消息队列的特性以及真正适用的场景,周二许老师的课程中刚好提到了相关东西特意为了许老师。
        关系型数据库、非关系型数据库、消息队列其实都属于存储中间件,老师今天的话题如何利用缓存来减少磁盘IO其实就是涉及了三种存储中间件;其实老师今天的三种方法我的理解其实就是对应了三种存储中间件:三种存储中间件都和内存有关,如何合理使用缓存、内存、磁盘去做合适的事情才是关键;
    老师课程的算法:几门相关的课程同时在学时间还是偏紧,不过刘老师的操作系统、许老师的架构、以及老师的消息队列同时学习,倒是了解什么场景下用以及用哪种消息队列;至少跟着老师的消息队列我应当知道业务中的什么事情让它去承担并且使用什么策略,先用现成的吧;改进只能等第二遍学习时再去做了。
      一直跟着学习学到现在发现学好消息队列的前提:1)对业务的数据情况清楚2)对于当下使用的存储中间件情况非常清楚3)对于消息队列所在的系统熟悉:其中就涉及需要非常了解操作系统和当前系统的架构最后就是充分利用当下的消息队列追加一些适合自己业务场景的算法:调整优化当下所用的消息队列。
    2019-08-29
    7
  • 姜戈
    利用双向链表+哈希表, 支持所有操作时间复杂度都为O(1). https://github.com/djangogao/mqexercise.git
    实现了最基础LRU算法, 关于LRU的改进算法:LRU-K和 LRU 2Q,可参考此文章https://www.jianshu.com/p/c4e4d55706ff
    2019-08-30
    6
  • 付永强
    可以通过在进行更新数据库操作时,删除缓存,读取数据库时如果有缓存就直接读,没有缓存则从数据库读取并更新缓存,这样的设计可以确保缓存幂等。
    2019-09-03
    2
    4
  • 岁月安然
    ”⻚⾯位置与尾部的距离。因为越是靠近尾部的数据,被访问的概率越⼤“
    不大理解这句话,尾部指的是啥?当某个客户端正在从很旧的位置开始向后读取⼀批历史数据,是怎么判断与尾部的距离,从而减少这部分的缓存。

    作者回复: 与尾部的距离=最后一个一条消息的尾部位置 - 页面的位置

    这个值越小,说明请求的数据与尾部越近,置换的时候被留下的概率也就越大。

    对于历史数据,由于距离远,这个值会很大,那这些页面在置换的时候被留下的概率就很小,所以很快就会被从内从中置换出去。

    2019-08-29
    3
  • Yasir
    交作业,双向链表实现
    https://github.com/tuhao/leetcode/blob/master/src/mqexercise/LRUTest.java

    作者回复: 👍👍👍

    2019-11-07
    1
  • Switch
    参考@A9 的写了一版链表的,然后又写了一版基于 优先队列的。
    https://github.com/Switch-vov/mq-learing/tree/master/src/main/java/com/switchvov/cache
    2019-10-24
    1
  • ponymm
    “找不到会触发一个缺页中断,然后操作系统把数据从文件读取到 PageCache 中,再返回给应用程序” 这里pagecache中没有数据并不会产生缺页中断,而是alloc page,然后放入lru链表中,接着调用a_ops->readpage()读取数据到page,可以参考kernel的 do_generic_mapping_read 函数
    2019-08-29
    1
    1
  • 布小丫学编程
    老师,我在github上写了一个lru的实现。https://github.com/fomeiherz/code-snippet/tree/master/lru

    使用了HashMap和Queue一起实现的。使用HashMap保存键值对可以实现O(1)复杂查询,使用队列保存key,头部出队,尾部入队。更新比较复杂,需要删除对应的元素后,才可以再入队,这里是O(n) 复杂度。

    老师,更新队列顺序时是否会有更快办法?或者有更快的实现办法呢?求指导

    作者回复: 你这个实现中,命中缓存后“移动元素到尾部”这个操作,同时会移动其它无关的元素的位置(从队头移到队尾),这样就不满足LRU的原则了,可以试试将队列换成链表这种可以支持随机写的数据结构。

    2019-11-28
  • 付锐涛
    置换时机:实时,定期,事件
    2019-11-11
  • A9
    JMQ是如何使用自定义的LRU算法的?即使使用DirectBuffer不是也要经过PageCache吗?

    作者回复: DirectBuffer确实也需要经过PageCache,但是它有更好的批量大小,写入时的系统调用次数也会更少,所以性能更好一些。

    2019-10-27
  • Pitcher
    https://gist.github.com/mrpanc/2cf2e668eab099114e0a1f99eb44f573 交作业,用go写了一版基础的lru策略
    2019-09-27
  • godtrue
    缓存的速度理论上比磁盘快十万倍,那对性能的提升是多么的立竿见影——性能提升的神器。
    而且一级不行可以再来一级,我正好也在压测,用不用本地缓存,性能可以说会差上几十倍之多。
    具体缓存怎么用,还需根据自己的实际业务场景而定。我们redis缓存是每晚定时跑的,也可以人工触发,分为全量和增量,本地缓存是读redis缓存时添加的,为了防止不存在的key加重redis缓存的负载,会将一个特殊值写入本地缓存,以做标识。
    2019-09-24
  • 13761642169
    PageCache是OS提供的能力,用户程序调用什么API才能使用到PageCache,为什么说kafka大量使用到PageCache,因为mmap?

    作者回复: 对应的几个写文件的系统调用都会经过PageCache,比如write pwrite还有mmap

    2019-09-22
  • 泛岁月的涟漪
    public class DefaultLruCache<K, V> extends LruCache<K, V> {

        private LruCacheImpl<K, V> cache;

        public DefaultLruCache(int capacity, Storage<K, V> lowSpeedStorage) {
            super(capacity, lowSpeedStorage);
            cache = new LruCacheImpl<>(capacity, true);
        }

        @Override
        public V get(K key) {
            V value = cache.get(key);
            if (value == null) {
                value = lowSpeedStorage.get(key);
                cache.put(key, value);
            }
            return value;
        }

        class LruCacheImpl<K, V> extends LinkedHashMap<K, V> {

            public LruCacheImpl(int capacity, boolean accessOrder) {
                super(capacity, 0.75F, accessOrder);
            }

            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > capacity;
            }
        }
    }
    2019-09-09
  • 明日
    Java实现: https://gist.github.com/imgaoxin/ed59397c895b5a8a9572408b98542015

    作者回复: 👍👍👍

    2019-09-03
    2
  • 木木木
    缓存和db的一致性有很多工作做的。文中三种方法,定时更新全部缓存估计只适合量小的,更新不频繁的配置性数据。定时的时间也有问题,太短影响性能,太长一致性问题。设置缓存时间也是类似的问题。我们业务中实际使用同步更新的方式,当然异步也可以。关键是在业务代码落库时落缓存补偿的记录,里面带序号,用来解决异步问题。更新后还可以进行一致性检查
    2019-09-03
  • 0xDDE58
    期待JMQ开源
    2019-09-02
  • 约书亚
    来晚了,根据jms和kafka这两节,我试着猜想一下jms的机制,顺便提个问题:
    1. 老师这节提到的jms实现的缓存机制,都是基于direct buffer自己实现的一个内存池,并实现了变种的LRU对么?这个缓存就是前面提到的journal cache,被writeThread/RelicationThread/FlushThread使用?
    2. 这个内存池看起来并没有借助于netty的direict buffer pool是吧?
    3. 那原谅我对比一下jms和rocket,jms没有基于mmap去做而选择direct buffer,看起来是为了:
    a. 减少GC的压力
    b. 比mmap更容易控制,就更容增加缓存的命中率
    这样?
    4. 另外,有个概念我很模糊,有资料说direct buffer在写磁盘/socket时并不能真的节省一次cpu copy?那这样的话jms可以说并没有利用zero copy?

    望解惑

    作者回复: A1:是的。
    A2:是的。
    对于问题3和4,你可以看一下关于JMQ的这篇文章:
    https://www.jiqizhixin.com/articles/2019-01-21-19

    2019-09-01
  • 💪😊
    有一个问题:java 内部实现缓存很容易导致fullgc频繁发生
    2019-08-30
收起评论
32
返回
顶部