Redis

Nosql

nosql无需事先存储的数据建立字段,

什么是redis?

Redis是一款开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存也可持久化的日志型、Key-Value高性能数据库。Redis与其他Key-Value缓存产品相比有以下三个特点:
1. 支持数据持久化,可以将内存中的数据保存在磁盘中,重启可再次加载使用
2. 支持简单的Key-Value类型的数据,同时还提供List、Set、Zset、Hash等数据结构的存储
3. 支持数据的备份,即Master-Slave模式的数据备份
同时,我们再看下Redis有什么优势:
(1) 读速度为110000次/s,写速度为81000次/s,性能极高
(2) 具有丰富的数据类型,这个上面已经提过了
(3) Redis所有操作都是原子的,意思是要么成功执行要么失败完全不执行,多个操作也支持事务
(4) 丰富的特性,比如Redis支持publish/subscribe、notify、key过期等
Redis就是一个单进程以epoll包装 大量的多路io复用。

Redis安装

wget http://download.redis.io/releases/redis-3.2.11.tar.gz,
解压后 进入目录make编译。
Root用户可以通过在./etc/profile 添加环境变量(可选)PATH:HOME/.local/bin:$HOME/bin:/data/component/redis/redis-3.2.11/src
记得要source /etc/profile
非root就在 ~/.bash_profile文件添加 添加前最好备份一下文件。
通过src目录下redis-server redis.config 启动redis:
Redis - 图1

修改redis.conf的daemonize yes 为后台启动
之后可以使用ps -ef | grep redisnetstat -ant | grep 6379命令来验证Redis已经启动
Redis - 图2

Redis登录授权

没有密码的redis是非常不安全的 修改redis.conf文件requirepass
Redis - 图3
通过redis-cli -h 127.0.0.1 -p 6379 -a 123456登录
或者redis-cli -h 127.0.0.1 -p 6379auth 123456 连接后授权
如果不填密码登录就会:
Redis - 图4

Redis配置文件redis.conf

Redis.conf详细配置信息

bind

当配置了bind之后:
l 只有bind指定的ip可以直接访问Redis,这样可以避免将Redis服务暴露于危险的网络环境中,防止一些不安全的人随随便便通过远程访问Redis
l 如果bind选项为空或0.0.0.0的话,那会接受所有来自于可用网络接口的连接-127.0.0.1

protected-mode-protected-mode

是Redis3.2之后的新特性,用于加强Redis的安全管理,当满足以下两种情况时,protected-mode起作用:
· bind未设置,即接收所有来自网络的连接
· 密码未设置
当满足以上两种情况且protected-mode=yes的时候,访问Redis将报错,即密码未设置的情况下,无密码访问Redis只能通过安装Redis的本机进行访问-yes

port-Redis

访问端口,由于Redis是单线程模型,因此单机开多个Redis进程的时候会修改端口,不然一般使用大家比较熟悉的6379端口就可以了-6379

tcp-backlog

半连接队列的大小,对半连接队列不熟的可以看我以前的文章TCP:三次握手、四次握手、backlog及其他-511

Timeout

指定在一个client空闲多少秒之后就关闭它,0表示不管-0

tcp-keepalive

设置tcp协议的keepalive,从Redis的注释来看,这个参数有两个作用:
· 发现死的连接
从中间网络设备的角度看连接是否存活-300

Daemonize

这个前面说过了,指定Redis是否以守护进程的方式启动-no

Supervised

这个参数表示可以通过upstart和systemd管理Redis守护进程,这个具体和操作系统相关,资料也不是很多,就暂时不管了-no

Pidfile

当Redis以守护进程的方式运行的时候,Redis默认会把pid写到pidfile指定的文件中-/var/run/redis_6379.pid

Loglevel

指定Redis的日志级别,Redis本身的日志级别有notice、verbose、notice、warning四种,按照文档的说法,这四种日志级别的区别是:
· debug,非常多信息,适合开发/测试
· verbose,很多很少有用的信息(直译,读着拗口,从上下文理解应该是有用信息不多的意思),但并不像debug级别这么混乱
· notice,适度的verbose级别的输出,很可能是生产环境中想要的
warning,只记录非常重要/致命的信息-notice

Logfile

配置log文件地址,默认打印在命令行终端的窗口上-“”

Databases

设置Redis数据库的数量,默认使用0号DB-16
save-把Redis数据保存到磁盘上,这个是在RDB的时候用的,介绍RDB的时候专门说这个 -save 900 1
save 300 10
save 60 10000

stop-writes-on-bgsave-error

当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。
这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。
如果Redis重启了,那么又可以重新开始接收数据了- yes

rdbcompression

是否在RBD的时候使用LZF压缩字符串,如果希望省点CPU,那就设为no,不过no的话数据集可能就比较大 -yes

Rdbchecksum

是否校验RDB文件,在RDB文件中有一个checksum专门用于校验-yes

Dbfilename

dump的文件位置-dump.rdb

Dir

Redis工作目录-./

Slaveof

主从复制,使用slaveof让一个节点称为某个节点的副本,这个只需要在副本上配置- 关闭

Masterauth

如果主机使用了requirepass配置进行密码保护,使用这个配置告诉副本连接的时候需要鉴权-关闭

