Redis

1.基本命令

#查看所有的key
redis 127.0.0.1:6379> keys * 
1) "users::1"
2) "sean"
3) "last_week_rank"

#查看key的类型
redis 127.0.0.1:6379> type sean
zset

#切换数据库
redis 127.0.0.1:6379> select 1
OK

#清空当前数据库
redis 127.0.0.1:6379[1]> flushdb
OK
redis 127.0.0.1:6379[1]> keys *
(empty list or set)

#清空全部数据库
redis 127.0.0.1:6379[1]> flushAll
OK
#查看key是否存在
redis 127.0.0.1:6379> exists sean
(integer) 1

2.基本数据类型

1.String(字符串)

#设置值
redis 127.0.0.1:6379> set key1 value1 
OK
#获取值
redis 127.0.0.1:6379> get key1 
"value1"
#追加字符串,如果当前key不存在,则相当于set key
redis 127.0.0.1:6379> append key1 ":hello world" 
(integer) 18
#获取字符串的长度
redis 127.0.0.1:6379> strlen key1
(integer) 18
#######################自增和自减##########################
#incr 自增1
redis 127.0.0.1:6379> incr views
(integer) 1
#decr自减1
redis 127.0.0.1:6379> decr views
(integer) 0
#自增步长10
redis 127.0.0.1:6379> incrby views 10
(integer) 10
#自减步长10
redis 127.0.0.1:6379> decrby views 10
(integer) 10
###################截取和替换##############################
redis 127.0.0.1:6379> set key1 "hello,my friend"
OK
redis 127.0.0.1:6379> get key1
"hello,my friend"
redis 127.0.0.1:6379> getrange key1 0 4 #截取字符串(0,4)
"hello"
redis 127.0.0.1:6379> getrange key1 0 -1  #获取全部的字符串
"hello,my friend"

# 对非空字符串进行 SETRANGE
redis> SET greeting "hello world"
OK
redis> SETRANGE greeting 6 "Redis"
(integer) 11
redis> GET greeting
"hello Redis"

# 对空字符串/不存在的 key 进行 SETRANGE
redis> EXISTS empty_string
(integer) 0
redis> SETRANGE empty_string 5 "Redis!"   # 对不存在的 key 使用 SETRANGE
(integer) 11
redis> GET empty_string                   # 空白处被"\x00"填充
"\x00\x00\x00\x00\x00Redis!"
#########################################################################
#SETEX命令(set with expire)设置过期时间

redis 127.0.0.1:6379> setex mydb 30 redis
OK
#这个命令类似于以下两个命令:
SET key value
EXPIRE key seconds  # 设置生存时间
#不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用

#SETNX (set if not exist)
#将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作

redis 127.0.0.1:6379> setnx my_setnx "if the key not exist,will be created , if exist will not change it"
(integer) 1
redis 127.0.0.1:6379> get my_setnx
"if the key not exist,will be created , if exist will not change it"
redis 127.0.0.1:6379> setnx my_setnx "change it"
(integer) 0
redis 127.0.0.1:6379> get my_setnx
"if the key not exist,will be created , if exist will not change it"

#批量设置或获取值
redis 127.0.0.1:6379> mset k1 v1 k2 v2 #MSET
OK
redis 127.0.0.1:6379> mget k1 k2 #MGET
1) "v1"
2) "v2"
redis 127.0.0.1:6379>


#设置一个id=1的user对象,值为json字符串来保存一个对象
#这里的key是一个巧妙地设计:user:{id}:{filed}
redis 127.0.0.1:6379> mset user:1:name zhangsan user:1:age 20
OK
redis 127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "20"

#getset组合命令  如果不存在,则返回nil,如果存在先返回存在的值,然后再作更新修改
redis 127.0.0.1:6379> getset test_getset "test  getset commond"
(nil)
redis 127.0.0.1:6379> get test_getset
"test  getset commond"
redis 127.0.0.1:6379> getset test_getset "change it to getset again"
"test  getset commond"
redis 127.0.0.1:6379> get test_getset
"change it to getset again"

2.List(列表)

在redis里面可以把list实现队列,栈,阻塞队列的功能

image-20210629171638378

#将一个或多个值 value 插入到列表 key 的表头  
Lpush key value #value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a 
RPush key value # value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c 

LPop key #移除并返回列表 key 的头元素。
RPop key #移除并返回列表 key 的尾元素

