Luckylau's Blog

SpringBoot之Redis

最近项目使用了Redis,抽个时间系统的整理一下Redis的使用。

目前提供一套学习使用教程,见Git地址

什么是Redis?

​ Redis是Nosql数据库中使用较为广泛的非关系型内存数据库,Redis内部是一个key-value存储系统。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型,类似于Java中的map)。Redis基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

为什么要使用Redis?

​ 数据结构(Data Structure)需求越来越多, 但memcache中没有, 影响开发效率;性能需求, 随着读操作的量的上升需要解决,经历的过程有:数据库读写分离(M/S)–>数据库使用多个Slave–>增加Cache (memcache)–>转到Redis。没有使用Redis时候需要水平拆分,对表的拆分,将有的用户放在这个表,有的用户放在另外一个表;可靠性需求 ,Cache的”雪崩”问题让人纠结 ,Cache面临着快速恢复的挑战;开发成本需求 。Cache和DB的一致性维护成本越来越高(先清理DB,再清理缓存, 不行啊, 太慢了!) 开发需要跟上不断涌入的产品需求 硬件成本最贵的就是数据库层面的机器,基本上比前端的机器要贵几倍,主要是IO密集型,很耗硬件;维护性复杂 ,一致性维护成本越来越高;

​ 所以大数据时代淘宝、微信、以及微博会广泛使用了redis数据库,将一些固定不变的数据例如学校,区域等固定的信息保存在关系型数据库中。然后对于经常变化的数据例如淘宝每个节日都会有比较热门的搜索显示在搜索框,当节日过去关键字自动删除,为了便于管理,可以将这些数据保存在redis数据库中,并设置过期时间,到达时间就自动删除。 为了缓解数据库压力,微博首先将发送的微博保存到redis数据库,自己可以立即查看到,然后将内存中的数据同步到关系型数据库。

安装Redis

以ubantu16

1
2
3
4
5
6
7
8
//下载redis
wget http://download.redis.io/releases/redis-3.0.7.tar.gz
//解压redis
tar -xvf redis-3.0.7.tar.gz
//安装redis
cd redis-3.0.7
//“有可能需要安装gcc插件:yum install -y gcc ”
make

配置Redis的依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>

参数配置属性的说明

在application.properties中加入Redis服务端的相关配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0

Redis配置文件

redis官方提供的redis.conf文件,足有700+行,其中100多行为有效配置行,另外的600多行为注释说明。

1
2
3
4
5
6
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes

​ redis配置中对单位的大小写不敏感,1GB、1Gb和1gB都是相同的。由此也说明,redis只支持bytes,不支持bit单位。

​ redis配置文件被分成了几大块区域,它们分别是:

通用(general)

1
2
3
4
5
6
7
8
9
10
11
12
daemonize no //当以daemon形式运行时,redis会生成一个pid文件,默认会生成在/var/run/redis.pid
pidfile /path/to/redis.pid //配置pid文件
port 6379 //端口,默认是6379 ,6379在是手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
tcp-backlog 511 //TCP接收队列长度,受/proc/sys/net/core/somaxconn和tcp_max_syn_backlog这两个内核参数的影响
bind 127.0.0.1 //绑定的主机地址
timeout 0 //当一个redis-client一直没有请求发向server端,那么server端有权主动关闭这个连接,可以通过timeout来设置“空闲超时时限”,0表示永不关闭。
tcp-keepalive 60 //TCP连接保活策略,可以通过tcp-keepalive配置项来进行设置,单位为秒,假如设置为60秒,则server端会每60秒向连接空闲的客户端发起一次ACK请求,以检查客户端是否已经挂掉,对于无响应的客户端则会关闭其连接。所以关闭一个连接最长需要120秒的时间。如果设置为0,则不会进行保活检测。
loglevel notice //设置日志等级,共分四级,即debug、verbose、notice、warning。
logfile "" //日志名,假如你在daemon情况下将日志设置为输出到标准输出,则日志会被写到/dev/null中。
syslog-ident redis //如果希望日志打印到syslog中,也很容易,通过syslog-enabled来控制。另外,syslog-ident还可以让你指定syslog里的日志标志
syslog-facility local0 //还支持指定syslog设备,值可以是USER或LOCAL0-LOCAL7
databases 16 //这16个数据库的编号将是0到15。默认的数据库是编号为0的数据库,可以使用SELECT <dbid>命令在连接上指定数据库id。

