Redis、Spring Boot + Redis

Redis的数据类型

Redis五种数据类型:string、hash、list、set、zset

公用命令

del key
dump key:序列化给定key,返回被序列化的值
exists key:检查key是否存在
expire key second:为key设定过期时间,以秒计算,可以不写second,默认为秒
ttl key:返回key剩余时间,-1为永久,-2为失效
persist key:移除key的过期时间,key将持久保存
keys pattern:查询所有符号给定模式的key eg:keys *
randomkey:随机返回一个key
rename key newkey:修改key的名称
move key db:移动key至指定数据库中 eg:move a 1
type key:返回key所储存的值的类型

expirekey second的使用场景:
1、限时的优惠活动
2、网站数据缓存
3、手机验证码
4、限制网站访客频率

key的命名建议

key不要太长,尽量不要超过1024字节。不仅消耗内存,也会降低查找的效率
key不要太短,太短可读性会降低
在一个项目中,key最好使用统一的命名模式,如user:123:password
key区分大小写

String

string类型是二进制安全的,redis的string可以包含任何数据,如图像、序列化对象。一个键最多能存储512MB。二进制安全是指,在传输数据的时候,能保证二进制数据的信息安全,也就是不会被篡改、破译;如果被攻击,能够及时检测出来

set key_name value:命令不区分大小写,但是key_name区分大小写
setnx key value:当key不存在时设置key的值。(SET if Not eXists),分布式锁的问题
setex:创建一个key,并且设置他的过期时间
get key_name
getrange key start end:获取key中字符串的子字符串,从start开始,end结束
setrange key offset value:设置从offset往后的值
mget key1 [key2 …]:获取多个key
getset key_name value:返回key的旧值,并设定key的值。当key不存在,返回nil
strlen key:返回key所存储的字符串的长度
incr key_name :INCR命令key中存储的值+1,如果不存在key,则key中的值话先被初始化为0再加1
INCRBY KEY_NAME 增量
DECR KEY_NAME:key中的值自减一
DECRBY KEY_NAME
append key_name value:字符串拼接,追加至末尾,如果不存在,为其赋值

String应用场景:
	1、String通常用于保存单个字符串或JSON字符串数据
	2、因为String是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储
	3、计数器:常规Key-Value缓存应用,如微博数、粉丝数。INCR本身就具有原子性特性,所以不会有线程安全问题

hash

Redis hash是一个string类型的field和value的映射表,hash特别适用于存储对象。每个hash可以存储232-1(40亿左右)键值对。可以看成KEY和VALUE的MAP容器。相比于JSON,hash占用很少的内存空间。

hset key_name field value:为指定的key设定field和value
hmset key field value[field1,value1]
hsetnx:当不存在才创建该field
hget key field
hmget key field[field1]
hgetall key:返回hash表中所有字段和值
hkeys key:获取hash表所有字段
hvals key:获取hash表所有值
hlen key:获取hash表中的字段数量
hdel key field [field1]:删除一个或多个hash表的字段
hexists:在key里面是否存在指定的field
hincrby key field increment:增加某个field的值

Hash的应用场景,
	通常用来存储一个用户信息的对象数据。
	相比于存储对象的string类型的json串,json串修改单个属性需要将整个值取出来。而hash不需要。
	相比于多个key-value存储对象,hash节省了很多内存空间
	如果hash的属性值被删除完,那么hash的key也会被redis删除

list

类似于Java中的LinkedList。

lpush key value1 [value2]:从左侧插入,右边的先出,相当于一个栈
eg:lpush list 1 2 3 lrange list 0 -1 输出:3 2 1

rpush key value1 [value2]: 从右侧插入,左边的先出

eg:rpush list 1 2 3 lrange list 0 -1 输出:1 2 3

lpushx key value:从左侧插入值,如果list不存在,则不操作

rpushx key value:从右侧插入值,如果list不存在,则不操作
llen key:获取列表长度
lindex key index:获取指定索引的元素,从零开始
lrange key start stop:获取列表指定范围的元素
lpop key :从左侧移除第一个元素
prop key:移除列表最后一个元素
irem:删除指定个数的同一元素
eg:irem list 2 3 删掉了集合中的两个三

blpop key [key1] timeout:移除并获取列表第一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止

brpop key [key1] timeout:移除并获取列表最后一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止
ltrim key start stop :对列表进行修改,让列表只保留指定区间的元素,不在指定区间的元素就会被删除
eg:list1中元素1 2 3 4 5 ltrim list1 2 3 list1剩余元素:3 4

lset key index value :指定索引的值
linsert key before|after world value:在列表元素前或则后插入元素


■ 应用场景
		对数据大的集合数据删减
		
		   列表显示、关注列表、粉丝列表、留言评价…分页、热点新闻等
		
		任务队列
		   list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样通过order by来排序
		补充:
		rpoplpush list1 list2 移除list1最后一个元素,并将该元素添加到list2并返回此元素
		用此命令可以实现订单下单流程、用户系统登录注册短信等。


■ 性能总结
		它是一个字符串链表,left、right都可以插入添加;
		如果键不存在,创建新的链表;
		如果键已存在,新增内容;
		如果值全移除,对应的键也就消失了。
		链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。

set

唯一、无序

sadd key value1[value2]:向集合添加成员

scard key:返回集合成员数

smembers key:返回集合中所有成员

sismember key member:判断memeber元素是否是集合key成员的成员

srandmember key [count]:返回集合中一个或多个随机数

srem key member1 [member2]:移除集合中一个或多个成员

spop key:移除并返回集合中的一个随机元素

smove source destination member:将member元素从source集合移动到destination集合

sdiff key1 [key2]:返回给定的第一个集合和其他集合的差集(即在key1中的值而在其他key中找不到)

sdiffstore destination key1[key2]:返回给定的第一个集合与其他的集合的差集并存储在destination中

eg:set1:1 2 3 set2:3 4 5 6 sdiffstore set3 set1 set2 smembers set3 result:1 2

sinter key1 [key2]:返回所有集合的交集

sunion key1 [key2]:返回所有集合的并集

对两个集合间的数据[计算]进行交集、并集、差集运算
	1、以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。
	2、利用唯一性,可以统计访问网站的所有独立 IP

zset

