redis学习笔记

数据结构
String (二进制安全)
  • 底层结构是array(类似与java的 ArrayList)
  • 内容小于1M时扩容是加倍现有的空间
  • 大于1M时 每次扩容1M,最大长度512M
List
  • 元素较少时会为元素分配一块连续的内存(ziplist)
  • 元素较多时会将多个块(ziplist)组成双向链表(quicklist),块内是连续存储的(节省内存且保持插入性能)
Set
  • 底层是通过字典(Hash表)来实现的
Hash
  • 当field-value长度较短且个数较少时用ziplist
  • 否则使用hashtable
Zset

数据结构包含包含两部分

  • hash结构,用于关联value和权重score
  • 跳跃表(skiplist) ,目的在于给value排序,根据score的范围获取元素列表
事务
乐观锁

redis事务的实现方式,在更新前判断有没有别的事务已经改过这个数据(版本号机制),适用于多读的情况

redis事务的三个特性
  • 单独的隔离操作(单线程的原因,命令执行时不会因为线程调度被打断)
  • 没有隔离级别,事务被提交之前任何指令都不会被实际执行(实际上可以理解为只有串行化)
  • 不保证原子性,命令执行失败时不会回滚,不影响后续指令的执行
持久化

官方建议RDB与AOF同时开启(启动时优先读取AOF文件),对数据不敏感的可以只用RDB,不建议单独开启AOF

RDB(默认开启)

fork一个子进程,会先将数据写入一个临时文件,待持久化结束再替换上次的备份文件(写时复制技术)

  • 优点:恢复速度快,节省磁盘空间(适合大规模数据)
  • 缺点:最后一次备份后的数据可能会丢失数据(适合完整性和一致性要求不高的应用),临时文件的存在使得redis的最大占用内存只能为物理内存的一半,备份消耗性能,存在一些bug(?待了解)
AOF(默认关闭)

以日志的方式记录写操作(增量保存),记录redis所有执行过的写指令,只允许追加文件但不允许改写文件

  • 优点:丢失数据的概率更低,备份是可读的日志文件
  • 缺点: 占用更多的磁盘空间,备份恢复速度慢,always模式下会有性能压力,
  • 异常恢复

redis可以对损坏的aof文件进行修复 [redis-check-aof --fix]

  • AOF 同步频率
  • appendfsync always 始终同步,每次redis写入时会立即记入日志,性能差但数据完整性好
  • appendfsync everysec 每秒钟计入日志一次,如果宕机最后一秒的数据可能丢失
  • appendfsync no redis不主动同步,把同步时机交给操作系统(原理?)
  • AOF重写压缩操作

AOF文件持续增长过大时会fork出一条新进程来将文件重写,本质是把ADB附在AOF文件头部

  • AOF持久化流程
  • Client的写命令会被追加到AOF的缓冲区内
  • AOF缓冲区根据AOF持久化策略将操作sync到磁盘AOF文件中
  • AOF文件大小超过重写策略时,会对AOF文件rewrite,压缩AOF容量
  • 当redis服务重启的时候会重新加载AOF文件中的写操作达到数据恢复的目的
主从复制
全量复制

TODO

增量复制

TODO

哨兵模式
master选举规则
  1. 选择优先级高的【redis.conf->replica-priority】
  2. 选择偏移量最大的(保证slave数据是最新的)
  3. 选择runid(redis启动时随机生成)最小的
redis集群(无中心化集群)
插槽(16383个)

【CRC(key)%16384】来计算key属于哪个插槽

故障恢复
  • 主机挂掉后,从机会作为主机提供服务
  • 如果主从都挂了,且【redis.conf->cluster-require-full-coverage】为yes,那么整个集群都挂掉
  • 如果为no,那么该段插槽全都不能使用也无法存储
缓存
缓存穿透

应用服务器压力变大,redis命中率降低,一直查询数据库
【解决方案】

  • 对空值进行缓存,对于在db中查询结果即使是不存在的数据(null)也进行缓存
  • 设置访问的名单,利用redis的bitmaps类型,名单id作为bitmaps的偏移量,每次访问时看id是否在bitmaps中,不在就不允许访问,缺点是每次都要访问bitmaps效率不高
  • 采用布隆过滤器,底层使用一个bitmaps和一系列哈希函数来检索是否在集合中,缺点是存在一定的误识别率和删除困难
  • 进行实时监控,设置黑名单限制访问
缓存击穿

redis中的某个key过期了,服务器压力突然增大,大量访问这个key
【解决方案】

  • 预先设置热门数据,在redis访问高峰前提前缓存
  • 实时调整过期时间,热点数据的过期时间增大
  • 加锁,对某个不存在的key同一时刻只允许一个线程loaddb(可以利用sentnx)
缓存雪崩

短时间内key大量过期,数据库压力变大而崩溃
【解决方案】

  • 多级缓存架构,nginx缓存+redis缓存+其他缓存(ehcache等)
  • 使用锁或者队列,避免大量的请求落在db上,不适合高并发
  • 设置过期标志更新缓存,缓存过期时触发另外的线程去更新缓存
  • 分散缓存失效时间,可以在缓存实现时间设置一个随机值
分布式锁

分布式锁的主流实现方式

  • 基于数据库实现分布式锁
  • 基于缓存(redis)实现(性能最高)
  • 基于ZooKeeper(可靠性最高)
Redlock算法

目标:互斥、无死锁、容错(大部分redis节点还活着就可以获取锁释放锁)
【算法】

  1. 获取当前系统时间(ms),作为开始获取锁的时间
  2. 尝试从N个master中使用相同的Key和随机值获取锁,设置一个网络超时时间且小于锁的超时时间,避免redis挂了client还在等待
  3. client在获取到锁的时候计算:
    获取锁使用的时间=当前时间-开始获取锁的时间
    当且仅当大多数(半数以上)的redis获取到锁且获取锁的时间小于锁超时时间,锁才算获取成功
  4. 如果获取到了锁,则:
    key真正有效时间=有效时间-获取锁的时间
  5. 如果锁获取失败(重试防止脑裂),则client在所有的redis上解锁
  • 问题:

redlock只保证了锁的高可用性,无法保证锁的正确性,例如:
在获取锁后提交前被阻塞,锁超时失效,此时另一个线程获得锁并提交 ,那么就会发生两次提交。

实际上作者提供了一个解决思路,client在获得锁的同时获取一个全局递增的token值,在提交时如果当前的token值小于上一次提交的token值就会被拒绝提交,看起来还是zookeeper好用

你可能感兴趣的:(redis学习笔记)