快照(snapshotting)

1
2
3
4
5
6
7
8
9
10
11
save <seconds> <changes>
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
stop-writes-on-bgsave-error yes //默认如果开启RDB快照(至少一条save指令)并且最新的后台保存失败,Redis将会停止接受写操作,这将使用户知道数据没有正确的持久化到硬盘,否则可能没人注意到并且造成一些灾难。
rdbcompression yes //对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
rdbchecksum yes //在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果你希望获取到最大的性能提升,可以关闭此功能。
dbfilename dump.rdb //设置数据库名字。
dir ./ //指定本地数据库存放目录。

复制(replication)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
slaveof <masterip> <masterport> //设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步。
masterauth <master-password> //当master服务设置了密码保护时,slav服务连接master的密码。
requirepass foobared //设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
slave-read-only yes //你可以配置salve实例是否接受写操作。可写的slave实例可能对存储临时数据比较有用(因为写入salve的数据在同master同步之后将很容易被删除,自从redis2.6版本之后,默认从redis为只读。
rename-command CONFIG //只读的从redis并不适合直接暴露给不可信的客户端。为了尽量降低风险,可以使用rename-command指令来将一些可能有破坏力的命令重命名,避免外部直接调用。
repl-ping-slave-period 10 //从redis会周期性的向主redis发出PING包。你可以通过repl_ping_slave_period指令来控制其周期。默认是10秒。
repl-timeout 60//在主从同步时,可能在这些情况下会有超时发生:以从redis的角度来看,当有大规模IO传输时,
//以从redis的角度来看,当数据传输或PING时,主redis超时
//以主redis的角度来看,在回复从redis的PING时,从redis超时
repl-disable-tcp-nodelay no //我们可以控制在主从同步时是否禁用TCP_NODELAY。如果开启TCP_NODELAY,那么主redis会使用更少的TCP包和更少的带宽来向从redis传输数据。但是这可能会增加一些同步的延迟,大概会达到40毫秒左右。如果你关闭了TCP_NODELAY,那么数据同步的延迟时间会降低,但是会消耗更多的带宽。
repl-backlog-size 1mb //队列长度(backlog)是主redis中的一个缓冲区,在与从redis断开连接期间,主redis会用这个缓冲区来缓存应该发给从redis的数据。
repl-backlog-ttl 3600 //如果主redis等了一段时间之后,还是无法连接到从redis,那么缓冲队列中的数据将被清理掉。如果设置为0,则表示永远不清理。默认是1个小时。
slave-priority 100 //我们可以给众多的从redis设置优先级,在主redis持续工作不正常的情况,优先级高的从redis将会升级为主redis。而编号越小,优先级越高。当优先级被设置为0时,这个从redis将永远也不会被选中。
min-slaves-to-write 3
min-slaves-max-lag 10//有超过M个从redis的连接延时大于N秒,那么主redis就停止接受外来的写请求。假如有大于等于3个从redis的连接延迟大于10秒,那么主redis就不再接受外部的写请求。上述两个配置中有一个被置为0,则这个特性将被关闭。

安全(security)

1
2
requirepass luckylau //你的redis-server处于一个不太可信的网络环境中时,相信你会用上这个功能。由于redis性能非常高,所以每秒钟可以完成多达15万次的密码尝试,所以你最好设置一个足够复杂的密码,否则很容易被黑客破解。
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c89 //redis允许我们对redis指令进行更名,当为""空串时候为禁用掉该命令。