有序且不重复。每个元素都会关联一个double类型的分数,Redis通过分数进行从小到大的排序。分数可以重复

zadd key score1 memeber1
zcard key :获取集合中的元素数量
zcount key min max 计算在有序集合中指定区间分数的成员数
zcount key min max 计算在有序集合中指定区间分数的成员数
zrange key start stop 指定输出索引范围内的成员
zrangebyscore key min max 指定输出score区间内的成员

zrank key member:返回有序集合指定成员的索引

zrevrange key start stop :返回有序集中指定区间内的成员,通过索引,分数从高到底
zrem key member [member …] 移除有序集合中的一个或多个成员
zremrangebyrank key start stop 移除有序集合中给定的索引区间的所有成员(第一名是0)(低到高排序)
zremrangebyscore key min max 移除有序集合中给定的分数区间的所有成员

常用于排行榜:
如推特可以以发表时间作为score来存储
存储成绩
还可以用zset来做带权重的队列,让重要的任务先执行
解析配置文件

解析配置文件

#是否在后台运行;no:不是后台运行
daemonize yes

#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。
protected-mode yes

#redis的进程文件
pidfile /var/run/redis/redis-server.pid

#redis监听的端口号。
port 6379

#此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。该内核参数默认值一般是128,对于负载很大的服务程序来说大大的不够。一般会将它修改为2048或者更大。在/etc/sysctl.conf中添加:net.core.somaxconn = 2048,然后在终端中执行sysctl -p。
tcp-backlog 511

#指定 redis 只接收来自于该 IP 地址的请求,如果不进行设置,那么将处理所有请求
#bind 127.0.0.1
#bind 0.0.0.0

#配置unix socket来让redis支持监听本地连接。
# unixsocket /var/run/redis/redis.sock

#配置unix socket使用文件的权限
# unixsocketperm 700

# 此参数为设置客户端空闲超过timeout,服务端会断开连接,为0则服务端不会主动断开连接,不能小于0。
timeout 0

#tcp keepalive参数。如果设置不为0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有两个好处:检测挂掉的对端。降低中间设备出问题而导致网络看似连接却已经与对端端口的问题。在Linux内核中,设置了keepalive,redis会定时给对端发送ack。检测到对端关闭需要两倍的设置值。
tcp-keepalive 0

#指定了服务端日志的级别。级别包括:debug(很多信息,方便开发、测试),verbose(许多有用的信息,但是没有debug级别信息多),notice(适当的日志级别,适合生产环境),warn(只有非常重要的信息)
loglevel notice

#指定了记录日志的文件。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。
logfile /var/log/redis/redis-server.log

#是否打开记录syslog功能
# syslog-enabled no

#syslog的标识符。
# syslog-ident redis

#日志的来源、设备
# syslog-facility local0

#数据库的数量,默认使用的数据库是DB 0。可以通过SELECT命令选择一个db
databases 16

# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) 
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) 
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000

#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes

#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes

#是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
rdbchecksum yes

#rdb文件的名称
dbfilename dump.rdb

#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data


############### 主从复制 ###############

#复制选项,slave复制对应的master。
# slaveof 

Redis的持久化

查看持久化信息
redis-cli info persistence


loading:服务器是否正在载入持久化文件

rdb_changes_since_last_save:离最近一次成功生成rdb文件,写入命令的个数,即有多少个写入命令没有持久化

rdb_bgsave_in_progress:服务器是否正在创建rdb文件

rdb_last_save_time:离最近一次成功创建rdb文件的时间戳。当前时间戳 - rdb_last_save_time=多少秒未成功生成rdb文件

rdb_last_bgsave_status:最近一次rdb持久化是否成功

rdb_last_bgsave_time_sec:最近一次成功生成rdb文件耗时秒数

rdb_current_bgsave_time_sec:如果服务器正在创建rdb文件,那么这个域记录的就是当前的创建操作已经耗费的秒数

rdb_last_cow_size:RDB过程中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等)。

aof_enabled:是否开启了aof

aof_rewrite_in_progress:标识aof的rewrite操作是否在进行中

aof_rewrite_scheduled:rewrite任务计划,当客户端发送bgrewriteaof指令,如果当前rewrite子进程正在执行,那么将客户端请求的bgrewriteaof变为计划任务,待aof子进程结束后执行rewrite

aof_last_rewrite_time_sec:最近一次aof rewrite耗费的时长

aof_current_rewrite_time_sec:如果rewrite操作正在进行,则记录所使用的时间,单位秒

aof_last_bgrewrite_status:上次bgrewriteaof操作的状态

aof_last_write_status:上次aof写入状态

aof_last_cow_size:AOF过程中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等)。

① RDB

在指定的时间间隔内生成内存中整个数据集的持久化快照。快照文件默认被存储在当前文件夹中,名称为dump.rdb,可以通过dir和dbfilename参数来修改默认值。

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何的IO操作的,这就确保了极高的性能。

配置文件
# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) 
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) 
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000

#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes

#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes

#是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
rdbchecksum yes

#rdb文件的名称
dbfilename dump.rdb

#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data

Fork

fork的作用相当于复制一个与当前进程一样的进程。但是是一个全新的进程,并作为原进程的子进程。

触发条件

1,通过配制文件中的save条件(可自己配置)

save 900 1
save 300 10
save 60 10000

2,手动通过save和bgsave命令

  • save:save时只管保存,其他不管,全部阻塞
  • bgsave:redis会在后台异步的进行快照操作,同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的事件

1,通过flushall命令,也会产生dump.rdb文件,但是里面是空的,无意义。
2,通过shutdown命令,安全退出,也会生成快照文件(和异常退出形成对比,比如:kill杀死进程的方式)

如何恢复
appendonly no 
dbfilename dump.rdb 
dir /var/lib/redis #可以自行指定

appendonly 设置成no,redis启动时会把/var/lib/redis 目录下的dump.rdb 中的数据恢复。dir 和dbfilename 都可以设置。

优势

  • 恢复数据的速度很快,适合大规模的数据恢复,而又对部分数据不敏感的情况
  • dump.db文件是一个压缩的二进制文件,文件暂用空间小

