• Redis核心原理-基础和应用篇


    2020,到新公司这一年多以来,更新文章和总结知识的习惯被丢掉了。我复盘了下自己,原因不是公司技术氛围不好,也不是每天业务需求太多,其根本原因还是---惰性。作为我们技术人随着年龄的增长,精力也会被生活中许多琐碎的事情分散,但我们不应该忘记当初写下第一行代码时的初衷。我们一定要明白持之以恒、长远规划、阶段性复盘的重要性。2021新的一年,新的心态,新的目标,GO! GO! GO!!!

    本文是读钱文品《Redis深度历险》的读书笔记

    一、redis应用

    1、记录帖子点赞数、评论数和点击数(hash)

    2、记录用户的帖子ID列表,便于快速显示用户的帖子列表(zset)

    3、记录帖子的标题、摘要、作者和封面信息,用户列表页展示(hash)

    4、记录帖子的点赞用户ID列表,评论ID列表,用于显示和去重计数(hash)

    5、缓存近期热帖内容(帖子内容的空间占用比较大),减少数据库压力(hash)

    6、记录帖子的相关文章ID,根据内容推荐相关帖子(list)

    7、如果帖子ID是整数自增的,可以使用redis来分配帖子ID(计数器)[计数系统要考虑:防作弊、按照不同维度计数,数据持久化到底层数据源等。]

    8、收藏集和帖子之间的关系(zset)

    9、记录热榜帖子ID列表、总热榜和分类热榜(zset)

    10、缓存用户行为历史,过滤恶意行为(zset、hash)

    11、保证同一用户不会中奖两次(set)

    12、登录时获取短信验证码限速(string)

    13、分布式系统,缓存用户登录信息

    二、redis数据结构

    string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)

    1. string(字符串)

    其内部字符串是一个动态字符串,类似于ArrayList的动态扩容。以此减少频繁分配内存的开销。字符串长度小于1M时,成倍扩容;大于1M时,只增加1M;最大长度512M。

    使用场景:

    缓存用户登录信息。token作为key,用户信息使用JSON序列化成字符串,获取时再反序列化。

    常用命令

    set name value  #存值
    get name    #取值
    exists name     #判断
    del name
    mset name1 name2 value1 value2   #批量取
    mget name1 name2 .....   #批量取
    expire name m秒    #m秒后过期
    setex name m秒 value    #存值,并且设置过期时间
    setnx name value    #存值,如果name已经存在,就返回0
    set name 1  #设置为整数
    incr name   #还可以自增
    incrby name 整数   #加
    
    • setex : 如果 key 已经存在, SETEX 命令将覆写旧值
    • setnx : 若给定的 key 已经存在,则 SETNX 不做任何动作。

    2. list(列表)

    类似于LinkedList 双向链表。插入删除块,查询慢。

    使用场景

    • lpush 、lpop 栈(stack)
    • lpush 、rpop 队列(queue)
    • lpush 、brpop 消息队列

    常用命令

    blpop key timeout # 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时返回nil或发现可弹出元素为止。
    
    lpush key value1...value #先进后出,将一个或多个元素插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。
    
    rpush key value1...value... #先进先出,在列表的尾部插入一个或多个元素
    
    llen key #队列长度
    
    lpop key  # 移除并返回列表第一个元素
    
    rpop key # 移除并返回列表最后一个元素
    
    lrange key start end # 根据索引获取区间元素
    

    3. hash(字典)

    类似于HashMap

    使用场景

    可以对存储结构中每个字段单独存储。过期时间是针对真个hash对象,而不是单独的子key.

    常用命令

    hset key filed1 value1
    hset key filed2 value2   #存 
    hget key filed1     #取
    

    4. set(集合)

    sadd, smembers, scard

    5. zset(有序集合)

    zadd, zrange,zrank, zrem,zcard

    三、HyperLogLog

    • 场景:估数、精确度要求不高场景(统计网站的PV 和UV)
    • 命令 pfadd、pfcount、pfmerge
    • 内存占用比set小,有一定的误差

    四、布隆过滤器

    • 原理:布隆过滤器是一个BIT数组
    • 场景:信息推荐去重(微博推荐刷新时过滤已经看过的信息),垃圾邮件过滤、爬虫系统过滤已爬内容、解决缓存穿透问题
    • 布隆过滤器可以判断某个数据一定不存在,但是无法判断一定存在(不精确的SET)
    • 占用内存极少,并且插入和查询速度都足够快。
    • 缺点,无法删除数据;随着数据的增加,误判率会增加
    • Redisson 实现

    五、Reids6种淘汰策略

    • volatile-lru:从设置了过期时间的数据集中,选择最近最久未使用的数据释放;
    • allkeys-lru:从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;
    • volatile-random:从设置了过期时间的数据集中,随机选择一个数据进行释放;
    • allkeys-random:从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放;
    • volatile-ttl:从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;
    • noeviction:不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误。

    默认策略是noeviction

    推荐使用的策略是volatile-lru

    通过maxmemory-samples配置样本数量,默认为5

    缓存淘汰算法(LFU、LRU、ARC、FIFO、MRU)

    六、Redis 持久化方案:

    • RDB 默认方式 (RDB持久化即通过创建快照的方式进行持久化,保存某个时间点的全量数据。)
    • AOF (Append-Only-File持久化即记录所有变更数据库状态的指令,以append的形式追加保存到AOF文件中)
    • 如果Redis只是用来做缓存服务器,比如数据库查询数据后缓存,那可以不用考虑持久化,因为缓存服务失效还能再从数据库获取恢复。

    七、缓存和数据库数据一致性(并发竞争问题)

    • 延时双删策略(在写库前、后进行redis.del,并且设定合理的延时时间。)
    • 读取binlog分析 ,利用消息队列(rabbitmq、kafka), 推送更新各台的redis缓存数据

    八、缓存穿透

    • 现象:用户大量并发请求的数据(key)对应的数据在redis和数据库中都不存在,导致尽管数据不存在但还是每次都会进行查DB。
    • 解决方案:从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null

    九、缓存击穿

    • 现象:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
    • 解决方案:

    1.设置热点数据永远不过期

    2.接口限流与熔断,降级

    3.加互斥锁

    4.布隆过滤器

    十、缓存雪崩

    • 现象:大量key同一时间点失效,同时又有大量请求打进来,导致流量直接打在DB上,造成DB不可用。
    • 解决方案:

    1.设置key永不失效(热点数据);

    2.设置key缓存失效时候尽可能错开;

    3.使用多级缓存机制,比如同时使用redsi和memcache缓存,请求->redis->memcache->db;

    4.购买第三方可靠性高的Redis云服务器;

    十一、Redis热点key处理

    1 热点key发现

    • 监控热key(抓包程序抓redis监听端口的数据,抓到数据后往kafka里丢。接下来,flink流式计算系统消费kafka里的数据,进行数据统计即可)
    • 通知系统做处理

    2 解决方案

    • 本地缓存(利用ehcache或HashMap将发现的热key加载到jvm,热key直接走jvm查询)

    • 集群(把这个热key,在多个redis上都存一份)

    • 阿里云Redis已经在内核层面解决热点key问题

    3. 热key的危害

    • 流量集中,达到物理网卡上限。

    • 请求过多,缓存分片服务被打垮。

    • DB 击穿,引起业务雪崩。

    十二、拒绝大KEY

    • 集群环境,大key会导致数据迁移卡顿
    • 如果被删除时,内存一次性回收,也会卡顿
    • 扩容时,会一次性申请更大的内存,也会卡顿
    • 注意:如果Redis内存起伏较大,很有可能是大key导致,这时需要定位大key并优化
    • 定位大key可以使用scan、或者redis-cli指令完成

    十三、Redis是单线程的,但Redis为什么这么快

    • 1、基于内存
    • 2、数据结构和操作简单
    • 3、多路I/O复用模型(非阻塞IO),Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间,
    • 4、单线程避免了不必要的上下文切换和竞争条件

    Redis是单线程来处理命令的,所以一条命令从客户端达到服务端不会立刻被执行,所有命令都会进入一个队列中,然后逐个被执行。

    十四、漏斗限流

    分布式限流:redis-cell

    单机:Google的guava包提供了RateLimiter类

    限流的常见算法有以下三种:
    1.时间窗口算法
    2.漏桶算法
    3.令牌算法

    十五、GEO

    • Redis通过GeoHash算法实现附近的人查询功能;
    • 内部数据结构是zset,通过score还原就可以得到原始坐标;
    • 集群环境中单个key对应的数据不宜超过1M,如果超过需要按相应业务规则拆分降低key的数据大小。

    十六、scan

    • 通过游标分步进行,相比于keys,不会阻塞线程
    • 提供limit参数可以控制返回结果条数
  • 相关阅读:
    Ray聊天记录
    ServiceFabric极简文档-5.0 Service Fabric有状态与无状态
    ServiceFabric极简文档-5.1 编程模型选择
    Java网络编程学习A轮_08_NIO的Reactor模型
    Java网络编程学习A轮_07_基于Buffer的Socket编程
    Java网络编程学习A轮_06_NIO入门
    Java网络编程学习A轮_05_Socket编程
    Spring IOC 源码简单分析 04
    Spring IOC 源码简单分析 03
    Spring IOC 源码简单分析 02
  • 原文地址:https://www.cnblogs.com/monkjavaer/p/14244021.html
Copyright © 2020-2023  润新知