redis 127.0.0.1:6379> Lpush mylist one two three four #插入4条数据到mylist的list中
(integer) 4
redis 127.0.0.1:6379> lrange mylist 0 -1 #查看list中全部的元素
1) "four"
2) "three"
3) "two"
4) "one"
redis 127.0.0.1:6379> rpush mylist -1 #从底部插入元素-1
(integer) 5
redis 127.0.0.1:6379> lrange mylist 0 -1  #查看list中全部的元素
1) "four"
2) "three"
3) "two"
4) "one"
5) "-1"
redis 127.0.0.1:6379> rpop mylis #移除尾部元素-1,并返回元素-1
"-1"
redis 127.0.0.1:6379> lrange mylist 0 -1  #查看list中全部的元素
1) "four"
2) "three"
3) "two"
4) "one"
redis 127.0.0.1:6379> lpop mylist #移除头部元素four,并返回移除的元素
"four"
redis 127.0.0.1:6379> lrange mylist 0 -1  #查看list中全部的元素
1) "three"
2) "two"
3) "one"


#Lindex key index -->返回列表 key 中,下标为 index 的元素。
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "three"
2) "two"
3) "one"
redis 127.0.0.1:6379> lindex mylist 0
"three"
redis 127.0.0.1:6379> lindex mylist 1
"two"
redis 127.0.0.1:6379> lindex mylist 2
"one"
#Llen key  -->返回列表 key 的长度。
redis 127.0.0.1:6379> llen mylist
(integer) 3

#LRem key count value -->根据参数 count 的值,移除列表中与参数 value 相等的元素
#count 的值可以是以下几种:
    #count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
    #count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
    #count = 0 : 移除表中所有与 value 相等的值。
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "three"
2) "four"
3) "three"
4) "three"
5) "two"
6) "three"
redis 127.0.0.1:6379> lrem mylist -2 three #从尾部删除2个three
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "three"
2) "four"
3) "three"
4) "two"

#Ltrim key start end --> 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "three"
2) "four"
3) "three"
4) "two"
redis 127.0.0.1:6379> ltrim mylist 1 2
OK
redis 127.0.0.1:6379> lrange mylist 0 -1 
1) "four"
2) "three"

#RPopLPush source destination -->命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
    #将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
    #将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "four"
2) "three"
redis 127.0.0.1:6379> rpoplpush mylist newlist
"three"
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "four"
redis 127.0.0.1:6379> lrange newlist 0 -1
1) "three"
redis 127.0.0.1:6379>

#LSET key index value -->将列表 key 下标为 index 的元素的值设置为 value 。
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "test"
2) "foot"
3) "new"
4) "four"
redis 127.0.0.1:6379> lset mylist  0 "new test"
OK
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "new test"
2) "foot"
3) "new"
4) "four"
redis 127.0.0.1:6379
#LINSERT key BEFORE|AFTER pivot value -->将值 value 插入到列表 key 当中,位于值 pivot 之前或之后
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"

3. Set(集合)

set中的元素不能重复的

#SADD key member [member ...] --> 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
redis 127.0.0.1:6379> sadd myset 1 2 3 4 5 1 #向set集合中添加元素,重复元素将会被忽略
(integer) 5
redis 127.0.0.1:6379> smembers myset#查看set集合中的元素
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

# SISMEMBER key member --> 判断 member 元素是否集合 key 的成员。
redis 127.0.0.1:6379> sismember myset 1
(integer) 1
redis 127.0.0.1:6379> sismember myset 6
(integer) 0

#SCARD key --> 返回集合 key 的基数(集合中元素的数量)。
redis 127.0.0.1:6379> scard myset
(integer) 5

#SREM key member [member ...] --> 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
redis 127.0.0.1:6379> srem myset 1 5
(integer) 2
redis 127.0.0.1:6379> smembers myset
1) "2"
2) "3"
3) "4"

#SRANDMEMBER key [count] --> 如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素。
#从 Redis 2.6 版本开始
    #如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
    #如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
redis 127.0.0.1:6379> srandmember myset
"2"
redis 127.0.0.1:6379> srandmember myset 2
1) "3"
2) "2"
redis 127.0.0.1:6379> srandmember myset 8
1) "2"
2) "3"
3) "4"
redis 127.0.0.1:6379> srandmember myset -2
1) "3"
2) "3"
#SPOP key --> 移除并返回集合中的一个随机元素。
redis 127.0.0.1:6379> smembers myset
1) "2"
2) "3"
3) "4"
redis 127.0.0.1:6379> spop myset
"3"
redis 127.0.0.1:6379> spop myset
"2"

