redis知识总结

一、Redis

1. 简介

单线程为什么这么快?

  1. 纯内存
  2. 非阻塞IO
  3. 避免线程切换和竞争消耗

单线程Redis注意事项

  1. 一次只运行一条命令
  2. 拒绝长(慢)命令,例如:keys、flushall、flushdb、slow lua script、mutil/exec、operate big value(collection)
  3. Redis其实不是单线程,fysnc file descriptor进行持久化

Redis特点

  1. 速度快
  2. 持久化
  3. 多钟数据结构
  4. 支持多种编程语言
  5. 功能丰富
  6. 简单
  7. 主从复制
  8. 高可用,分布式

补充知识:同步非同步,阻塞非阻塞

1. 同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。

异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

总结:同步和异步主要关注的是不是调用函数自己返回调用结果,同步是调用者返回调用结果,异步是调用发出后立马返回空值,由回调函数返回调用结果

2.阻塞与非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

2. 应用场景

缓存系统、排行榜、计数器、社交网络、消息队列系统、实时系统

3. 数据类型

支持的数据类型:字符串、列表、集合、字典(hash)、有序集合(ZSET)

4.持久化

  1. Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。

  2. RDB (Redis Database Backup)持久化适合大规模的数据恢复但它的数据一致性和完整性较差。

    针对RDB方式的持久化,可以使用:

    • save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。
    • bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。

      Redis借助了fork命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件。

      另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

      而且由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,必须采用AOF持久化方式。

  3. RDB持久化原理:

  • Redis调用fork(),产生一个子进程。
  • 父进程继续处理client请求,子进程把内存数据写到一个临时的RDB文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
  • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出
  1. Redis 需要手动开启AOF(Append Only File)持久化方式,默认是每秒将写操作日志追加到AOF文件中。
  2. AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。
  3. AOF原理:
    • redis调用fork ,现在有父子两个进程;
    • 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令;
    • 父进程继续处理client请求,除了把写命令写入到原来的AOF文件中,同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题;
    • 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件;
    • 现在父进程可以使用临时文件替换老的AOF文件,并重命名,后面收到的写命令也开始往新的AOF文件中追加。
  4. 日志重写
      随着写操作的不断增加,AOF文件会越来越大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。
    工作原理如下:
    • Redis调用fork(),产生一个子进程。
    • 子进程把新的AOF写到一个临时文件里。
    • 主进程持续把新的变动写到内存里的buffer,同时也会把这些新的变动写到旧的AOF里,这样即使重写失败也能保证数据的安全。
    • 当子进程完成文件的重写后,主进程会获得一个信号,然后把内存里的buffer追加到子进程生成的那个新AOF里。

5.基本指令

  1. string
1
set key value [EX seconds] [PX ms] [nx|xx]
  • key: 键名
  • value: 键值
  • ex seconds: 键秒级过期时间
  • ex ms: 键毫秒及过期时间
  • nx: 键不存在才能设置,setnx和nx选项作用一样,用于添加,分布式锁的实现
  • xx: 键存在才能设置,setxx和xx选项作用一样,用于更新
  1. list

    rpush:右边插入

    lrange list-key start end:列出start到end范围的key值

  2. set

    sadd set-key item:set插入值

    smembers set-key:列出set的key值

    sismember set-key item4:查看是否set的member

    srem set-key item2:set remove key

  3. hash

    hset user name LotusChing:设置user的name为lotus

    hset user age 21

    hget user name:获取user的name

    hkeys user:获取user的key值

    hvals user:获取user的键值

    hdel user age:删除hash中某个键

    hlen user:获取user的键值对个数

    hmset user name “LotusChing” age 21 gender “Male”:批量设置key-value

    hmget user name age gender:批量获取value

  4. zset

    zadd zset-key 728 member1:加入值为728,key为member1的键值对

什么是 RedLock

Redis 官方站这篇文章提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它可以保证以下特性:

  1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
  2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
  3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务

redlock的实现方式

1
set key value nx px

Redlock 算法

算法很易懂,起 5 个 master 节点,分布在不同的机房尽量保证可用性。为了获得锁,client 会进行如下操作:

  1. 得到当前的时间,微妙单位
  2. 尝试顺序地在 5 个实例上申请锁,当然需要使用相同的 key 和 random value,这里一个 client 需要合理设置与 master 节点沟通的 timeout 大小,避免长时间和一个 fail 了的节点浪费时间
  3. 当 client 在大于等于 3 个 master 上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用获得锁的当下时间减去第一步获得的时间戳得到,如果锁的持续时长(lock validity time)比流逝的时间多的话,那么锁就真正获取到了。
  4. 如果锁申请到了,那么锁真正的 lock validity time 应该是 origin(lock validity time) - 申请锁期间流逝的时间
  5. 如果 client 申请锁失败了,那么它就会在少部分申请成功锁的 master 节点上执行释放锁的操作,重置状态

放锁

放锁操作很简单,就是依次释放所有节点上的锁就行了

性能、崩溃恢复和 fsync

如果我们的节点没有持久化机制,client 从 5 个 master 中的 3 个处获得了锁,然后其中一个重启了,这是注意 整个环境中又出现了 3 个 master 可供另一个 client 申请同一把锁! 违反了互斥性。如果我们开启了 AOF 持久化那么情况会稍微好转一些,因为 Redis 的过期机制是语义层面实现的,所以在 server 挂了的时候时间依旧在流逝,重启之后锁状态不会受到污染。但是考虑断电之后呢,AOF部分命令没来得及刷回磁盘直接丢失了,除非我们配置刷回策略为 fsync = always,但这会损伤性能。解决这个问题的方法是,当一个节点重启之后,我们规定在 max TTL 期间它是不可用的,这样它就不会干扰原本已经申请到的锁,等到它 crash 前的那部分锁都过期了,环境不存在历史锁了,那么再把这个节点加进来正常工作。

Redlock可靠吗

不可靠,因为万一多个节点之间的通信延迟,就会出错。可靠的方式是使用zookeeper和fencing token(每次获取锁共享变量+1)。