slave-serve-stale-data

当一个Slave与Master失去联系或者复制正在进行中,Slave可能会有两种表现:
· 如果为yes,Slave仍然会应答客户端请求,但返回的数据可能是过时的或者数据可能是空的
如果为no,在执行除了INFO、SLAVEOF两个命令之外,都会应答”SYNC with master in progres”错误-yes

slave-read-only

配置Redis的Slave实例是否接受写操作,即Slave是否为只读Redis- yes

slave-priority

从站优先级是可以从redis的INFO命令输出中查到的一个整数。当主站不能正常工作时,redis sentinel使用它来选择一个从站并将它提升为主站。
低优先级的从站被认为更适合于提升,因此如果有三个从站优先级分别是10, 100, 25,sentinel会选择优先级为10的从站,因为它的优先级最低。
然而优先级值为0的从站不能执行主站的角色,因此优先级为0的从站永远不会被redis sentinel提升。 -100

Requirepass

设置客户端认证密码-关闭

rename-command

命令重命名,对于一些危险命令例如:
· flushdb(清空数据库)
· flushall(清空所有记录)
· config(客户端连接后可配置服务器)
· keys(客户端连接后可查看所有存在的键)
作为服务端redis-server,常常需要禁用以上命令来使得服务器更加安全,禁用的具体做法是是:
· rename-command FLUSHALL “”
也可以保留命令但是不能轻易使用,重命名这个命令即可:
· rename-command FLUSHALL abcdefg
这样,重启服务器后则需要使用新命令来执行操作,否则服务器会报错unknown command-关闭

maxclients

设置同时连接的最大客户端数量,一旦达到了限制,Redis会关闭所有的新连接并发送一个”max number of clients reached”的错误-关闭,默认10000

Maxmemory

不要使用超过指定数量的内存,一旦达到了,Redis会尝试使用驱逐策略来移除键 -关闭

maxmemory-policy

当达到了maxmemory之后Redis如何移除数据,有以下的一些策略:
· volatile-lru,使用LRU算法,移除范围为设置了失效时间的
· allkeys-lru,根据LRU算法,移除范围为所有的
· volatile-random,使用随机算法,移除范围为设置了失效时间的
· allkeys-random,使用随机算法,移除范围为所有的
· volatile-ttl,移除最近过期的数据
· noeviction,不过期,当写操作的时候返回错误
注意,当写操作且Redis发现没有合适的数据可以移除的时候,将会报错-关闭,noeviction

appendonly

是否开启AOF,关于AOF后面再说 - no

Appendfilename

AOF文件名称-appendonly.aof

appendfsync

操作系统实际写数据到磁盘的频率,有以下几个选项:
· always,每次有写操作都进行同步,慢,但是最安全
· everysec,对写操作进行累积,每秒同步一次,是一种折衷方案
· no,当操作系统flush缓存的时候同步,性能更好但是会有数据丢失的风险
当不确定是使用哪种的时候,官方推荐使用everysec,它是速度与数据安全之间的一种折衷方案-everysec

no-appendfsync-on-rewrite

aof持久化机制有一个致命的问题,随着时间推移,aof文件会膨胀,当server重启时严重影响数据库还原时间,因此系统需要定期重写aof文件。
重写aof的机制为bgrewriteaof(另外一种被废弃了,就不说了),即在一个子进程中重写从而不阻塞主进程对其他命令的处理,但是这依然有个问题。
bgrewriteaof和主进程写aof,都会操作磁盘,而bgrewriteaof往往涉及大量磁盘操作,这样就会让主进程写aof文件阻塞。
针对上述问题,可以使用此时可以使用no-appendfsync-on-rewrite参数做一个选择:
· no,最安全,不丢失数据,但是需要忍受阻塞
yes,数据写入缓冲区,不造成阻塞,但是如果此时redis挂掉就会丢失数据,在Linux操作系统默认设置下,最坏场景下会丢失30秒数据- no

auto-aof-rewrite-percentage

本次aof文件超过上次aof文件该值的百分比时,才会触发rewrite -100

auto-aof-rewrite-min-size

aof文件最小值,只有到达这个值才会触发rewrite,即rewrite由auto-aof-rewrite-percentage+auto-aof-rewrite-min-size共同保证- 64mb

aof-load-truncated

redis在以aof方式恢复数据时,对最后一条可能出问题的指令的处理方式:
· yes,log并继续
no,直接恢复失败- yes

slowlog-log-slower-than

Redis慢查询的最低条件,单位微妙,即查询时间>这个值的会被记录 - 10000

slowlog-max-len

Redis存储的慢查询最大条数,超过该值之后会将最早的slowlog剔除-128

lua-time-limit

一个lua脚本执行的最大时间,单位为ms-5000

cluster-enabled

正常来说Redis实例是无法称为集群的一部分的,只有以集群方式启动的节点才可以。为了让Redis以集群方式启动,就需要此参数。-关闭

cluster-config-file

每个集群节点应该有自己的配置文件,这个文件是不应该手动修改的,它只能被Redis节点创建且更新,每个Redis集群节点需要不同的集群配置文件-关闭,nodes-6379.conf

cluster-node-timeout

集群中一个节点向其他节点发送ping命令时,必须收到回执的毫秒数- 关闭,15000