劣势

  • 当出现异常退出时,会丢失最后一次快照后的数据

  • 当fork的时候,内存的中的数据会被克隆一份,大致两倍的膨胀需要考虑。而且,当数据过大时,fork操作占用过多的系统资源,造成主服务器进程假死。

使用场景

  • 数据备份

  • 可容忍部分数据丢失

  • 跨数据中心的容灾备份

② AOF

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作补不可记录),只许追加文件但不可以改写文件,redis启动之初会读取改文件重新构建数据。保存的是appendonly.aof文件
aof机制默认关闭,可以通过appendonly = yes参数开启aof机制,通过appendfilename = myaoffile.aof指定aof文件名称。

aof持久化的一些策略配置

#aof持久化策略的配置 
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。 
#always表示每次写入都执行fsync,以保证数据同步到磁盘。 
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。 
appendfsync everysec

对于触发aof重写机制也可以通过配置文件来进行设置:

# aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。 
auto-aof-rewrite-percentage 100
# 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写 
auto-aof-rewrite-min-size 64mb

当aop重写时会引发重写和持久化追加同时发生的问题,可以通过no-appendfsync-on-rewrite no进行配置

# 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果对延迟要求很高的应用,这个字段可以设置为yes,,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,不会造成阻塞的问题(因为没有磁盘竞争),等rewrite完成后再写入,这个时候redis会丢失数据。Linux的默认fsync策略是30秒。可能丢失30秒数据。因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。 
no-appendfsync-on-rewrite no

如何恢复

正常恢复

将文件放到dir指定的文件夹下,当redis启动的时候会自动加载数据,注意:aof文件的优先级比dump大

异常恢复
  • 有些操作可以直接到appendonly.aof文件里去修改。

    eg:使用了flushall这个命令,此刻持久化文件中就会有这么一条命令记录,把它删掉就可以了

  • 写坏的文件可以通过 redis-check-aof --fix 进行修复

优势

1,根据不同的策略,可以实现每秒,每一次修改操作的同步持久化,就算在最恶劣的情况下只会丢失不会超过两秒数据。

2,当文件太大时,会触发重写机制,确保文件不会太大。

3,文件可以简单的读懂

劣势

1,aof文件的大小太大,就算有重写机制,但重写所造成的阻塞问题是不可避免的
2,aof文件恢复速度慢

总结

1,果你只希望你的数据在服务器运行的时候存在,可以不使用任何的持久化方式

2,一般建议同时开启两种持久化方式。AOF进行数据的持久化,确保数据不会丢失太多,而RDB更适合用于备份数据库,留着一个做万一的手段。

3,性能建议:

因为RDB文件只用做后备用途,建议只在slave上持久化RDB文件,而且只要在15分钟备份一次就够了,只保留900 1这条规则。

如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价:1、带来了持续的IO;2、AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。

如果不Enable AOF,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时宕掉,会丢失10几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构。

Redis的事务

可以一次执行多个命令,本质是一组命令的集合。一个事物中的所有命令都会被序列化,按顺序的串行执行而不会被其他命令插入,不许加塞。
一个队列中,一次性的,顺序的,排他的执行一系列命令。

常用命令

multi 标记一个事务的开始
exec 执行所有事务块内的命令
discard 取消事务,放弃执行事务块内的所有命令
watch key [key] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
unwatch 取消watch命令对所有 key 的监视。

这么玩

1,正常执行
Redis、Spring Boot + Redis_第1张图片
2,放弃事务
Redis、Spring Boot + Redis_第2张图片

乐观锁和悲观锁

Watch监控

watch指令,类似乐观锁,如果key的值已经被修改了,那么整个事务队列都不会被执行,同时返回一个Nullmulti-bulk应答以通知调用者事务执行失败。

注意:一旦执行了exec或者discard,之前加的所有监控锁都会被取消掉了。

例子:

  • 初始化信用卡的可用余额和欠额
    Redis、Spring Boot + Redis_第3张图片
  • 无加塞篡改
    Redis、Spring Boot + Redis_第4张图片
  • 有加塞篡改,当watch的key被修改,后面的那个事务全部执行失败
    Redis、Spring Boot + Redis_第5张图片
  • unwatch
    Redis、Spring Boot + Redis_第6张图片
3阶段

开启:以multi开启事务

入队:将多个命令入队到事务中,接到这些命令不会立刻执行,而是放到等待执行的事务队列里面

执行:有exec命令触发事务

3特性

单独的隔离操作:事务中的所有命令都会序列化,按顺序的执行。事务在等待执行的时候,不会被其他客户端发送来的米命令请求打断

没有隔离级别的概念:队列中的所有命令没有提交exec之前都是不会被执行的

不保证原子性:redis中如果一条命令执行失败,其后的命令仍然会被执行,没有回滚


Redis的发布订阅(一般不用)

发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
Redis、Spring Boot + Redis_第7张图片
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
Redis、Spring Boot + Redis_第8张图片

命令
subscribe channel [channel…]:订阅一个或多个频道的信息
psubscribe pattern [pattern…]:订阅一个或多个符合规定模式的频道
publish channel message :将信息发送到指定频道
unsubscribe [channel[channel…]]:退订频道
punsubscribe [pattern[pattern…]]:退订所有给定模式的频道
应用场景
构建实时的消息系统,比如普通聊天、群聊等功能。 1、博客网站订阅,当作者发布就可以推送给粉丝 2、微信公众号模式

Redis的复制(Master/Slave)

就是我们常说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主

  • 读写分离
  • ​容灾恢复
怎么玩
#配置从库
slaveof 主库ip 主库端口
#查看主从信息
info replication

每次与master断开后,都需要重新连接,除非你配置进redis.conf文件

常用的主从方式
一主二仆

含义:就是一个Master两个Slave
Redis、Spring Boot + Redis_第9张图片
通过info replication查看主从信息

