Redis学习笔记2—缓存、集群、一致性等

缓存淘汰策略

为了保证高性能,缓存都保存在内存中,当内存满了之后,需要通过适当的策略淘汰老数据,以便腾出空间存储新数据。数据的淘汰策略,典型的包括FIFO(先进先出,淘汰最老数据),LRU(淘汰最近最少使用的),LFU(淘汰使用频率最低的)。

FIFO很简单就不展开了,主要说下LRU和LFU的区别,详细区别参考这里

  1. LRU(Least Recently Used),首先淘汰最长时间未被使用的数据。实现方法是每次访问数据后把数据移到队头,删除时从队尾开始删除。
  2. LFU(Least Frequently Used),首先淘汰一定时期内被访问次数最少的数据。实现方法是记录数据在一定时段内的访问评率,删除访问频率最低的数据。此算法需要额外维护每个数据的访问量,并排序,实现比较复杂。

Java的LinkedHashMap已经实现了LRU算法,具体实现请查看JDK源码,使用方法请仔细阅读LinkedHashMap以下两个方法的JavaDoc(我贴出来了,注释有点多,有删减)。

    /**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
    /**
     * Returns <tt>true</tt> if this map should remove its eldest entry.
     *
     * <p>This implementation merely returns <tt>false</tt> (so that this
     * map acts like a normal map - the eldest element is never removed).
     *
     * @param    eldest The least recently inserted entry in the map, or if
     *           this is an access-ordered map, the least recently accessed
     *           entry.  
     * @return   <tt>true</tt> if the eldest entry should be removed
     *           from the map; <tt>false</tt> if it should be retained.
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

Redis提供的淘汰策略包括如下几种:

  1. noeviction(内存满后不主动回收,无法写入新数据)
  2. allkeys-lru(最近最少使用的Key)
  3. allkeys-random(随机回收)
  4. volatile-lru(过期集合中最近最少使用的Key)
  5. volatile-random(过期随机回收)
  6. volatile-ttl(过期最短存活)

常见缓存性能问题

缓存穿透

缓存穿透是指去获取一个Redis和DB都不存在的数据,由于Redis中不存在,导致流量透传到DB,而DB中无相关数据,查询后不会缓存结果到Redis。如果大量此类查询,会给数据库带来性能风险,此问题可被攻击者利用。

避免方法:

  1. 对于DB中查询不到的数据,也在Redis中进行短期缓存,避免反复查询DB。
  2. 使用互斥锁(mutex key):到缓存没命中时,不是立即去查询DB,而是先获取一个互斥锁(SETNX命令),获取到锁成功后再去查询DB。
  3. 业务层增加校验,过滤非法数据。
public String get(key) {  
    String value = redis.get(key);  
    if (value == null) { //代表缓存值过期  
       //设置超时,防止del失败时死锁  
       if (redis.setnx(key_mutex, 1, 60) == 1) { //代表设置成功  
           value = db.get(key);  
           redis.set(key, value, expire_secs);  
           redis.del(key_mutex);  
        } else {  //没获取到锁,表明其他线程获取了,等待一段时间后重试查询缓存  
           sleep(50);  
           get(key); //重试  
        }  
    } else {  
        return value;  
  }  
}

以上代码参考自Redis 的key设计技巧&&缓存问题

缓存击穿

缓存击穿是指在某个热点Key过期的时候,客户端产生大量的情况,导致请求击穿缓存直接到达DB,给DB带来巨大压力,避免方法请参考上述缓存穿透的互斥锁

缓存雪崩

缓存雪崩是指缓存服务器重启时或者大量缓存在短时间内集中过期时,恰好此时大量客户端执行并发操作,缓存命中失败导致给DB带来巨大压力。

避免方法:

  1. 查询缓存失败后,查询数据库的代码先加锁再查询数据库,或者队列执行,对于每个Key,每个进程同时只允许一个线程访问数据库,减轻数据库压力。
  2. 参考上述缓存穿透的互斥锁
  3. 给每个Key的过期时间后面加个随机值,确保缓存不会在同一时刻大面积失效。
  4. 设置热点数据永不过期,数据更新后,主动刷新缓存。

缓存和数据一致性

多服务写Redis

首先,多个服务修改同一个Key是不好的设计模式,应该把维护同一个Key的操作集中到一个服务里,比如更新订单的状态,应该都转发给订单服务来操作。当多写情况无法避免时,应采取如下措施:

  1. 互斥写,通过Redis的setex实现互斥说,来竞争对Key的写入权限。
  2. 使用乐观锁,给数据添加版本号或时间戳,通过乐观锁判断是否可以写入。

Redis & DB一致性

Redis作为缓存使用的时候,一般都是DB数据的映像,两套系统就会存在数据不一致的情况,如何才能最大限度的降低数据不一致的影响呢?比如数据库刚写入一个更新,缓存更新命令还没执行,这个时候来了个读请求,从缓存中读取的数据就不是最新的。

如果对于高一致性要求的场景,只能把读写操作串行执行,确保缓存和DB的一致性,但这样会严重降低系统的吞吐量,甚至成为系统瓶颈。

更通用的保存缓存和DB数据一致性的做法,是DB写入数据库后,删除缓存数据。这样系统下次读取请求时,会从DB中读取最新的数据并进行缓存。采用删除而不是更新缓存,主要是基于性能的考虑,不然如果反复更新数据场景下,反复写无人消费的缓存数据是一种浪费。

Redis集群部署

Redis支持主从和分片两种Cluster部署模式,提供高可用性。

主从

在主备模式下,Redis通过Sentinel哨兵来监控Master的状态,当Master异常后,从从节点中选出新主节点,并调整其他从节点的slaveof到新Master。
Sentinel通过部署多实例,实例间通过 Raft协议 实现自身的高可用,所有Sentinel需要部署 3个 节点才能保持自身的健壮性。

在一主多从模式下,为了减轻Master的数据同步压力,可以把主从模式配置为主从链模式,即A是B的主,B是C的主,从而减轻B和C都从A同步数据的压力。

主从模式下,当有新节点加入时,流程如下:

  1. 新节点向Master发送psync命令
  2. Master执行bgsave,fork子进程,生成RDB数据,并缓存新数据
  3. Master把RDB发送给新节点恢复
  4. Master把缓存中的新数据发送给新节点
  5. 新节点初始化完成,后续通过AOF进行增量数据的同步

分片

分别模式先,Redis通过一致性Hash算法,在内部把数据切分为16384个slot。通过对数据的Key进行Hash计算来数据保存的分片位置。

数据持久化机制和恢复

Redis支持RDB和AOF两种持久化机制。
RDB是内存数据库快照,Redis通过fork子进程把内存存储(二进制压缩)为RDB文件。快照过程中,新增数据使用COW(copy on write)的模式写入。RDB适合做灾备,但是由于定时报错,容易丢失部分数据。
AOF(append only file)是以文本日志形式记录Redis每个写入和删除操作。AOF日志写入支持灵活的策略,如每秒刷盘,根据数据量刷盘等。
RDB是最新数据快照,文件小,AOF记录操作过程,文件比较大。数据恢复时,一般采用RDB+AOF的模式来实现,RDB作为基准数据,叠加快照之后的AOF数据,完成完整的数据恢复。

Keys和Scan方法

Keys会全表扫描,对于单线程的Redis,容易出现卡顿,影响性能。
Scan采用类似分页获取的方式,虽然实现代码复杂一点,而且有可能数据重复,但是不会有性能问题。
一般生产库,都会禁用keys命令。

Redis vs Memcached

特性 Redis Memcached
性能 单线程非阻塞异步IO,避免线程切换 多线程异步IO,可利用多核
持久化 支持,可作为NoSQL数据库 不支持,有效期最长30天
数据结构 支持5种 只支持简单数据结构
限制 - Key 250字节,Value 1M,缓存30天
HA 主从、Cluster 不支持
Image placeholder
吴相忘
未设置
  52人点赞

没有讨论,发表一下自己的看法吧

推荐文章
如何保证缓存与数据库的双写一致性?

分布式缓存是现在很多分布式应用中必不可少的组件,但是用到了分布式缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?CacheAsidePa

阿里面试题:如何保证缓存与数据库的双写一致性?

作者:你是我的海啸出处:https://blog.csdn.net/chang384915878/article/details/86756463只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只

如何设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析

前言设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。缓存穿透缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则

如何构建批流一体数据融合平台的一致性语义保证?

一、批流一体架构 批和流是数据融合的两种应用形态 下图来自Flink官网。传统的数据融合通常基于批模式。在批的模式下,我们会通过一些周期性运行的ETLJOB,将数据从关系型数据库、文件存储向下游的目标

一致性哈希算法 PHP 实现

一致性哈希算法(consistenthashing)PHP实现本文转载于 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hotspo

MySQL是怎么保证数据一致性的

在《写数据库同时发mq消息事务一致性的一种解决方案》一文的方案中把分布式事务巧妙转成了数据库事务。我们都知道关系型数据库事务能保证数据一致性,那数据库到底是怎么设计事务这一特性的呢?一、MySQL事务

陆天炜: GoldenDB事务一致性处理机制优化历程

前言:GoldenDB是中兴通讯推出的一款自研的金融级交易型分布式数据。针对金融行业关注的数据库事务一致性问题,中兴通讯GoldenDB分布式数据库架构师陆天炜,在DTCC2019数据库大会上做了干货

数据库大牛李海翔详解全局读一致性技术

作者简介:李海翔,网名“那海蓝蓝”,腾讯金融云数据库技术专家。中国人民大学信息学院工程硕士企业导师。著有《数据库事务处理的艺术:事务管理和并发访问控制》、《数据库查询优化器的艺术:原理解析与SQL性能

从 GFS 失败的架构设计来看一致性的重要性

作者简介陈东明,饿了么北京技术中心架构组负责人,负责饿了么的产品线架构设计以及饿了么基础架构研发工作。曾任百度架构师,负责百度即时通讯产品的架构设计。具有丰富的大规模系统构建和基础架构的研发经验,善于

写数据库同时发mq消息事务一致性的一种解决方案

一、引子《事务注解(@Transactional)引起的数据覆盖故障》一文收到不少反馈。事务里不要有rpc,基本原则,sb封装的太好了,把很多人养傻了,function级别的事务,坑太大。网友一这个是

Talos网卡负载优化:基于个性化一致性哈希的负载均衡

本文将详细介绍基于个性化一致性哈希的流量均衡方法。 目录  业务增长带来的流量均衡需求基于一致性哈希的调度策略个性化一致性哈希的负载均衡流量均衡在Talos中的实现前文《小米消息队列的实践》介绍了小米

jQuery 学习笔记

jQuery中的dom追加操作方法(主动/被动、前面/后面、里面/外面): append:主动/后面/里面 appendTo:被动/后面/里面 prepend:主动/前面/里面 prependTo:被

流畅的python学习笔记-第4章

第4章文本和字节序列[toc]编码和解码markdom可以插入emoji表情包其中作为前缀,x1f54为对应表情的unicode编码🥺emoji 把码位转换成字节序列的过程是编码;把字节序列转换成码位

流畅的python学习笔记-第5章

第5章函数[toc]函数在python中一切都可以视作为对象,包括函数deffunction_try(): '''itisfuncitontrydoc''' print('function_tryd

软件工程学习笔记(一):软件工程

1计算机软件1.1软件计算机软件是指计算机系统中的程序以及文档,程序是计算任务处理对象和处理规则的描述.1.2软件特点 一种逻辑实体. 维护工作量大. 维护软件过程中会引入副作用. 1.3软件分类1.

go学习笔记-goroutine竞争状态

如果两个或者多个goroutine在没有相互同步状态的情况下同时访问某个资源,并且同时对这个资源进行读写的时候,对于这个资源就处于相互竞争状态(racecandition)。下面来看一个相互竞争的例子

深入浅出百亿请求高可用Redis(codis)分布式集群揭秘

摘要:作为noSql中的kv数据库的王者,redis以其高性能,低时延,丰富的数据结构备受开发者青睐,但是由于redis在水平伸缩性上受限,如何做到能够水平扩容,同时对业务无侵入性是很多使用redis

Redis 6.0 新特性之集群代理

Redis6.0releasenotes ======================= UpgradeurgencyLOW:ThisisthefirstRCofRedis6. Introduct

《MySQL主从不一致情形与解决方法》

一、MySQL主从不同步情况1.1网络的延迟由于mysql主从复制是基于binlog的一种异步复制通过网络传送binlog文件,理所当然网络延迟是主从不同步的绝大多数的原因,特别是跨机房的数据同步出现

干货 | 揭秘京东数科强一致、高性能的分布式事务中间件JDTX

导读:在分布式数据库、云原生数据库、NewSQL等名词在数据库领域层出不穷的当今,变革——在这个相对稳定的领域已愈加不可避免。相比于完全革新,渐进式增强的方案在拥有厚重沉淀的行业则更受青睐。同所有分布

influxDB集群模式实践

influxDB数据库以其优秀的时序数据存储和查询能力,在处理和分析类似资源监控等时序数据方面得到了广泛的应用。而influxDB自带的各种特殊函数如求平均值、标准差、随机取数据,使数据分析变得十分方

美团点评Kubernetes集群管理实践

背景作为国内领先的生活服务平台,美团点评很多业务都具有非常显著、规律的“高峰”和“低谷”特征。尤其遇到节假日或促销活动,流量还会在短时间内出现爆发式的增长。这对集群中心的资源弹性和可用性有非常高的要求

PB级数据实时查询,滴滴Elasticsearch多集群架构实践

Elasticsearch是基于Lucene实现的分布式搜索引擎,提供了海量数据实时检索和分析能力。Elastic 公司开源的一系列产品组成的ElasticStack,可以为日志服务、搜索引擎、系统监

如何在生产环境中部署Kubernetes集群?

如果把kubernetes集群应用于生产环境中,需要做哪些准备?我们先要弄清楚一个词,什么是“生产就绪”?生产就绪,是我们经常听到的一个专业术语,大概意思是已经准备好并已处于即将生产的状态。但是,到底

运营商大规模数据集群治理的实践指南

写在开头的话Q: 军哥,你们运营商行业的大规模集群,都有啥特点啊?A: 我们集群主要是承载B域、信令和互联网日志等去标识化数据,简单的说,有三个特点:1)集群规模较大:数千节点规模,近百PB数据量,日