cluster-slave-validity-factor

如果该项设置为0,不管Slave节点和Master节点间失联多久都会一直尝试failover。
比如timeout为5,该值为10,那么Master与Slave之间失联50秒,Slave不会去failover它的Master-关闭,10

cluster-migration-barrier

当一个Master拥有多少个好的Slave时就要割让一个Slave出来。
例如设置为2,表示当一个Master拥有2个可用的Slave时,它的一个Slave会尝试迁移-关闭,1

cluster-require-full-coverage

有节点宕机导致16384个Slot全部被覆盖,整个集群是否停止服务,这个值一定要改为no-关闭,yes

Redis数据类型

String

string 是Redis的最基本的数据类型,可以理解为与 Memcached 一模一样的类型,一个key 对应一个 value。string 类型是二进制安全的,意思是 Redis 的 string 可以包含任何数据,比如图片或者序列化的对象,一个 redis 中字符串 value 最多可以是 512M。

应用场景

比如登录次数校验,错误超过三次5分钟内就不让登录了,每次登录设置key自增一次,并设置该key的过期时间为5分钟后,每次登录检查一下该key的值来进行 限制登录。

常见命令

Set:将字符串值value关联到key上,原本key带没带TTL时间都会覆盖。
Get:返回key的value值,key不存在返回null,存储的不是字符串就返回错误。
Setnx:和set相同,区别 如果key存在就不做任何操作 返回0否则返回1
Setex:和set相同,区别 可以设置过期时间,set也可以设置 而setex是原子操作
Mset:可以设置多个key-value值,也可以msetnx有一个key存在不做任何操作,原子性的。
Mget:返回多个key的value值,如果key不存在返回null。,
Incr:key中的数字+1,返回增加后的值如果key不存在 就初始化0并+1
Decr:key中的数字-1,其他和incr相同。
Incrby:指定添加的值,其他和incr相同。
Decrby:指定减去的值,其他和decr相同。


关于redis的失效策略:
被动触发,即GET的时候检查一下Key是否失效
主动触发,后台每1秒跑10次定时任务(通过redis.conf的hz参数配置,默认为10,这个上文没有写),随机选择100个设置了过期时间的Key,对过期的Key进行失效

Hash

 hash 是一个键值对集合,是一个 string 类型的 key和 value 的映射表,key 还是key,但是value是一个键值对(key-value)。类比于 Java里面的 Map> 集合。

应用场景

查询的时间复杂度是O(1),用于缓存一些信息

常见命令

Hset:将哈希表key中的域field的值设置value,存在就覆盖否则创建;
语法:HSET key field value
Redis - 图5
Hget:返回哈希表key中指定域的值
语法:HGET key field
Redis - 图6
Hdel:删除哈希表key中一个或多个制定域,不存在的略过
语法:HDEL key filed [field …]
Redis - 图7
Hexists:查看哈希表key中,制定域是否存在 存在返回1 否则0.
语法:HEXISTS key field
Redis - 图8
HgetALL:返回哈希表key中,所有的域和值。
语法:HGETALL key
Redis - 图9
HINCRBY:制定域增量和incrby相同
语法:HINCRYBY key filed increment
Hkeys:返回指定key的所有域
语法: hkeys key
Redis - 图10
Hlen:返回指定key中域的数量
语法:hlen key
Redis - 图11
Hmset:同时设置多个值,和hset 相同
语法:HMSET key field value [field value …]
Hmget:同时获取多个值,和hget相同
语法:HMGET key field [field …]
Redis - 图12
Hvals:返回哈希表key的所有域和值
语法:HVALS key
Redis - 图13

List

list 列表,它是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边),它的底层实际上是个链表。
  列表有两个特点:
  一、有序
  二、可以重复

应用场景

一、栈
  通过命令 lpush+lpop
  二、队列
  命令 lpush+rpop
  三、有限集合
  命令 lpush+ltrim
  四、消息队列
  命令 lpush+brpop

常见命令

Lpush:将一个或多个value插入到key表头,如果多个value从左到右依次插入
语法:LPUSH key value [value …]
Redis - 图14
Loushx:将value插入到表头,仅当key存在时插入 否则不做任何操作。
语法:lpushx key value
Lpop:移除并返回列表key的头元素
语法:LPOP key
Redis - 图15
Lrange:返回key列表指定区间的元素 start stop都以0开始 stop设置-1表示最后一个 -2表示倒数第二个 以此类推。
语法:LRANGE key start stop
Redis - 图16
Lrem:根据count的值移除列表中 value的值的数量 count>0表示从头到尾 移除值为 value的, 数量为count ;count<0同理
语法:LREM key count value
Redis - 图17
Lset:指定坐标的位置 修改为value
语法:LSET key index value
Redis - 图18
Lindex:返回指定key列表 下表为index的元素
语法:LINDEX key index
Redis - 图19
Linsert:指定value的值插入到 某个值得 前面或者后面
语法:LINSERT key BEFORE|AFTER pivot value
Redis - 图20
Llen:返回指定key列表的长度
语法:LLEN key
ltrim:对列表进行剪切 sart下标到stop下标
语法:LTRIM key start stop
Redis - 图21
Rpop:弹出尾部元素
语法:RPOP key
Rpoplpush:弹出 source列表的尾部元素 并加入到指定列表的头部,操作是原子性 的。
语法:RPOPLPUSH source destination
Redis - 图22
Rpush:将一个或多个元素value插入到列表尾部
语法:RPUSH key value [value …]
Rpushx:将value插入到列表key中尾部,仅当key存在并且是列表结构。
语法:RPUSHX key value
Tip:另外List还有BLPOP、BRPOP、BRPOPLPUSH三个命令没有说,它们是几个POP的阻塞版本, 即没有数据可以弹出的时候将阻塞客户端直到超时或者发现有可以弹出的元素为止。

