Redis知识总结

Redis数据类型和应用场景

  1. String
  • 内部实现

image
- 优势:
1. 获取字符串长度的复杂度为O(1),结构内len属性记录字符串长度
2. 既可以保存文本数据,又可以保存二进制数据;
3. 拼接字符串不会造成缓冲区溢出,先检查空间是否满足要求
4.
- 编码方式为 embstr,int,raw

  • 使用场景

    1. 缓存对象:对象的JSON
    2. 计数器:文章阅读量 转发次数 库存
    3. 共享Session信息
    4. 分布式锁
    SETNX key value//不存在则set成功 否则失败
    SETEX key seconds value
    
 @Resource
RedisTemplate<String, String> redisTemplate;

public void updateUserWithRedisLock(SysUser sysUser) throws InterruptedException {
 // 占分布式锁,去redis占坑
 Boolean lock = redisTemplate.opsForValue()
                             .setIfAbsent("SysUserLock" + sysUser.getId(), 
                               "value");
 if(lock) {
   //加锁成功... 执行业务

   redisTemplate.delete("SysUserLock" + sysUser.getId());   //删除key,释放锁
 } else {
   Thread.sleep(100);   // 加锁失败,重试
   updateUserWithRedisLock(sysUser);
 }
}

问题:

  • 异常导致锁没有释放: 为redis的key设置过期时间
  • 锁过期之后被别的线程重新获取与释放: 怎么判断这把锁是不是自己的?加锁时为value赋随机值,加锁的随机值等于解锁时的获取到的值,才能证明这把锁是你的。image
  1. Hash
  • 内部实现
    压缩列表
    哈希表
  • 应用场景
  1. List
  • 内部实现

    1. 双向链表
      list封装了双向链表
      头结构:
      typedef struct list {
        //链表头节点
        listNode *head;
        //链表尾节点
        listNode *tail;
        //节点值复制函数
        void *(*dup)(void *ptr);
        //节点值释放函数
        void (*free)(void *ptr);
        //节点值比较函数
        int (*match)(void *ptr, void *key);
        //链表节点数量
        unsigned long len;
      } list;
      
    2. 压缩链表

    当个数小于512个,每个元素的值小于64字节

    • 节约内存 由连续内存块组成的顺序数据结构
    • 数据结构:zlbytes占用字节数 zltail尾部节点距起始地址的字节 zlen节点数 zlend结束符
    • 每个节点数据结构:prevlen encoding data 根据不同类型和大小分配空间
    • 导致连锁更新问题 更新某个节点超过分配内存则会导致内存重新分配 影响性能
  • 应用场景
    消息队列

  1. Set
    哈希表或整数集合
  • 哈希表是一个数组(dictEntry **table),数组的每个元素是一个指向「哈希表节点(dictEntry)」的指针。
    image

  • 整数集合
    一块连续内存空间, contents 数组

  • 应用场景
    聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。

  1. Zset
    压缩列表或跳表
  • 跳表的优势是能支持平均 O(logN) 复杂度的节点查找。
    image
    Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
  1. bitmap
    用1bit来表示value

线程模型

Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」
「关闭文件、AOF 刷盘、释放内存」是后台线程

Redis快的原因:

  • 在内存中完成,CPU不是瓶颈
  • 单线程模型没有多线程竞争 以及线程切换的开销
  • IO多路复用一个线程处理多个请求

内存淘汰策略

  • 删除过期key 定期删除+惰性删除
  • 内存淘汰机制
    1. 在设置了过期时间的数据中进行淘汰:随机淘汰;优先淘汰最早过期的;优先淘汰最近最久未使用的;优先淘汰使用次数最少的
    2. 所有数据淘汰:随机淘汰任意键值;
      淘汰整个键值中最久未使用的键值;淘汰整个键值中最少使用的键值。

Redis持久化方式

  • AOF日志
    每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
  • RDB快照
    将某一时刻的内存数据,以二进制的方式写入磁盘;
    用fork()创建子线程来进行快照,此时子进程和父进程是共享同一片内存数据的写时复制,主进程写时会复制一份副本数据,子进程会把该副本数据写入 RDB 文件
  • 混合持久化
    1. RDB 优点是数据恢复速度快,但是快照的频率不好把握。
    2. AOF 优点是丢失数据少,但是数据恢复不快。RDB+AOF增量部分

缓存雪崩 穿透 击穿

  • 如何避免缓存雪崩?

    1. 将缓存失效时间随机打散:
    2. 设置缓存不过期:
  • 如何避免缓存击穿?

    1. 互斥锁方案
    2. 不给热点数据设置过期时间
  • 如何避免缓存穿透?

    1. 非法请求的限制:
    2. 设置空值或者默认值:
    3. 使用布隆过滤器快速判断数据是否存在在数据库中。避免查询数据库

数据库和缓存一致性

  • 缓存更新策略

    1. Cache Aside(旁路缓存)策略
      image
      不能先删除缓存再更新数据库,否则会有数据库缓存不一致
    2. Read/Write Through(读穿 / 写穿)策略
      应用程序只和缓存交互,不再和数据库交互,而是由缓存和数据库交互,相当于更新数据库的操作由缓存自己代理了。
    3. Write Back(写回)策略
  • 保证缓存和数据库数据的一致性

    1. 先更新数据库,再删除缓存
      因为缓存的写入通常要远远快于数据库的写入
      「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。给缓存数据加上了「过期时间」

    删除失败解决方法:

    • 重试机制。
    • 订阅 MySQL binlog,再操作缓存。
    1. 先删除缓存,再更新数据库」:「延迟双删」
    2. 「更新数据库 + 更新缓存」:在更新缓存前先加个分布式锁

Redis高可用 集群方案

  • 主从复制
    一主多从 读写分离

  • 哨兵模式

    1. 提供主从节点故障转移的功能。
    2. 监控、选主、通知。
    3. ping主节点 如果客观下线则从哨兵中选举新的主节点
    4. 主从故障转移
  • 切片集群

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值