#SMOVE source destination member --> 将 member 元素从 source 集合移动到 destination 集合。
redis 127.0.0.1:6379> smove myset newset 8
(integer) 1
redis 127.0.0.1:6379> smembers myset
1) "4"
2) "9"
redis 127.0.0.1:6379> smembers newset
1) "8"

#数学集合中的交集,并集,差集
redis 127.0.0.1:6379> sadd myset a b c #myset集合中添加数据
(integer) 3
redis 127.0.0.1:6379> sadd newset c d e #newset集合中添加数据
(integer) 3
redis 127.0.0.1:6379> sinter myset newset #并集
1) "c"
redis 127.0.0.1:6379> sunion myset newset #交集
1) "c"
2) "a"
3) "b"
4) "e"
5) "d"
redis 127.0.0.1:6379> sdiff myset newset #差集
1) "a"
2) "b"

#场景--> 微博,B站中的共同关注,共同好友等

4. Hash(哈希)

Map集合, key-value其中value的值也是一个map的key-value,更加适合存储对象

image-20210629171605739

#HSET key field value --> 将哈希表 key 中的域 field 的值设为 value 
redis 127.0.0.1:6379> hset myhash name zhangsan
(integer) 1
redis 127.0.0.1:6379> hget myhash name
"zhangsan"
###################################################
#设置和获取多个key
redis 127.0.0.1:6379> hmset myhash age 13 phone 1356667778
OK
redis 127.0.0.1:6379> hmget myhash name age
1) "zhangsan"
2) "13"
###################################################
#获取全部的值
redis 127.0.0.1:6379> hgetall myhash
1) "name"
2) "zhangsan"
3) "age"
4) "13"
5) "phone"
6) "1356667778"

#获取所有的key
redis 127.0.0.1:6379> hkeys myhash
1) "name"
2) "age"
3) "phone"
#获取所有的values
redis 127.0.0.1:6379> hvals myhash
1) "19"
2) "1356667778"
###################################################
#获取hash的长度
redis 127.0.0.1:6379> hlen myhash
(integer) 3

#HDEL key field [field ...] --> 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
redis 127.0.0.1:6379> hdel myhash name
(integer) 1
redis 127.0.0.1:6379> hgetall myhash
1) "age"
2) "13"
3) "phone"
4) "1356667778"
redis 127.0.0.1:6379>



5. Zset(有序集合)

在set的基础上,增加了一个值score -->zset key field score value

image-20210629180209815


案例思路: set 排序 存储班级成绩表 工资表排序 排行榜的应用实现

String List Hash Set Zset
设置值 set key value LPush/RPush key value Hset key filed value Sadd key value Zadd key vlue
设置多个值 Mset key value1 value2 HMset key field1 value1 field2 value2
获取某个key的值 get key Lindex key index Hget key field
获取多个key的值 Mget key1 key1 HMget key field1 field2
查看全部值 getRange key start end lrange key start end SMembers key
删除值 LPop key /LRem key count value HDel key field SPOP key / SRem key member

3. 三种特殊数据类型

1. geospatial(地理位置)

朋友的定位,附近的人,打车距离计算

  • 适合只简单获取某二维点周围范围情况。比如当前位置周围有多少用户在线。
  • 适合精度低的场景。这个和GeoHash的算法有关,其本身是把地图二维化,把地图一直切两刀分成4块。具体的块状大小,和传入的经度、维度的有效位数有关。

可以查询一些测试数据: http://www.jsons.cn/lngcode/

6个命令

image-20210629184420674

GEOADD 将指定的地理空间位置(纬度、经度、名称)添加到指定的key

image-20210629192027906

#GEOADD key longitude latitude member [longitude latitude member ...] 命令格式

geoadd china:chengdu 104.06151 30.67387 QingYang 104.05114 30.69126 JinNiu 

GEOPOS 从key里返回所有给定位置元素的位置(经度和纬度)。

#GEOPOS key member [member ...] 命令格式
127.0.0.1:6379> geopos china:chengdu JinNiu
1) 1) "104.05114084482192993"
   2) "30.69125926566729134"

GEODIST 返回两个给定位置之间的距离

image-20210629192355717