# Replication 
role:master connected_slaves:0 
master_replid:f6baff9abfda12ca58048cfce4b0e2c1f4683da1 
master_replid2:e8fe596d47d9d1d923d56d884b28128b78d2c1e0 
master_repl_offset:0 
second_repl_offset:1 
repl_backlog_active:0 
repl_backlog_size:1048576 
repl_backlog_first_byte_offset:0 
repl_backlog_histlen:0
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:0
master_link_down_since_seconds:1585217521
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:adbec19afa734e84a333b07ea2f33c43c73fe743
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
注意
  1. 第一次slave1 和slave2切入点,是全量复制,之后是增量复制
  2. 主机可以写,但是从机不可以写,从机只能读
  3. 主机shutdowm后从机待机状态,等主机回来后,主机新增记录从机可以顺利复制
  4. 从机shutdowm后,每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
  5. 从机复制到的数据,会被本机持久化。就算shutdown断开连接依然会有数据。
  6. 重新连接或者变更master,会清除之前的数据,重新建立拷贝最新的数据
薪火相传

含义:就是上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力。
Redis、Spring Boot + Redis_第10张图片注意事项和一主二仆差不多,但注意虽然有slave是相对master,但是依然是slave

反客为主
SLAVEOF no one

使当前数据库停止与其他数据库的同步,转成主数据库

哨兵模式(sentinel)

反客为主的自动版,能够后台监控Master库是否故障,如果故障了根据投票数自动将slave库转换为主库。一组sentinel能
同时监控多个Master。

使用步骤:

  1. 在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;
  2. 配置哨兵,在sentinel.conf文件中填入内容(可以配置多个)
    	#说明:最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机。 
    	sentinel monitor 被监控数据库名字(自己起名字)ip port 1
    
  3. 启动哨兵模式(路径按照自己的需求进行配置):
    	redis-sentinel  /myredis/sentinel.conf
    

注意:
4. 当master挂掉后,会通过选票进行选出下一个master。而且只有使用了sentinel.conf启动的才能开启选票
5. 当原来的master后来后,很不幸变成了slave。

复制原理
  1. Slave启动成功连接到master后会发送一个sync命令;

  2. Master接到命令启动后的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master

    将传送整个数据文件到slave,以完成一次完全同步;

  3. 全量复制:而slave服务在数据库文件数据后,将其存盘并加载到内存中;

  4. 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步;

  5. 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行。

复制的缺点

延时,由于所有的写操作都是在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使得这个问题更加严重。

命令
命令 作用
slaveof 主库ip 主库端口 配置从库
info replication 查看redis主从复制的情况
slaveof no one 使当前数据库停止与其他数据库的同步,转成主数据库
sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1 配置哨兵,监视master
redis-sentinel /myredis/sentinel.conf 以哨兵模式启动redis



Spring Boot 与 Redis 整合

我的 redis.conf 文件
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 30
daemonize yes
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile "/var/log/redis/redis-server.log"
databases 16
always-show-logo yes
save 900 1
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
oom-score-adj no
oom-score-adj-values 0 200 800
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes

引入maven坐标
// springboot2.0以后默认使用lettuce作为操作redis的客户端,它使用netty进行网络通信
// lettuce的bug导致netty堆外内存溢出。netty如果没有指定堆外内存,默认使用Xms的值,可以使用-Dio.netty.maxDirectMemory进行设置
// 解决方案:由于是lettuce的bug造成,不要直接使用-Dio.netty.maxDirectMemory去调大虚拟机堆外内存,治标不治本。
//		1)、升级lettuce客户端。但是没有解决的
//		2)、切换使用jedis
// lettuce和jedis是操作redis的底层客户端,RedisTemplate是再次封装


<dependency>
    <groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-data-redisartifactId>
	<exclusions>
	   <exclusion>
	     <groupId>io.lettucegroupId>
	     <artifactId>lettuce-coreartifactId>
	  exclusion>
	exclusions>
dependency>
<dependency>
	<groupId>redis.clientsgroupId>
	<artifactId>jedisartifactId>
dependency>
properties配置(只做演示的简单配置)
## redis配置
# 服务器地址
spring.redis.host=39.106.59.154
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大阻塞等待时间(使用负值表示没有限制)  
spring.redis.lettuce.pool.max-wait=30000
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=200
# 连接池中的最大空闲连接  
spring.redis.lettuce.pool.max-idle=10
# 连接池中的最小空闲连接  
spring.redis.lettuce.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
编写RedisConfig配置类(写法比较固定)

import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;

/**
 * Redis 配置
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 自定义 redisTemplate 配置
     * 
     * @param factory
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<Object>(Object.class);
        new GenericJackson2JsonRedisSerializer();
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }

    /**
     * 解决保存的redis中数据乱码的问题
     * 
     * @param cacheProperties
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        config = config.serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }

    /**
     * 自定义缓存key生成策略。可以不写。默认是名字是缓存注解的 value::key
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

}

使用 RedisUtil 工具类操作redis(RedisUtil工具类在最下面)

举个栗子:【service层中 增加点赞数】
我用 hash类型 存点赞数的

//注入RedisUtil
@Autowired
private RedisUtil redisUtil;

//通过id增加点赞数
@Override
public void incLikeCntByArticleId(Long articleId) {
	// 点赞后,点赞数增加 1
	redisUtil.hIncrBy(articleLikeCountKey, articleLikeCountField + articleId, 1);
}

使用注解进行缓存,注解多用于查询方法(一般加上service层的方法上)

举个栗子。网页首页访问数量大,可以把首页数据缓存到redis。
( 更多注解使用方式:https://blog.csdn.net/qq_22200097/article/details/82747149 )

    /**
     * 1、查询redis缓存中是否存在需要的数据  hasKey
     * 2、如果缓存不存在从数据库中取出数据、并将数据存入缓存  set
     * 3、如果缓存存在则从缓存中读取数据  get
     * 如果没自定义缓存key生成策略,则保存到redis中的key为 index::selectAllArticle
     */
     // 注意:这里的 keyGenerator 是上面自定义缓存key生成策略的方法名
     // unless : 查询结果不为空时才缓存
     // condition : 参数 offset 等于 0 的时候才缓存(只缓存第一页的商品)
    @Cacheable(cacheNames = "goods", keyGenerator = "keyGenerator", unless = "#result == null",condition = "#offset==0")
    @Override
    public List<GoodsVo> listShowGoods(Long offset, Long size) {
        return goodsMapper.listShowGoods(offset, size);
    }



为方便操作,编写 RedisUtil 工具类 「来自 gitee」
package com.article.util;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Component;

/**
 * redisTemplate封装
 * @author June
 * @date 2020/09/05
 * @version V1.0
 */