限制(limits)

1
2
3
4
maxclients 10000 //如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。
maxmemory <bytes//设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。如果redis无法根据移除规则来移除内存中的数据,或者我们设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。但是对于无内存申请的指令,仍然会正常响应,比如GET等。
maxmemory-policy volatile-lru//volatile-lru使用LRU算法移除过期集合中的key,allkeys-lru:使用LRU算法移除key,volatile-random:在过期集合中移除随机的key,allkeys-random:移除随机的key,volatile-ttl:移除那些TTL值最小的key,即那些最近才过期的key,noeviction:不进行移除。针对写操作,只是返回错误信息。
maxmemory-samples 3//LRU算法和最小TTL算法都并非是精确的算法,而是估算值。所以你可以设置样本的大小。假如redis默认会检查三个key并选择其中LRU的那个,那么你可以改变这个key样本的数量。

追加模式(append only mode)

​ 追加文件(Append Only File)是一种更好的保持数据一致性的方式。即使当服务器断电时,也仅会有1秒钟的写请求丢失,当redis进程出现问题且操作系统运行正常时,甚至只会丢失一条写请求。我们建议大家,AOF机制和RDB机制可以同时使用,不会有任何冲突。

1
2
3
appendonly no //指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendfilename "appendonly.aof" //设置aof文件的名称。
appendfsync everysec //no:不调用fsync()。而是让操作系统自行决定sync的时间。这种模式下,redis的性能会最快。always:在每次写请求后都调用fsync()。这种模式下,redis会相对较慢,但数据最安全。everysec:每秒钟调用一次fsync()。这是性能和安全的折衷。

​ 当fsync方式设置为always或everysec时,如果后台持久化进程需要执行一个很大的磁盘IO操作,那么redis可能会在fsync()调用时卡住。目前尚未修复这个问题,这是因为即使我们在另一个新的线程中去执行fsync(),也会阻塞住同步写调用。
​ 为了缓解这个问题,我们可以使用下面的配置项,这样的话,当BGSAVE或BGWRITEAOF运行时,fsync()在主进程中的调用会被阻止。这意味着当另一路进程正在对AOF文件进行重构时,redis的持久化功能就失效了,就好像我们设置了“appendsync none”一样。如果你的redis有时延问题,那么请将下面的选项设置为yes。否则请保持no,因为这是保证数据完整性的最安全的选择。

1
no-appendfsync-on-rewrite no

​ 我们允许redis自动重写aof。当aof增长到一定规模时,redis会隐式调用BGREWRITEAOF来重写log文件,以缩减文件体积。

1
2
3
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes //AOF文件可能在尾部是不完整的,那redis重启时load进内存的时候就有问题了,如果yes,会自动发布一个log给客户端然后load(默认)。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。注意,如果在读取的过程中,发现这个aof是损坏的,服务器也是会退出的,这个选项仅仅用于当服务器尝试读取更多的数据但又找不到相应的数据时。

LUA脚本(lua scripting)

1
lua-time-limit 5000 //lua脚本的最大运行时间是需要被严格限制的,要注意单位是毫秒

慢日志(slow log)

​ Redis慢日志是指一个系统进行日志查询超过了指定的时长。这个时长不包括IO操作,比如与客户端的交互、发送响应内容等,而仅包括实际执行查询命令的时间。

1
2
slowlog-log-slower-than 10000//单位是微秒,即1000000表示一秒。负数则会禁用慢日志功能,而0则表示强制记录每一个命令。
slowlog-max-len 128//慢日志最大长度,可以随便填写数值,没有上限,但要注意它会消耗内存。你可以使用SLOWLOG RESET来重设这个值。

事件通知(event notification)