#命令格式  GEODIST key member1 member2 [unit]127.0.0.1:6379> geodist china:chengdu PiXian WenJiang km"14.4971"

GEORADIUS 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素

image-20210629193102037

#命令格式 GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] 
[WITHDIST] [WITHHASH] [COUNT count]
127.0.0.1:6379> georadius china:chengdu 104.065735 30.659462 10 km
1) "ChengHua"
2) "WuHou"
3) "QingYang"
4) "JinNiu"

GEORADIUSBYMEMBER

image-20210629193446653

#命令格式 GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] 
[WITHDIST] [WITHHASH] [COUNT count]
127.0.0.1:6379> georadiusbymember china:chengdu PiXian 30 km 
1) "ChongZhou" 
2) "WenJiang" 
3) "ShuangLiu" 
4) "WuHou" 
5) "QingYang" 
6) "JinNiu" 
7) "PiXian" 
8) "PengZhou" 
9) "ChengHua"
10) "XinDu"

GEO底层的实现原理其实就是Zset,我们可以使用Zset命令来操作GEO

127.0.0.1:6379> zrange china:chengdu 0 -1 #查询所有的member
1) "PuJiang" 
2) "QiongLai" 
3) "XinJin" 
4) "DaYi" 
5) "ChongZhou" 
6) "WenJiang" 
7) "ShuangLiu" 
8) "WuHou" 
9) "QingYang"
10) "JinNiu"
11) "PiXian"
12) "DuJiangYan"
13) "PengZhou"
14) "LongQuan"
15) "ChengHua"
16) "XinDu"
17) "QingBaiJiang"
18) "JingTang"
127.0.0.1:6379> zrem china:chengdu QingBaiJiang #移除member 
QingBaiJiang(integer) 1

2. hyperloglog(基数统计)

应用案例: 统计网页的UV(一个人访问网站多次,但是还是算作一个人)

传统解决方法: 用一个set保存用户的Id,然后就可以统计set中的元素数量作为判断标志,但是这样做的缺点就是可能用户Id是使用uuid生成的,如果访问量大了后很容易造成内存溢出.

hyperloglog的占用内存是固定的,只需要12kb内存,如果是从内存角度来比较的话Hyperloglog是首选,会存在0.81%的错误率

127.0.0.1:6379> pfadd key1 a b c d e f g(integer) 1
127.0.0.1:6379> pfadd key2 d e k i m a(integer) 1
127.0.0.1:6379> pfmerge key3 key1 key2OK
127.0.0.1:6379> pfcount key1(integer) 7
127.0.0.1:6379> pfcount key2(integer) 6
127.0.0.1:6379> pfcount key3(integer) 10

如果允许容错那么一定可以用Hyperloglogfa

3. bitmap(位图)

Bit-map的基本思想就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省(PS:划重点 节省存储空间),只有2个状态的都可以使用bitmap

image-20210630104534651

#对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)
127.0.0.1:6379> setbit sign 0 1(integer) 0
127.0.0.1:6379> setbit sign 1 0(integer) 0#对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
127.0.0.1:6379> setbit sign 2 1 (integer) 0
127.0.0.1:6379> getbit sign 1(integer) 0
127.0.0.1:6379> getbit sign 0(integer) 1
#BITCOUNT key [start] [end] 计算给定字符串中,被设置为 1 的比特位的数量。
127.0.0.1:6379> bitcount sign 0 -1 (integer) 2

4.事务

  1. Redis单条命令是保证原子性的,但是事务不保存原子性.Redis事务没有隔离级别的概念

  2. Redis事务包括三个阶段

    • 开启事务(multi)
    • 命令入队(....)
    • 提交事务(exec)
  3. 执行Redis事务过程中出错,包括两种情况,在mysql关系型数据库的事务中,如果出现错误整个事务都会回滚,但是在redis中却不一定

    • 编译型异常,在Redis事务中的命令有误,事务中的所有命令都不会被执行

      127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v1QUEUED127.0.0.1:6379> getset k3 #错误指令(error) ERR wrong number of arguments for 'getset' command127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> exec #提交事务后,整个事务所有的命令都没执行(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k1(nil)
      
    • 运行时异常,如果事务队列中存在语法型错误,那么执行命令的时候,其他命令可以正常执行,错误命令抛出异常

      127.0.0.1:6379[1]> set k1 v1OK127.0.0.1:6379[1]> multiOK127.0.0.1:6379[1]> incr k1QUEUED127.0.0.1:6379[1]> set k2 v2QUEUED127.0.0.1:6379[1]> set k3 v3QUEUED127.0.0.1:6379[1]> get k2QUEUED127.0.0.1:6379[1]> get k3QUEUED127.0.0.1:6379[1]> exec1) (error) ERR value is not an integer or out of range2) OK3) OK4) "v2"5) "v3"
      