Set

Redis 的 set 是 string 类型的无序集合。
  相对于列表,集合也有两个特点:
  一、无序
  二、不可重复

应用场景

利用集合的交并集特性,比如在社交领域,我们可以很方便的求出多个用户的共同 好友,共同感兴趣的领域等

常见命令

Sadd:将一个或多个member元素加入到key中,
语法:SADD key number [member …]
Redis - 图23
Scard:返回key集合的数量
语法: SCARD key
Redis - 图24
Sdiff:返回一个集合的全部成员,如果两个集合 则 两个集合的差集(我有你没有的)
语法: SDIFF key [key …]
Redis - 图25
Sdiffstore:将结果保存到指定集合中 和sdiff类似
语法:SDIFFSTORE destionation key [key …]
Sinert:返回去一个集合的全部成员可以使多个集合的交集
语法:SINTER key [key …]
Slsmembers:判断value是否是key 的成员 1是 0否
语法:SISMEMBER key member
Smembers:返回set集合的全部成员
语法:SMEMBERS key
其他命令 自行百度吧

SortedSet

zset(sorted set 有序集合),和上面的set 数据类型一样,也是 string 类型元素的集合,但是它是有序的。

应用场景

和set数据结构一样,zset也可以用于社交领域的相关业务,并且还可以利用zset 的有序特性,还可以做类似排行榜的业务。

常见命令

ZADD-(1)将一个或多个member元素及其score值加入有序集key中
(2)如果member已经是有序集的成员,那么更新member对应的score并重新插入member保证member在正确的位置上
(3)score可以是整数值或双精度浮点数-ZADD key score member [[score member] [score member] …]
ZCARD- (1)返回有序集key的元素个数-ZCARD key
ZCOUNT -(1) 返回有序集key中,score值>=min且<=max的成员的数量-ZCOUNT key min max
ZRANGE - (1)返回有序集key中指定区间内的成员,成员位置按score从小到大排序
(2)具有相同score值的成员按字典序排列
(3)需要成员按score从大到小排列,使用ZREVRANGE命令
(4)下标参数start和stop都以0为底,也可以用负数,-1表示最后一个成员,-2表示倒数第二个成员
(5)可通过WITHSCORES选项让成员和它的score值一并返回-ZRANGE key start stop [WITHSCORES]
ZRANK- (1)返回有序集key中成员member的排名,有序集成员按score值从小到大排列
(2)排名以0为底,即score最小的成员排名为0
(3)ZREVRANK命令可将成员按score值从大到小排名-ZRANK key number
ZREM-(1)移除有序集key中的一个或多个成员,不存在的成员将被忽略
(2)当key存在但不是有序集时,返回错误 -ZREM key member [member …]
ZREMRANGEBYRANK-(1)移除有序集key中指定排名区间内的所有成员 -ZREMRANGEBYRANK key start stop
ZREMRANGEBYSCORE-(1)移除有序集key中,所有score值>=min且<=max之间的成员 -ZREMRANGEBYSCORE key min max

数据类型应用

Redis key操作

Keys
keys h
llo
Redis - 图26
Redis - 图27
Redis - 图28

Redis持久化

RDB

RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。

触发方式:自动触发、手动触发

自动触发

查看redis.conf文件 SHAPSHOTTING部分
Redis - 图29
 ①、save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave(这个命令下面会介绍,手动触发RDB持久化的命令)
  默认如下配置:
save 900 1:表示900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10:表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存
    当然如果你只是用Redis的缓存功能,不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。可以直接一个空字符串来实现停用:save “”
  ②、stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
  ③、rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
  ④、rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
  ⑤、dbfilename :设置快照的文件名,默认是 dump.rdb
  ⑥、dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。默认是和当前配置文件保存在同一目录。
  也就是说通过在配置文件中配置的 save 方式,当实际操作满足该配置形式时就会进行 RDB 持久化,将当前的内存快照保存在 dir 配置的目录中,文件名由配置的 dbfilename 决定。

手动触发

手动触发Redis进行RDB持久化的命令有两种:
  1、save
  该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
  显然该命令对于内存比较大的实例会造成长时间阻塞,这是致命的缺陷,为了解决此问题,Redis提供了第二种方式。
  2、bgsave
  执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。

恢复数据

将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可,redis就会自动加载文件数据至内存了。Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
  获取 redis 的安装目录可以使用 config get dir 命令
  Redis - 图30
  载入的标识是如下命令:
Redis - 图31

RDB的优势和劣势

优势

(1) RDB是一个非常紧凑的文件 它保存了某个时间点上的数据集,适合备份和灾 难恢复。
(2) 生成RDB文件时,redis主进程会fork一个子进程进行处理工作,主进程不需 要任何的IO操作。
(3) RDB恢复大量数据的时候比Aof要快