@Component
public class RedisUtil {
	
	@Autowired
	private StringRedisTemplate redisTemplate;

	public void setRedisTemplate(StringRedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public StringRedisTemplate getRedisTemplate() {
		return this.redisTemplate;
	}

	/** -------------------key相关操作--------------------- */

	/**
	 * 删除key
	 * 
	 * @param key
	 */
	public void delete(String key) {
		redisTemplate.delete(key);
	}

	/**
	 * 批量删除key
	 * 
	 * @param keys
	 */
	public void delete(Collection<String> keys) {
		redisTemplate.delete(keys);
	}

	/**
	 * 序列化key
	 * 
	 * @param key
	 * @return
	 */
	public byte[] dump(String key) {
		return redisTemplate.dump(key);
	}

	/**
	 * 是否存在key
	 * 
	 * @param key
	 * @return
	 */
	public Boolean hasKey(String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 设置过期时间
	 * 
	 * @param key
	 * @param timeout
	 * @param unit
	 * @return
	 */
	public Boolean expire(String key, long timeout, TimeUnit unit) {
		return redisTemplate.expire(key, timeout, unit);
	}

	/**
	 * 设置过期时间
	 * 
	 * @param key
	 * @param date
	 * @return
	 */
	public Boolean expireAt(String key, Date date) {
		return redisTemplate.expireAt(key, date);
	}

	/**
	 * 查找匹配的key
	 * 
	 * @param pattern
	 * @return
	 */
	public Set<String> keys(String pattern) {
		return redisTemplate.keys(pattern);
	}

	/**
	 * 将当前数据库的 key 移动到给定的数据库 db 当中
	 * 
	 * @param key
	 * @param dbIndex
	 * @return
	 */
	public Boolean move(String key, int dbIndex) {
		return redisTemplate.move(key, dbIndex);
	}

	/**
	 * 移除 key 的过期时间,key 将持久保持
	 * 
	 * @param key
	 * @return
	 */
	public Boolean persist(String key) {
		return redisTemplate.persist(key);
	}

	/**
	 * 返回 key 的剩余的过期时间
	 * 
	 * @param key
	 * @param unit
	 * @return
	 */
	public Long getExpire(String key, TimeUnit unit) {
		return redisTemplate.getExpire(key, unit);
	}

	/**
	 * 返回 key 的剩余的过期时间
	 * 
	 * @param key
	 * @return
	 */
	public Long getExpire(String key) {
		return redisTemplate.getExpire(key);
	}

	/**
	 * 从当前数据库中随机返回一个 key
	 * 
	 * @return
	 */
	public String randomKey() {
		return redisTemplate.randomKey();
	}

	/**
	 * 修改 key 的名称
	 * 
	 * @param oldKey
	 * @param newKey
	 */
	public void rename(String oldKey, String newKey) {
		redisTemplate.rename(oldKey, newKey);
	}

	/**
	 * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
	 * 
	 * @param oldKey
	 * @param newKey
	 * @return
	 */
	public Boolean renameIfAbsent(String oldKey, String newKey) {
		return redisTemplate.renameIfAbsent(oldKey, newKey);
	}

	/**
	 * 返回 key 所储存的值的类型
	 * 
	 * @param key
	 * @return
	 */
	public DataType type(String key) {
		return redisTemplate.type(key);
	}

	/** -------------------string相关操作--------------------- */

	/**
	 * 设置指定 key 的值
	 * @param key
	 * @param value
	 */
	public void set(String key, String value) {
		redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 获取指定 key 的值
	 * @param key
	 * @return
	 */
	public String get(String key) {
		return redisTemplate.opsForValue().get(key);
	}

	/**
	 * 返回 key 中字符串值的子字符
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public String getRange(String key, long start, long end) {
		return redisTemplate.opsForValue().get(key, start, end);
	}

	/**
	 * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public String getAndSet(String key, String value) {
		return redisTemplate.opsForValue().getAndSet(key, value);
	}

	/**
	 * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
	 * 
	 * @param key
	 * @param offset
	 * @return
	 */
	public Boolean getBit(String key, long offset) {
		return redisTemplate.opsForValue().getBit(key, offset);
	}

	/**
	 * 批量获取
	 * 
	 * @param keys
	 * @return
	 */
	public List<String> multiGet(Collection<String> keys) {
		return redisTemplate.opsForValue().multiGet(keys);
	}

	/**
	 * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
	 * 
	 * @param key
	 * @param postion
	 *            位置
	 * @param value
	 *            值,true为1, false为0
	 * @return
	 */
	public boolean setBit(String key, long offset, boolean value) {
		return redisTemplate.opsForValue().setBit(key, offset, value);
	}

	/**
	 * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
	 * 
	 * @param key
	 * @param value
	 * @param timeout
	 *            过期时间
	 * @param unit
	 *            时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
	 *            秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
	 */
	public void setEx(String key, String value, long timeout, TimeUnit unit) {
		redisTemplate.opsForValue().set(key, value, timeout, unit);
	}

	/**
	 * 只有在 key 不存在时设置 key 的值
	 * 
	 * @param key
	 * @param value
	 * @return 之前已经存在返回false,不存在返回true
	 */
	public boolean setIfAbsent(String key, String value) {
		return redisTemplate.opsForValue().setIfAbsent(key, value);
	}

	/**
	 * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
	 * 
	 * @param key
	 * @param value
	 * @param offset
	 *            从指定位置开始覆写
	 */
	public void setRange(String key, String value, long offset) {
		redisTemplate.opsForValue().set(key, value, offset);
	}

	/**
	 * 获取字符串的长度
	 * 
	 * @param key
	 * @return
	 */
	public Long size(String key) {
		return redisTemplate.opsForValue().size(key);
	}

	/**
	 * 批量添加
	 * 
	 * @param maps
	 */
	public void multiSet(Map<String, String> maps) {
		redisTemplate.opsForValue().multiSet(maps);
	}

	/**
	 * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
	 * 
	 * @param maps
	 * @return 之前已经存在返回false,不存在返回true
	 */
	public boolean multiSetIfAbsent(Map<String, String> maps) {
		return redisTemplate.opsForValue().multiSetIfAbsent(maps);
	}

	/**
	 * 增加(自增长), 负数则为自减
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long incrBy(String key, long increment) {
		return redisTemplate.opsForValue().increment(key, increment);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Double incrByFloat(String key, double increment) {
		return redisTemplate.opsForValue().increment(key, increment);
	}

	/**
	 * 追加到末尾
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Integer append(String key, String value) {
		return redisTemplate.opsForValue().append(key, value);
	}

	/** -------------------hash相关操作------------------------- */

	/**
	 * 获取存储在哈希表中指定字段的值
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public Object hGet(String key, String field) {
		return redisTemplate.opsForHash().get(key, field);
	}

	/**
	 * 获取所有给定字段的值
	 * 
	 * @param key
	 * @return
	 */
	public Map<Object, Object> hGetAll(String key) {
		return redisTemplate.opsForHash().entries(key);
	}

	/**
	 * 获取所有给定字段的值
	 * 
	 * @param key
	 * @param fields
	 * @return
	 */
	public List<Object> hMultiGet(String key, Collection<Object> fields) {
		return redisTemplate.opsForHash().multiGet(key, fields);
	}

	public void hPut(String key, String hashKey, String value) {
		redisTemplate.opsForHash().put(key, hashKey, value);
	}

	public void hPutAll(String key, Map<String, String> maps) {
		redisTemplate.opsForHash().putAll(key, maps);
	}

	/**
	 * 仅当hashKey不存在时才设置
	 * 
	 * @param key
	 * @param hashKey
	 * @param value
	 * @return
	 */
	public Boolean hPutIfAbsent(String key, String hashKey, String value) {
		return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
	}

	/**
	 * 删除一个或多个哈希表字段
	 * 
	 * @param key
	 * @param fields
	 * @return
	 */
	public Long hDelete(String key, Object... fields) {
		return redisTemplate.opsForHash().delete(key, fields);
	}

	/**
	 * 查看哈希表 key 中,指定的字段是否存在
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public boolean hExists(String key, String field) {
		return redisTemplate.opsForHash().hasKey(key, field);
	}

	/**
	 * 为哈希表 key 中的指定字段的整数值加上增量 increment
	 * 
	 * @param key
	 * @param field
	 * @param increment
	 * @return
	 */
	public Long hIncrBy(String key, Object field, long increment) {
		return redisTemplate.opsForHash().increment(key, field, increment);
	}

	/**
	 * 为哈希表 key 中的指定字段的整数值加上增量 increment
	 * 
	 * @param key
	 * @param field
	 * @param delta
	 * @return
	 */
	public Double hIncrByFloat(String key, Object field, double delta) {
		return redisTemplate.opsForHash().increment(key, field, delta);
	}

	/**
	 * 获取所有哈希表中的字段
	 * 
	 * @param key
	 * @return
	 */
	public Set<Object> hKeys(String key) {
		return redisTemplate.opsForHash().keys(key);
	}

	/**
	 * 获取哈希表中字段的数量
	 * 
	 * @param key
	 * @return
	 */
	public Long hSize(String key) {
		return redisTemplate.opsForHash().size(key);
	}

	/**
	 * 获取哈希表中所有值
	 * 
	 * @param key
	 * @return
	 */
	public List<Object> hValues(String key) {
		return redisTemplate.opsForHash().values(key);
	}

	/**
	 * 迭代哈希表中的键值对
	 * 
	 * @param key
	 * @param options
	 * @return
	 */
	public Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
		return redisTemplate.opsForHash().scan(key, options);
	}

	/** ------------------------list相关操作---------------------------- */

	/**
	 * 通过索引获取列表中的元素
	 * 
	 * @param key
	 * @param index
	 * @return
	 */
	public String lIndex(String key, long index) {
		return redisTemplate.opsForList().index(key, index);
	}

	/**
	 * 获取列表指定范围内的元素
	 * 
	 * @param key
	 * @param start
	 *            开始位置, 0是开始位置
	 * @param end
	 *            结束位置, -1返回所有
	 * @return
	 */
	public List<String> lRange(String key, long start, long end) {
		return redisTemplate.opsForList().range(key, start, end);
	}

	/**
	 * 存储在list头部
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lLeftPush(String key, String value) {
		return redisTemplate.opsForList().leftPush(key, value);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lLeftPushAll(String key, String... value) {
		return redisTemplate.opsForList().leftPushAll(key, value);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lLeftPushAll(String key, Collection<String> value) {
		return redisTemplate.opsForList().leftPushAll(key, value);
	}

	/**
	 * 当list存在的时候才加入
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lLeftPushIfPresent(String key, String value) {
		return redisTemplate.opsForList().leftPushIfPresent(key, value);
	}

	/**
	 * 如果pivot存在,再pivot前面添加
	 * 
	 * @param key
	 * @param pivot
	 * @param value
	 * @return
	 */
	public Long lLeftPush(String key, String pivot, String value) {
		return redisTemplate.opsForList().leftPush(key, pivot, value);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lRightPush(String key, String value) {
		return redisTemplate.opsForList().rightPush(key, value);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lRightPushAll(String key, String... value) {
		return redisTemplate.opsForList().rightPushAll(key, value);
	}

	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lRightPushAll(String key, Collection<String> value) {
		return redisTemplate.opsForList().rightPushAll(key, value);
	}

	/**
	 * 为已存在的列表添加值
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long lRightPushIfPresent(String key, String value) {
		return redisTemplate.opsForList().rightPushIfPresent(key, value);
	}

	/**
	 * 在pivot元素的右边添加值
	 * 
	 * @param key
	 * @param pivot
	 * @param value
	 * @return
	 */
	public Long lRightPush(String key, String pivot, String value) {
		return redisTemplate.opsForList().rightPush(key, pivot, value);
	}

	/**
	 * 通过索引设置列表元素的值
	 * 
	 * @param key
	 * @param index
	 *            位置
	 * @param value
	 */
	public void lSet(String key, long index, String value) {
		redisTemplate.opsForList().set(key, index, value);
	}

	/**
	 * 移出并获取列表的第一个元素
	 * 
	 * @param key
	 * @return 删除的元素
	 */
	public String lLeftPop(String key) {
		return redisTemplate.opsForList().leftPop(key);
	}

	/**
	 * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
	 * 
	 * @param key
	 * @param timeout
	 *            等待时间
	 * @param unit
	 *            时间单位
	 * @return
	 */
	public String lBLeftPop(String key, long timeout, TimeUnit unit) {
		return redisTemplate.opsForList().leftPop(key, timeout, unit);
	}

	/**
	 * 移除并获取列表最后一个元素
	 * 
	 * @param key
	 * @return 删除的元素
	 */
	public String lRightPop(String key) {
		return redisTemplate.opsForList().rightPop(key);
	}

	/**
	 * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
	 * 
	 * @param key
	 * @param timeout
	 *            等待时间
	 * @param unit
	 *            时间单位
	 * @return
	 */
	public String lBRightPop(String key, long timeout, TimeUnit unit) {
		return redisTemplate.opsForList().rightPop(key, timeout, unit);
	}

	/**
	 * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
	 * 
	 * @param sourceKey
	 * @param destinationKey
	 * @return
	 */
	public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
		return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
	}

	/**
	 * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
	 * 
	 * @param sourceKey
	 * @param destinationKey
	 * @param timeout
	 * @param unit
	 * @return
	 */
	public String lBRightPopAndLeftPush(String sourceKey, String destinationKey, long timeout, TimeUnit unit) {
		return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit);
	}

	/**
	 * 删除集合中值等于value得元素
	 * 
	 * @param key
	 * @param index
	 *            index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
	 *            index<0, 从尾部开始删除第一个值等于value的元素;
	 * @param value
	 * @return
	 */
	public Long lRemove(String key, long index, String value) {
		return redisTemplate.opsForList().remove(key, index, value);
	}

	/**
	 * 裁剪list
	 * 
	 * @param key
	 * @param start
	 * @param end
	 */
	public void lTrim(String key, long start, long end) {
		redisTemplate.opsForList().trim(key, start, end);
	}

	/**
	 * 获取列表长度
	 * 
	 * @param key
	 * @return
	 */
	public Long lLen(String key) {
		return redisTemplate.opsForList().size(key);
	}

	/** --------------------set相关操作-------------------------- */

	/**
	 * set添加元素
	 * 
	 * @param key
	 * @param values
	 * @return
	 */
	public Long sAdd(String key, String... values) {
		return redisTemplate.opsForSet().add(key, values);
	}

	/**
	 * set移除元素
	 * 
	 * @param key
	 * @param values
	 * @return
	 */
	public Long sRemove(String key, Object... values) {
		return redisTemplate.opsForSet().remove(key, values);
	}

	/**
	 * 移除并返回集合的一个随机元素
	 * 
	 * @param key
	 * @return
	 */
	public String sPop(String key) {
		return redisTemplate.opsForSet().pop(key);
	}

	/**
	 * 将元素value从一个集合移到另一个集合
	 * 
	 * @param key
	 * @param value
	 * @param destKey
	 * @return
	 */
	public Boolean sMove(String key, String value, String destKey) {
		return redisTemplate.opsForSet().move(key, value, destKey);
	}

	/**
	 * 获取集合的大小
	 * 
	 * @param key
	 * @return
	 */
	public Long sSize(String key) {
		return redisTemplate.opsForSet().size(key);
	}

	/**
	 * 判断集合是否包含value
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Boolean sIsMember(String key, Object value) {
		return redisTemplate.opsForSet().isMember(key, value);
	}

	/**
	 * 获取两个集合的交集
	 * 
	 * @param key
	 * @param otherKey
	 * @return
	 */
	public Set<String> sIntersect(String key, String otherKey) {
		return redisTemplate.opsForSet().intersect(key, otherKey);
	}

	/**
	 * 获取key集合与多个集合的交集
	 * 
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public Set<String> sIntersect(String key, Collection<String> otherKeys) {
		return redisTemplate.opsForSet().intersect(key, otherKeys);
	}

	/**
	 * key集合与otherKey集合的交集存储到destKey集合中
	 * 
	 * @param key
	 * @param otherKey
	 * @param destKey
	 * @return
	 */
	public Long sIntersectAndStore(String key, String otherKey, String destKey) {
		return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);
	}

	/**
	 * key集合与多个集合的交集存储到destKey集合中
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Long sIntersectAndStore(String key, Collection<String> otherKeys, String destKey) {
		return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey);
	}

	/**
	 * 获取两个集合的并集
	 * 
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public Set<String> sUnion(String key, String otherKeys) {
		return redisTemplate.opsForSet().union(key, otherKeys);
	}

	/**
	 * 获取key集合与多个集合的并集
	 * 
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public Set<String> sUnion(String key, Collection<String> otherKeys) {
		return redisTemplate.opsForSet().union(key, otherKeys);
	}

	/**
	 * key集合与otherKey集合的并集存储到destKey中
	 * 
	 * @param key
	 * @param otherKey
	 * @param destKey
	 * @return
	 */
	public Long sUnionAndStore(String key, String otherKey, String destKey) {
		return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
	}

	/**
	 * key集合与多个集合的并集存储到destKey中
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Long sUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
		return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
	}

	/**
	 * 获取两个集合的差集
	 * 
	 * @param key
	 * @param otherKey
	 * @return
	 */
	public Set<String> sDifference(String key, String otherKey) {
		return redisTemplate.opsForSet().difference(key, otherKey);
	}

	/**
	 * 获取key集合与多个集合的差集
	 * 
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public Set<String> sDifference(String key, Collection<String> otherKeys) {
		return redisTemplate.opsForSet().difference(key, otherKeys);
	}

	/**
	 * key集合与otherKey集合的差集存储到destKey中
	 * 
	 * @param key
	 * @param otherKey
	 * @param destKey
	 * @return
	 */
	public Long sDifference(String key, String otherKey, String destKey) {
		return redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey);
	}