5.Jedis

  • 导入依赖

    <dependency>
        <groupId>redis.clients</groupId>    
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
    
  • 连接redis

    Jedis jedis = new Jedis("127.0.0.1", 6379);
    
  • 通过jedis操作redis,和操作redis命令一样,只是把命令换成了方法

    //拼接
    jedis.append("key1", " Hello world");
    //获取key对应的值
    System.out.println(jedis.get("key1"));
    //自增自减操作
    jedis.set("Incr_key","10");
    System.out.println(jedis.get("Incr_key"));
    jedis.incr("Incr_key");
    System.out.println(jedis.get("Incr_key"));
    jedis.incrBy("Incr_key",15);
    System.out.println(jedis.get("Incr_key"));
    jedis.decr("Incr_key");
    System.out.println(jedis.get("Incr_key"));
    jedis.decrBy("Incr_key",6);
    System.out.println(jedis.get("Incr_key"));
    

6. SpringBoot整合Redis

springBoot2.x后,原来使用的jedis被替换成了lettuce,因为jedis采用直连,多个线程操作的话,是不安全的,使用jedis pool连接池可以避免,BIO模式;而lettuce底层是使用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况,NIO模式

1. 整合步骤

  • 导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  • 配置properties文件

    spring.redis.host=localhost
    spring.redis.port=6379
    

    在使用redis pool时不能使用spring.redis.jedis.pool 需要使用spring.redis.lettuce.pool

  • 使用RedisTemplate操作redis

    /*操作redis的5大基本数据类型,在redisTemplate中操作对应的opsForxx方法 
    redisTemplate.opsForHash()
    redisTemplate.opsForList() 
    redisTemplate.opsForSet()
    redisTemplate.opsForValue() 
    redisTemplate.opsForZSet()*/
    redisTemplate.opsForValue().set("key99","test for opsForValue");
    System.out.println(redisTemplate.opsForValue().get("key99"));
    //对于事务 key的管理等操作,可以直接操作
    redisTemplateredisTemplate.multi();//开启事务
    redisTemplate.exec();//事务提交
    redisTemplate.discard();//取消事务
    redisTemplate.delete("key");//删除
    keyredisTemplate.watch("");//监控
    redisTemplate.unwatch();
    redisTemplate.expireAt("key",new Date());//获取数据库的连接
    RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
    connection.flushDb();connection.flushAll();
    

2. 源码分析

序列化的问题

image-20210630164941096

当我们直接把一个没有序列化的对象直接存Redis里面,会报错

image-20210630164915568

解决方法

自己编写一个redisConfig类,实现我们自己的序列化配置

@Configuration
public class RedisConfig 
{    @Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) 
{        
	//为了开发方便,一般直接使用<String,Object>        
	RedisTemplate<String, Object> template = new RedisTemplate<>();        template.setConnectionFactory(redisConnectionFactory);        
	//Jackson序列化配置        
	Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper objectMapper = new ObjectMapper();        
	objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);        
	//String序列化配置        
	StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        
	//key采用String的序列化        
	template.setKeySerializer(stringRedisSerializer);        
	//Hashkey采用String的序列化        
	template.setHashKeySerializer(stringRedisSerializer);        
	//value采用Json的序列化        
	template.setValueSerializer(jackson2JsonRedisSerializer);        
	//HashValue采用Json的序列化        
	template.setHashValueSerializer(jackson2JsonRedisSerializer);        
	template.afterPropertiesSet();        
	return template;    
}}

7. redis配置文件

Unit

image-20210630175441222

Redis的单位换算,以及单位对大小写不敏感

INCLUDES 包含

image-20210630175609489

可以通过include把其他配置文件合并到一起,和Spring配置文件的import导入SpringMVC的配置类似的功能

NETWORK 网络

image-20210630175809892

bind 127.0.0.1 #绑定Ip地址protected-mode yes #保护模式-->保护模式是一层安全保护,为了避免Redis 实例在互联网上保持打开状态被访问和利用,默认开启port 6379 #端口设置