劣势

1) RDB方式数据数据没办法做到实时性的持久化,因为bgsave每次运行都要执行fork创建子进程,大致两倍的内存数据,频繁执行 影响性能。
2) RDB使用二进制文件,redis版本演变中有多个格式的RDB,版本不兼容
3) 一定间隔时间做一次持久化备份的话,redis以外down掉 最后一次的数据集容易丢失掉。

RDB自动保存的原理

首先Redis有个服务器状态结构:

1
2
3
4
5
6
7
8
9
struct redisService{
``//1``、记录保存save条件的数组
``struct saveparam *saveparams;
``//2``、修改计数器
``**long** **long** dirty;
``//3``、上一次执行保存的时间
``**time_t** lastsave;

}
1
2
3
4
5
6
struct saveparam{
``//``秒数
``**time_t** seconds;
``//``修改数
``**int** changes;
};
  1. 首先 redisService中数组 *savenparms 包含 秒数 secods 和修改数changes的选项。在redis,conf文件中 有关于save的参数
    Redis - 图32
    那么服务器中数组savenparms的结构是如下的:
    Redis - 图33
    2. Dirty计数器记录上一次执行save或者bgsave命令之后,redis服务器进行了多少次 修改(写入、删除、修改)
    Lastsave记录上一次执行save或bgsave的时间。
    通过这两个命令,当服务器执行上一次修改操作时,dirty计数器就加1,而lastsave记录上次执行save或bgsave持久化的时间,而redis还有个周期性的函数saverCron。默认是每隔100毫秒就执行一次,这个函数会遍历检查saveparams数组中的保存条件,只要有一个满足,就会执行bgsave操作。执行完成后 dirty初始化为0,lastsave同时更新为当前时间。

    AOF

    AOF通过保存redis服务器所执行的写命令来记录数据的状态,比如:
    Redis - 图34

    RDB持久化方式就是讲str1、str2、str3的键值信息保存到RDB文件中
    而AOF则是执行的set、sadd、lpush命令存到AOF文件中。

    AOF配置

    Redis.conf中APPEND ONLY MODE
    Redis - 图35
    ①、appendonly:默认值为no,也就是说redis 默认使用的是rdb方式持久化,如果想要开启 AOF 持久化方式,需要将 appendonly 修改为 yes。
      ②、appendfilename :aof文件名,默认是”appendonly.aof”
      ③、appendfsync:aof持久化策略的配置;
          no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
          always表示每次写入都执行fsync,以保证数据同步到磁盘,效率很低;
          everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。通常选择 everysec ,兼 顾安全性和效率。
      ④、no-appendfsync-on-rewrite:在aof重写或者写入rdb文件的时候,会执行大量IO,此时对 于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间, no-appendfsync-on-rewrite字段设置为默认设置为no。如果对延迟要求很高的应用,这个字段可 以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。 设置为yes表示 rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。 Linux的默认fsync策略是30秒。可能丢失30秒数据。默认值为no。
      ⑤、auto-aof-rewrite-percentage:默认值为100。aof自动重写配置,当目前aof文件大小超过上 一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够 调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的 二倍(设置为100)时,自动启动新的日志重写过程。
      ⑥、auto-aof-rewrite-min-size:64mb。设置允许重写的最小aof文件大小,避免了达到约定百分 比但尺寸仍然很小的情况还要重写。
    ⑦、aof-load-truncated:aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被 载入内存。重启可能发生在redis所在的主机操作系统宕机后,尤其在ext4文件系统没有加上 data=ordered选项,出现这种现象 redis宕机或者异常终止不会造成尾部不完整现象,可以选择让 redis退出,或者导入尽可能多的数据。如果选择的是yes,当截断的aof文件被导入的时候,会自 动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof修复AOF文件才 可以。默认值为 yes。

    AOF开启

    将 redis.conf 的 appendonly 配置改为 yes 即可。
      AOF 保存文件的位置和 RDB 保存文件的位置一样,都是通过 redis.conf 配置文件 的 dir 配置:
      Redis - 图36
      可以通过 config get dir 命令获取保存的路径。

    AOF文件恢复

    重启Redis后就会进行AOF文件的载入
    异常修复命令:redis-check-aof —fix 进行修复

    AOF重写

    由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令 bgrewriteaof 来重新。
      比如对于如下命令:
     Redis - 图37
      如果不进行 AOF 文件重写,那么 AOF 文件将保存四条 SADD 命令,如果使用AOF 重写,那么AOF 文件中将只会保留下面一条命令:
1 sadd animals ``"dog" "tiger" "panda" "lion" "cat"

  也就是说 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。
  AOF 文件重写触发机制:通过 redis.conf 配置文件中的 auto-aof-rewrite-percentage:默认值为100,以及auto-aof-rewrite-min-size:64mb 配置,也就是说默认Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。
  这里再提一下,我们知道 Redis 是单线程工作,如果 重写 AOF 需要比较长的时间,那么在重写 AOF 期间,Redis将长时间无法处理其他的命令,这显然是不能忍受的。Redis为了克服这个问题,解决办法是将 AOF 重写程序放到子程序中进行,这样有两个好处:
  ①、子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其他命令。
  ②、子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
  使用子进程解决了上面的问题,但是新问题也产生了:因为子进程在进行 AOF 重写期间,服务器进程依然在处理其它命令,这新的命令有可能也对数据库进行了修改操作,使得当前数据库状态和重写后的 AOF 文件状态不一致。
  为了解决这个数据状态不一致的问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区是在创建子进程后开始使用,当Redis服务器执行一个写命令之后,就会将这个写命令也发送到 AOF 重写缓冲区。当子进程完成 AOF 重写之后,就会给父进程发送一个信号,父进程接收此信号后,就会调用函数将 AOF 重写缓冲区的内容都写到新的 AOF 文件中。
  这样将 AOF 重写对服务器造成的影响降到了最低。

AOF优势和劣势

优点:
  ①、AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。
  ②、AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。
③、AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。
缺点:
  ①、对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。
  ②、虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 Redis 的负载较高时,RDB 比 AOF 具好更好的性能保证。
  ③、RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。
  那么对于 AOF 和 RDB 两种持久化方式,我们应该如何选择呢?
  如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写。但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。Redis后期官方可能都有将两种持久化方式整合为一种持久化模型。

RDB-AOF混合持久化

在Redis4.0之后,既上一篇文章介绍的RDB和这篇文章介绍的AOF两种持久化方式,又新增了RDB-AOF混合持久化方式。
  这种方式结合了RDB和AOF的优点,既能快速加载又能避免丢失过多的数据。
  具体配置为:aof-use-rdb-preamble
  设置为yes表示开启,设置为no表示禁用。
  当开启混合持久化时,主进程先fork出子进程将现有内存副本全量以RDB方式写入aof文件中,然后将缓冲区中的增量命令以AOF方式写入aof文件中,写入完成后通知主进程更新相关信息,并将新的含有 RDB和AOF两种格式的aof文件替换旧的aof文件。
  简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。
  这种方式优点我们很好理解,缺点就是不能兼容Redis4.0之前版本的备份文件了。
简述:
在redis4.0版本就已经出了混合持久化方式,在配置文件中对应的配置项是aof-use-rdb-preamble ,默认是关闭状态,开启后的aof持久化就会变成由RDB作为全量数据,AOF作为增量数据的AOF-RDB混合持久化方式。

Redis主从复制

1.修改配置文件

 首先将redis.conf 配置文件复制三份,通过修改端口分别模拟三台Redis服务器。
  Redis - 图38
  然后我们分别对这三个redis.conf 文件进行修改。
  ①、修改 daemonize yes
  Redis - 图39
  表示指定Redis以守护进程的方式启动(后台启动)
  ②、配置PID文件路径 pidfile
Redis - 图40
  表示当redis作为守护进程运行的时候,它会把 pid 默认写到 /var/redis/run/redis_6379.pid 文件里面
  ③、配置端口 port
Redis - 图41
  ④、配置log 文件名字
Redis - 图42
  ⑤、配置rdb文件名
  Redis - 图43
  依次将 6380redis.conf 、6381redis.conf 配置一次,则配置完毕。
  接下来我们分别启动这三个服务。
  Redis - 图44
  通过命令查看Redis是否启动:
Redis - 图45
  接下来通过如下命令分别进入到这三个Redis客户端:

1
2
3
redis-cli -p 6379
redis-cli -p 6380
redis-cli -p 6381

  注意:如果修改端口了,启动redis-cli 命令,比如加上 -p 端口,否则连接不上.如果密码也修改了,则还得添加密码 -a 密码指令来连接.

2.设置主从关系

  ①、通过 info replication 命令查看节点角色
Redis - 图46 Redis - 图47
Redis - 图48
  我们发现这三个节点都是扮演的 Master 角色。那么如何将 6380 和 6381 节点变为 Slave 角色呢?
  ②、选择6380端口和6381端口,执行命令:SLAVEOF 127.0.0.1 6379
Redis - 图49 Redis - 图50
  我们再看 6379 节点信息:
 Redis - 图51
  这里通过命令来设置主从关系,一旦服务重启,那么角色关系将不复存在。想要永久的保存这种关系,可以通过配置redis.conf 文件来配置。

1 slaveof 127.0.0.1 6379

3.测试主从关系

  ①、增量复制
  主节点执行 set k1 v1 命令,从节点 get k1 能获取吗?
  Redis - 图52
  Redis - 图53
  Redis - 图54
  由上图可知是可以获取的。
  ②、全量复制
  通过执行 SLAVEOF 127.0.0.1 6379,如果主节点 6379 以前还存在一些 key,那么执行命令之后,从节点会将以前的信息也都复制过来吗?
  答案也是肯定的,这里我就不贴测试结果了。
  ③、主从读写分离
  主节点能够执行写命令,从节点能够执行写命令吗?
Redis - 图55
  这里的原因是在配置文件 6381redis.conf 中对于 slave-read-only 的配置