	/**
	 * key集合与多个集合的差集存储到destKey中
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Long sDifference(String key, Collection<String> otherKeys, String destKey) {
		return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, destKey);
	}

	/**
	 * 获取集合所有元素
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Set<String> setMembers(String key) {
		return redisTemplate.opsForSet().members(key);
	}

	/**
	 * 随机获取集合中的一个元素
	 * 
	 * @param key
	 * @return
	 */
	public String sRandomMember(String key) {
		return redisTemplate.opsForSet().randomMember(key);
	}

	/**
	 * 随机获取集合中count个元素
	 * 
	 * @param key
	 * @param count
	 * @return
	 */
	public List<String> sRandomMembers(String key, long count) {
		return redisTemplate.opsForSet().randomMembers(key, count);
	}

	/**
	 * 随机获取集合中count个元素并且去除重复的
	 * 
	 * @param key
	 * @param count
	 * @return
	 */
	public Set<String> sDistinctRandomMembers(String key, long count) {
		return redisTemplate.opsForSet().distinctRandomMembers(key, count);
	}

	/**
	 * 
	 * @param key
	 * @param options
	 * @return
	 */
	public Cursor<String> sScan(String key, ScanOptions options) {
		return redisTemplate.opsForSet().scan(key, options);
	}

	/**------------------zSet相关操作--------------------------------*/