1
2
3
notify-keyspace-events ""//Redis能通知 Pub/Sub 客户端关于键空间发生的事件,默认关闭
latency-monitor-threshold 0//redis延时监控系统在运行时会采样一些操作,以便收集可能导致延时的数据根源。
通过 LATENCY命令 可以打印一些图样和获取一些报告,方便监控。这个系统仅仅记录那个执行时间大于或等于预定时间(毫秒)的操作, 这个预定时间是通过latency-monitor-threshold配置来指定的,当设置为0时,这个监控系统处于停止状态

高级配置

客户端输出缓冲的控制项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000 //HyperLogLog稀疏结构表示字节的限制。
activerehashing yes//启用哈希刷新,每100个CPU毫秒会拿出1个毫秒来刷新Redis的主哈希表
client-output-buffer-limit normal 0 0 0 //客户端的输出缓冲区的限制,可用于强制断开那些因为某种原因从服务器读取数据的速度不够快的客户端
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10//提高该值将在Redis空闲时使用更多的CPU时,但同时当有多个key,同时到期会使Redis的反应更灵敏,以及超时可以更精确地处理。
aof-rewrite-incremental-fsync yes //当一个子进程重写AOF文件时,如果启用下面的选项,则文件每生成32M数据会被同步。

Redis的5中数据结构

字符串(strings)

Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字;

常用命令: set,get,decr,incr,mget 等;

应用场景:String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类,即可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。除了提供与 Memcached 一样的get、set、incr、decr 等操作外,Redis还提供了下面一些操作:获取字符串长度;往字符串append内容;设置和获取字符串的某一段内容;设置及获取字符串的某一位(bit);
批量设置一系列字符串的内容;

实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

字符串列表(lists)

常用命令:lpush,rpush,lpop,rpop,lrange等。

应用场景:
Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
Lists 就是链表,我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api,你可以直接查询,删除Lists中某一段的元素。

实现方式:
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

字符串集合(sets)

​ Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

常用命令:sadd,spop,smembers,sunion 等。

应用场景:
​ Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

实现方式:
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

有序字符串集合(sorted sets)

常用命令:zadd,zrange,zrem,zcard等

使用场景:
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

实现方式:
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

哈希(hashes)

常用命令:hget,hset,hgetall 等。

应用场景:在Memcached中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。

这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部 Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。

实现方式:
上面已经说到Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

Redis其他应用

发布(Publish)与订阅(Subscribe)

​ 在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个 key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

Redis 管道技术

​ Redis服务是一种C/S模型,提供请求-响应式协议的TCP服务,所以当客户端请求发出,服务端处理并返回结果到客户端,一般是以阻塞形式等待服务端的响应,但这在批量处理连接时延迟问题比较严重,所以Redis为了提升或弥补这个问题,引入了管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。

HyperLogLog

​ Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog 不能像集合那样,返回输入的各个元素。可用于在访问量巨大时候,计算记录网站每天访问的独立IP数量这样的一个功能。

显示最新的项目列表

​ 在Web应用中,“列出最新的回复”之类的查询非常普遍,这通常会带来可扩展性问题。这令人沮丧,因为项目本来就是按这个顺序被创建的,但要输出这个顺序却不得不进行排序操作。
类似的问题就可以用Redis来解决。比如说,我们的一个Web应用想要列出用户贴出的最新20条评论。在最新的评论边上我们有一个“显示全部”的链接,点击后就可以获得更多的评论。

​ 我们假设数据库中的每条评论都有一个唯一的递增的ID字段。使用Redis的模板,每次新评论发表时,我们会将它的ID添加到一个Redis列表:PUSH latest.comments我们将列表裁剪为指定长度,因此Redis只需要保存最新的5000条评论:LTRIM latest.comments 0 5000;每次我们需要获取最新评论的项目范围时,如果少于5000个,就会一直询问Redis,只有在start/count参数超出了这个范围的时候,才需要去访问数据库。

排行榜相关