Redis - 图56
  如果我们将其修改为 no 之后,执行写命令是可以的。
  Redis - 图57
  但是从节点写命令的数据从节点或者主节点都不能获取的。
  ④、主节点宕机
  主节点 Maste 挂掉,两个从节点角色会发生变化吗?
  Redis - 图58
  Redis - 图59
  上图可知主节点 Master 挂掉之后,从节点角色还是不会改变的。
  ⑤、主节点宕机后恢复
  主节点Master挂掉之后,马上启动主机Maste,主节点扮演的角色还是 Master 吗?
 Redis - 图60
  也就是说主节点挂掉之后重启,又恢复了主节点的角色。

4.哨兵模式

通过前面的配置,主节点Master 只有一个,一旦主节点挂掉之后,从节点没法担起主节点的任务,那么整个系统也无法运行。如果主节点挂掉之后,从节点能够自动变成主节点,那么问题就解决了,于是哨兵模式诞生了。
  哨兵模式就是不时地监控redis是否按照预期良好地运行(至少是保证主节点是存在的),若一台主机出现问题时,哨兵会自动将该主机下的某一个从机设置为新的主机,并让其他从机和新主机建立主从关系。
  Redis - 图61
  哨兵模式搭建步骤:
  ①、在配置文件目录下新建 sentinel.conf 文件,名字绝不能错,然后配置相应内容
  Redis - 图62 

1 sentinel monitor ``被监控机器的名字(自己起名字) ip地址 端口号 得票数

  Redis - 图63
  分别配置被监控的名字,ip地址,端口号,以及得票数。上面的得票数为1表示表示主机挂掉后salve投票看让谁接替成为主机,得票数大于1便成为主机
  ②、启动哨兵

1 redis-sentinel /etc/redis/sentinel.conf

  启动界面:
 Redis - 图64
  接下来,我们干掉主机 6379,然后看从节点有啥变化。
  Redis - 图65
  干掉主节点之后,我们查看后台打印日志,发现 6381投票变为主节点了。
Redis - 图66
  这时候我们查看从节点 6381的节点信息:
Redis - 图67
  6381 节点自动变为主节点了。
  PS:哨兵模式也存在单点故障问题,如果哨兵机器挂了,那么就无法进行监控了,解决办法是哨兵也建立集群,Redis哨兵模式是支持集群的。

5.主从复制的原理

Redis的复制功能分为同步(sync)和命令传播(command propagate)两个操作。
  ①、旧版同步
  当从节点发出 SLAVEOF 命令,要求从服务器复制主服务器时,从服务器通过向主服务器发送 SYNC 命令来完成。该命令执行步骤:
  1、从服务器向主服务器发送 SYNC 命令
  2、收到 SYNC 命令的主服务器执行 BGSAVE 命令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从开始执行的所有写命令
  3、当主服务器的 BGSAVE 命令执行完毕时,主服务器会将 BGSAVE 命令生成的 RDB 文件发送给从服务器,从服务器接收此 RDB 文件,并将服务器状态更新为RDB文件记录的状态。
  4、主服务器将缓冲区的所有写命令也发送给从服务器,从服务器执行相应命令。
  ②、命令传播
  当同步操作完成之后,主服务器会进行相应的修改命令,这时候从服务器和主服务器状态就会不一致。
  为了让主服务器和从服务器保持状态一致,主服务器需要对从服务器执行命令传播操作,主服务器会将自己的写命令发送给从服务器执行。从服务器执行相应的命令之后,主从服务器状态继续保持一致。
  总结:通过同步操作以及命令传播功能,能够很好的保证了主从一致的特性。
  但是我们考虑一个问题,如果从服务器在同步主服务器期间,突然断开了连接,而这时候主服务器进行了一些写操作,这时候从服务器恢复连接,如果我们在进行同步,那么就必须将主服务器从新生成一个RDB文件,然后给从服务器加载,这样虽然能够保证一致性,但是其实断开连接之前主从服务器状态是保持一致的,不一致的是从服务器断开连接,而主服务器执行了一些写命令,那么从服务器恢复连接后能不能只要断开连接的哪些写命令,而不是整个RDB快照呢?
  同步操作其实是一个非常耗时的操作,主服务器需要先通过 BGSAVE 命令来生成一个 RDB 文件,然后需要将该文件发送给从服务器,从服务器接收该文件之后,接着加载该文件,并且加载期间,从服务器是无法处理其他命令的。
  为了解决这个问题,Redis从2.8版本之后,使用了新的同步命令 PSYNC 来代替 SYNC 命令。该命令的部分重同步功能用于处理断线后重复制的效率问题。也就是说当从服务器在断线后重新连接主服务器时,主服务器只将断开连接后执行的写命令发送给从服务器,从服务器只需要接收并执行这些写命令即可保持主从一致。

缺点:

主从复制虽然解决了主节点的单点故障问题,但是由于所有的写操作都是在 Master 节点上操作,然后同步到 Slave 节点,那么同步就会有一定的延时,当系统很繁忙的时候,延时问题就会更加严重,而且会随着从节点slave的增多而愈加严重。

Redis淘汰策略

设置key过期时间方式

Redis提供了四个命令来设置过期时间(生存时间)。
  ①、EXPIRE :表示将键 key 的生存时间设置为 ttl 秒。
  ②、PEXPIRE :表示将键 key 的生存时间设置为 ttl 毫秒。
  ③、EXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的秒数时间戳。
  ④、PEXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的毫秒数时间戳。
  PS:在Redis内部实现中,前面三个设置过期时间的命令最后都会转换成最后一个PEXPIREAT 命令来完成。
  另外补充两个知识点:
  一、移除键的过期时间
  PERSIST :表示将key的过期时间移除。
  二、返回键的剩余生存时间
  TTL :以秒的单位返回键 key 的剩余生存时间。