	/**
	 * 添加元素,有序集合是按照元素的score值由小到大排列
	 * 
	 * @param key
	 * @param value
	 * @param score
	 * @return
	 */
	public Boolean zAdd(String key, String value, double score) {
		return redisTemplate.opsForZSet().add(key, value, score);
	}

	/**
	 * 
	 * @param key
	 * @param values
	 * @return
	 */
	public Long zAdd(String key, Set<TypedTuple<String>> values) {
		return redisTemplate.opsForZSet().add(key, values);
	}

	/**
	 * 
	 * @param key
	 * @param values
	 * @return
	 */
	public Long zRemove(String key, Object... values) {
		return redisTemplate.opsForZSet().remove(key, values);
	}

	/**
	 * 增加元素的score值,并返回增加后的值
	 * 
	 * @param key
	 * @param value
	 * @param delta
	 * @return
	 */
	public Double zIncrementScore(String key, String value, double delta) {
		return redisTemplate.opsForZSet().incrementScore(key, value, delta);
	}

	/**
	 * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
	 * 
	 * @param key
	 * @param value
	 * @return 0表示第一位
	 */
	public Long zRank(String key, Object value) {
		return redisTemplate.opsForZSet().rank(key, value);
	}

	/**
	 * 返回元素在集合的排名,按元素的score值由大到小排列
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Long zReverseRank(String key, Object value) {
		return redisTemplate.opsForZSet().reverseRank(key, value);
	}

	/**
	 * 获取集合的元素, 从小到大排序
	 * 
	 * @param key
	 * @param start
	 *            开始位置
	 * @param end
	 *            结束位置, -1查询所有
	 * @return
	 */
	public Set<String> zRange(String key, long start, long end) {
		return redisTemplate.opsForZSet().range(key, start, end);
	}