​ 典型的比如那些在线游戏的排行榜,比如一个Facebook的游戏,根据得分你通常想要:– 列出前100名高分选手,– 列出某用户当前的全球排名这些操作对于Redis来说小菜一碟,即使你有几百万个用户,每分钟都会有几百万个新的得分。模式是这样的,每次获得新得分时,我们用这样的代码:ZADD leaderboard score userID。得到前100名高分用户很简单:ZREVRANGE leaderboard 0 99,用户的全球排名也相似,只需要:ZRANK leaderboard 。

Redis的持久化和备份

​ redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
​ 其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。
​ 如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。

RDB

​ RDB方式,是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。
​ 对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
​ 虽然RDB有不少优点,但它的缺点也是不容忽视的。如果你对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失。

AOF

​ AOF,英文是Append Only File,即只允许追加不允许改写的文件。AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。我们通过配置redis.conf中的appendonly yes就可以打开AOF功能。如果有写操作(如SET等),redis就会被追加到AOF文件的末尾。默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等情况导致日志写入不完整,也没有关系,redis提供了redis-check-aof工具,可以用来进行日志修复。
​ 因为采用了追加方式,如果不做任何处理的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。举个例子或许更形象,假如我们调用了100次INCR指令,在AOF文件中就要存储100条指令,但这明显是很低效的,完全可以把这100条指令合并成一条SET指令,这就是重写机制的原理。在进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。虽然优点多多,但AOF方式也同样存在缺陷,比如在同样数据规模的情况下,AOF文件要比RDB文件的体积大。而且,AOF方式的恢复速度也要慢于RDB方式。
​ 如果你直接执行BGREWRITEAOF命令,那么redis会生成一个全新的AOF文件,其中便包括了可以恢复现有数据的最少的命令集。
​ 如果运气比较差,AOF文件出现了被写坏的情况,也不必过分担忧,redis并不会贸然加载这个有问题的AOF文件,而是报错退出。这时可以通过以下步骤来修复出错的文件:
1.备份被写坏的AOF文件
2.运行redis-check-aof –fix进行修复
3.用diff -u来看下两个文件的差异,确认问题点
4.重启redis,加载修复后的AOF文件

​ AOF重写的内部运行原理是在重写即将开始之际,redis会创建(fork)一个“重写子进程”,这个子进程会首先读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。与此同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中了。

Redis的事务

​ 事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。Redis指令,即MULTI、EXEC、DISCARD、WATCH。这四个指令构成了Redis事务处理的基础。

MULTI用来组装一个事务;
EXEC用来执行一个事务;
DISCARD用来取消一个事务;
WATCH用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR 1
QUEUED
127.0.0.1:6379> INCR 2
QUEUED
127.0.0.1:6379> incr 3
QUEUED
127.0.0.1:6379> PING
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) PONG

​ 在上面的例子中,我们看到了QUEUED的字样,这表示我们在用MULTI组装事务时,每一个命令都会进入到内存队列中缓存起来,如果出现QUEUED则表示我们这个命令成功插入了缓存队列,在将来执行EXEC时,这些被QUEUED的命令都会被组装成一个事务来执行。
​ 对于事务的执行来说,如果redis开启了AOF持久化的话,那么一旦事务被成功执行,事务中的命令就会通过write命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了AOF持久化,这时AOF文件就会出现不完整的情况,这时,我们可以使用redis-check-aof工具来修复这一问题,这个工具会将AOF文件中不完整的信息移除,确保AOF文件完整可用。
有关事务,大家经常会遇到的是两类错误:调用EXEC之前的错误,调用EXEC之后的错误。
​ “调用EXEC之前的错误”,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis都会进行记录,在客户端调用EXEC时,redis会拒绝执行这一事务。(Redis 2.6.5之前的版本会忽略有语法错误的命令,然后执行事务中其他语法正确的命令。就此例而言,SET key value会被执行,EXEC命令会返回一个结果:1) OK。)

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379>MULTI
OK
127.0.0.1:6379>SET key value
QUEUED
127.0.0.1:6379>SET key
(error)ERR wrong number of arguments for 'set' command
127.0.0.1:6379> errorCOMMAND key
(error) ERR unknown command 'errorCOMMAND'
127.0.0.1:6379>EXEC
(error) EXECABORT Transaction discarded because of previous errors.