GENERAL 通用

image-20210630175933961

daemonize yes #以守护线程的方式运行,默认是no,需要开启为yes,在windows环境下不支持以守护线程后台运行pidfile /var/run/redis.pid #如果以后台的方式运行,就需要指定一个pid文件#日志loglevel notice #日志级别# debug (a lot of information, useful for development/testing)             #开发环境# verbose (many rarely useful info, but not a mess like the debug level)   # notice (moderately verbose, what you want in production probably)        #生成环境# warning (only very important / critical messages are logged)             logfile "" #日志的文件名,如果文件名为空则为标准的输出databases 16 #默认16个数据库always-show-log yes #是否显示logo

SNAPSHOTTING 快照

image-20210701100048411

#持久化RDB相关配置# save <seconds> <changes> 在规定时间内,执行多少次操作,则会持久化到文件save 900 1 #如果900s(15min)内,至少有1个key进行修改,就会执行持久化操作save 300 10  #如果300s(5min)内,至少有10个key进行修改,就会执行持久化操作save 60 10000  #如果00s(1min)内,至少有10000个key进行修改,就会执行持久化操作stop-writes-on-bgsave-error yes #如果持久化出错,是否还需要继续工作rdbcompression yes #是否压缩rdb文件,会消耗一些CPU资源rdbchecksum yes #保存rdb文件时,进行错误的检查校验dbfilename dump.rdb #保存rdb文件的名称dir ./ #保存Rdb文件的路径

REPLICATION 复制 (主从复制)

image-20210701100141017

slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay norepl-backlog-size 1mbrepl-backlog-ttl 3600slave-priority 100

SECURITY 安全相关配置

image-20210701100404362

requirepass foobared #设置redis服务器的密码,默认没有密码.#一般通过命令行的方式进行设置密码127.0.0.1:6379> config get requirepass #获取密码1) "requirepass"2) ""127.0.0.1:6379> config set requirepass "123456" #设置密码OK127.0.0.1:6379> config get requirepass #访问被拒绝需要权限(error) NOAUTH Authentication required.127.0.0.1:6379> auth 123456  #验证密码权限OK127.0.0.1:6379> config get requirepass1) "requirepass"2) "123456"

LIMITS 客户端限制

image-20210701100452357

maxclients 10000 #设置redis客户端最大的连接数maxmemory <bytes> #redis配置最大的内存容量maxmemory-policy noeviction #内存达到上限之后的处理策略    #volatile-lru:只对设置了过期时间的key进行LRU(默认值)     #allkeys-lru : 删除lru算法的key       #volatile-random:随机删除即将过期key       #allkeys-random:随机删除       #volatile-ttl : 删除即将过期的       #noeviction : 永不过期,返回错误

APPEND ONLY MODE 持久化AOF模式配置

image-20210701100532486

appendonly no #默认不开启AOF模式,默认使用RDB模式持久化appendfilename "appendonly.aof" #AOF持久化的文件名称# appendfsync always #每次修改都会同步,消耗性能appendfsync everysec #每秒执行一次sync,可能会丢失1s的数据# appendfsync no #不执行syncno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yes

8. 持久化

Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化功能

1. RDB持久化

什么是RDB

RDB其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

如何恢复RDB文件

  1. 只需要将rdb文件放在我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复其中的数据!

  2. 查看rdb需要存放的位置

    127.0.0.1:6379> config get dir1) "dir"2) "D:\\Program Files\\Java\\Redis-x64-5.0.10"
    

RDB 的优势和劣势

优势

  • RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

劣势

  • RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

2. AOF持久化

以日志的形式来记录每个写操作,将Redis执行过的所有指令都记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之后会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作.

如果这个aof文件有错误,此时redis不能启动,因此需要修复这个aof文件,redis提供了这么一个工具 redis-check-aof

 redis-check-aof --fix appendonly.aof

9.订阅/消费模式

10.Redis主从复制

和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步

127.0.0.1:6379> info replication #查看主从复制配置信息# Replicationrole:masterconnected_slaves:0master_replid:19228b678f07b689550d9bdfc4fcc68464d912dfmaster_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0

一主二从

默认情况下,每台redis服务器都是主节点,一般情况下只需要配置从机即可.

#Slaveof host port

11. 哨兵模式(一主二从三哨兵)

12. 缓存击穿及雪崩

原文地址:https://www.cnblogs.com/seanRay/p/15006318.html