	/**
	 * 获取集合元素, 并且把score值也获取
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<TypedTuple<String>> zRangeWithScores(String key, long start, long end) {
		return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
	}

	/**
	 * 根据Score值查询集合元素
	 * 
	 * @param key
	 * @param min
	 *            最小值
	 * @param max
	 *            最大值
	 * @return
	 */
	public Set<String> zRangeByScore(String key, double min, double max) {
		return redisTemplate.opsForZSet().rangeByScore(key, min, max);
	}

	/**
	 * 根据Score值查询集合元素, 从小到大排序
	 * 
	 * @param key
	 * @param min
	 *            最小值
	 * @param max
	 *            最大值
	 * @return
	 */
	public Set<TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max) {
		return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
	}

	/**
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) {
		return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end);
	}

	/**
	 * 获取集合的元素, 从大到小排序
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<String> zReverseRange(String key, long start, long end) {
		return redisTemplate.opsForZSet().reverseRange(key, start, end);
	}

	/**
	 * 获取集合的元素, 从大到小排序, 并返回score值
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) {
		return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
	}

	/**
	 * 根据Score值查询集合元素, 从大到小排序
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<String> zReverseRangeByScore(String key, double min, double max) {
		return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
	}

	/**
	 * 根据Score值查询集合元素, 从大到小排序
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<TypedTuple<String>> zReverseRangeByScoreWithScores(String key, double min, double max) {
		return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max);
	}

	/**
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<String> zReverseRangeByScore(String key, double min, double max, long start, long end) {
		return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, start, end);
	}

	/**
	 * 根据score值获取集合元素数量
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Long zCount(String key, double min, double max) {
		return redisTemplate.opsForZSet().count(key, min, max);
	}

	/**
	 * 获取集合大小
	 * 
	 * @param key
	 * @return
	 */
	public Long zSize(String key) {
		return redisTemplate.opsForZSet().size(key);
	}

	/**
	 * 获取集合大小
	 * 
	 * @param key
	 * @return
	 */
	public Long zZCard(String key) {
		return redisTemplate.opsForZSet().zCard(key);
	}

	/**
	 * 获取集合中value元素的score值
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Double zScore(String key, Object value) {
		return redisTemplate.opsForZSet().score(key, value);
	}

	/**
	 * 移除指定索引位置的成员
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Long zRemoveRange(String key, long start, long end) {
		return redisTemplate.opsForZSet().removeRange(key, start, end);
	}

	/**
	 * 根据指定的score值的范围来移除成员
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Long zRemoveRangeByScore(String key, double min, double max) {
		return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
	}

	/**
	 * 获取key和otherKey的并集并存储在destKey中
	 * 
	 * @param key
	 * @param otherKey
	 * @param destKey
	 * @return
	 */
	public Long zUnionAndStore(String key, String otherKey, String destKey) {
		return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
	}

	/**
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Long zUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
		return redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
	}

	/**
	 * 交集
	 * 
	 * @param key
	 * @param otherKey
	 * @param destKey
	 * @return
	 */
	public Long zIntersectAndStore(String key, String otherKey, String destKey) {
		return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey);
	}

	/**
	 * 交集
	 * 
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @return
	 */
	public Long zIntersectAndStore(String key, Collection<String> otherKeys, String destKey) {
		return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey);
	}

	/**
	 * 
	 * @param key
	 * @param options
	 * @return
	 */
	public Cursor<TypedTuple<String>> zScan(String key, ScanOptions options) {
		return redisTemplate.opsForZSet().scan(key, options);
	}

}

 





如有不正确的地方,欢迎指正,谢谢

你可能感兴趣的:(redis,spring,boot,数据库)