​ 而对于“调用EXEC之后的错误”,redis则采取了完全不同的策略,即redis不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是redis自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行。

最后一个就是WATCH,它本身的作用是“监视key是否被改动过”,而且支持同时监视多个key,只要还没真正触发事务,WATCH都会尽职尽责的监视,一旦发现某个key被修改了,在执行EXEC时就会返回nil,表示事务无法触发。

Redis的集群

Redis的主从配置

​ 像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构。主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的SORT就可以由从服务器来承担。redis的主从同步是异步进行的,这意味着主从同步不会影响主逻辑,也不会降低redis的处理性能。主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。
​ 在主从架构中,从服务器通常被设置为只读模式,这样可以避免从服务器的数据被误修改。但是从服务器仍然可以接受CONFIG等指令,所以还是不应该将从服务器直接暴露到不安全的网络环境中。如果必须如此,那可以考虑给重要指令进行重命名,来避免命令被外人误执行。

​ 其过程如下:从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中。
​ 在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。
​ 另外,要说的一点是,即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个下游。在redis2.8版本之前,如果从服务器与主服务器因某些原因断开连接的话,都会进行一次主从之间的全量的数据同步;而在2.8版本之后,redis支持了效率更高的增量同步策略,这大大降低了连接断开的恢复成本。
​ 主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。从服务器在与主服务器出现网络瞬断之后,从服务器会尝试再次与主服务器连接,一旦连接成功,从服务器就会把“希望同步的主服务器ID”和“希望请求的数据的偏移位置(replication offset)”发送出去。主服务器接收到这样的同步请求后,首先会验证主服务器ID是否和自己的ID匹配,其次会检查“请求的偏移位置”是否存在于自己的缓冲区中,如果两者都满足的话,主服务器就会向从服务器发送增量内容。
​ 增量同步功能,需要服务器端支持全新的PSYNC指令。这个指令,只有在redis-2.8之后才具有。

Redis的Sentinel配置

​ 在redis主从复制架构中,如果主服务器宕机,那么这个复制集群能否正常提供服务?答案当然是不能。Sentinel就是来解决redis复制集群主节点单点问题的,它解决的问题是:监控,即sentinel会不断的检查你的主服务器和从服务器是否运行正常;当被监控的某个redis服务器出现问题时,sentinel可以通过API向管理员或者其他应用程序发送通知;自动故障转移:当一个主服务器不能正常工作时,sentinel会开始一次自动故障转移操作,他会将失效主服务器下的一个从服务器升级为新的主服务器,并让失效主机下的其他从服务器改为复制到新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

SpringBoot操作Redis

Jedis

RedisTemplate

redis.repositories

关于以上3中操作参考文章前面的Git哦。

参考:

官方RedisTemplate文档

https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/RedisTemplate.html

http://blog.csdn.net/yujin2010good/article/details/54729939

http://blog.csdn.net/heiyeshuwu/article/details/41248379

http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html

http://coolshell.cn/articles/17416.html

http://blog.sina.com.cn/s/blog_6940cab30102uy02.html

http://www.cnblogs.com/lei2007/p/3837288.html

http://www.jb51.net/article/56448.htm

http://blog.csdn.net/zfl092005/article/details/17523945

https://www.cnblogs.com/LiZhiW/p/4851631.html

http://www.cnblogs.com/jager/p/6349860.html

https://docs.spring.io/spring-data/redis/docs/1.7.6.RELEASE/reference/html/#redis.repositories

Luckylau wechat
如果对您有价值,看官可以打赏的!