PTTL :以毫秒的单位返回键 key 的剩余生存时间。

过期时间如何判定的?

在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中。当我们查询一个键时,Redis便首先检查该键是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。

淘汰策略

通常删除key时,有三种策略

定时删除

在设置带时间的key时创建定时器,时间到了删除过期的key。这样可以保证内存的可用性。但是如果过期的key很多的时候,删除key会影响响应的时间。

惰性删除

当用到这个key 的时候 再去检查是否过期,不影响响应时间了 但是极端情况下 长期不用并且过期的key会一直存在内存中。很有可能造成内存泄漏。

定期删除

每个一段时间进行一次排查。删除过期的key
好处是能释放内存
但是有一段时间 是有可能获取过期的key的 不安全。而且执行的时间不确定。

Redis实现

通过结合惰性删除和定期删除和内存淘汰策方式每隔一段时间 进行随机扫描检测一定数量的key过期时间。然后获取key 的时候,再次判断key的过期。
Redis.conf文件中
Redis - 图68
默认每秒运行十次。
可能会有过期了 但是一直用不到 一直扫不到的key 可以用内存淘汰方式 通过redis.conf的maxmemory参数 判断当前内存是否大于这个参数,大于参数进行内存淘汰方式
内存淘汰方式有如下淘汰策略
1)volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) 。
  2)allkeys-lru 利用LRU算法移除任何key (和上一个相比,删除的key包括设置过期时间和不设置过期时间的)。通常使用该方式。(移除最少使用的key)。
  3)volatile-random 移除设置过过期时间的随机key 。
  4)allkeys-random 无差别的随机移除。
  5)volatile-ttl 移除即将过期的key(minor TTL)
  6)noeviction 不移除任何key,只是返回一个写错误 ,默认选项,一般不会选用。
Redis - 图69

Redis容易出现的问题

缓存穿透

指缓存和数据库中都没有的数据,但是一直在请求 每次都回到数据库,从而压垮数据库
Redis - 图70

解决办法

①、业务层校验
  用户发过来的请求,根据请求参数进行校验,对于明显错误的参数,直接拦截返回。
  比如,请求参数为主键自增id,那么对于请求小于0的id参数,明显不符合,可以直接返回错误请求。
  ②、不存在数据设置短过期时间
  对于某个查询为空的数据,可以将这个空结果进行Redis缓存,但是设置很短的过期时间,比如30s,可以根据实际业务设定。注意一定不要影响正常业务。
  ③、布隆过滤器
  关于布隆过滤器,后面会详细介绍。布隆过滤器是一种数据结构,利用极小的内存,可以判断大量的数据“一定不存在或者可能存在”。
  对于缓存击穿,我们可以将查询的数据条件都哈希到一个足够大的布隆过滤器中,用户发送的请求会先被布隆过滤器拦截,一定不存在的数据就直接拦截返回了,从而避免下一步对数据库的压力。

缓存击穿

Redis中一个热点key在失效的时间,大量请求过来,从而到达数据库 压垮数据库。
Redis - 图71

解决办法

  ①、设置热点数据永不过期
  对于某个需要频繁获取的信息,缓存在Redis中,并设置其永不过期。当然这种方式比较粗暴,对于某些业务场景是不适合的。
  ②、定时更新
  比如这个热点数据的过期时间是1h,那么每到59minutes时,通过定时任务去更新这个热点key,并重新设置其过期时间。
  ③、互斥锁
  这是解决缓存穿透比较常用的方法。
  互斥锁简单来说就是在Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则睡眠一段时间(比如100ms)后重试。

缓存雪崩

缓存雪崩:Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库。
  Redis - 图72
  对于一个业务系统,如果Redis宕机或大面积的key同时过期,会导致大量请求同时打到数据库,这是灾难性的问题。

解决办法

①、设置有效期均匀分布
  避免缓存设置相近的有效期,我们可以在设置有效期时增加随机值;
  或者统一规划有效期,使得过期时间均匀分布。
  ②、数据预热
  对于即将来临的大量请求,我们可以提前走一遍系统,将数据提前缓存在Redis中,并设置不同的过期时间。
  ③、保证Redis服务高可用
  前面我们介绍过Redis的哨兵模式和集群模式,为防止Redis集群单节点故障,可以通过这两种模式实现高可用。

布隆过滤器

自行百度吧

自己遇见过的Redis面试题(真实)

Redis是单线程的吗?为什么

redis中处理请求操作数据的是单线程执行的,因为redis在执行数据操作是内存中的,不进行IO读写硬盘操作,内存中的数据操作非常快,CPU并不是性能的瓶颈,主要是机器的内存大小和网络带宽。单线程不存在多线程的并发问题。

你说一下redis的特点

首先支持持久化,存储的数据类型也很多 string、hash、list、set、zset。Bgsave的方式后台备份,还有因为内存操作 所以性能很好,功能很多,像首部弹出元素,尾部弹出元素,切割。