Redis
第一部分 简介
1.nosql 数据库
2.redis有16个数据库,默认使用0号数据库
3.redis是单线程+多路IO复用,支持持久化,以及支持多种数据类型
4.memcached是多线程+锁,支持单一数据类型
5.redis有五种数据类型
6.redis基于key-value存储
第二部分 Redis运行基础
1. 安装
2. Redis基础命令
在linux安装成功后,/usr/local/bin 会出现以下命令
redis-benchmark 性能测试工具,可以运行测试一下自己笔记本的性能
redis-check-aof 修复有问题的aof文件
redis-check-rdb 修复有问题的rdb文件
redis-sentinel redis集群使用
redis-server 启动redis服务
redis-cli 进入客户端
3. Redis后台服务
redis一般使用后台服务启动所以要修改配置文件
配置文件在 /opt/redisxxxx/ 下 redis.conf
一般情况下会复制一份到etc下 然后修改etc下的配置文件
cp redis.conf /etc/redis.conf
vim /etc/redis.conf
搜索daemonize no 将no改为yes
4. 启动
cd /usr/local/bin
redis-server /etc/redis.conf
可通过ps -ef | grep redis 查看进程
5. 客户端连接
redis-cli -h localhost -p 6379
如果连接不上,或其他问题看看防火墙是否关闭
(本机连接不上)
检查防火墙状态
systemctl status firewalld
systemctl stop firewalld
6. 关闭
1.直接在里面shutdown
2.kill -9 uid
3.先exit推出redis 然后 redis-cli shutdown
第三部分 Redis数据类型及语法
key键(非数据类型)
因为redis基于key-value存储所以对key的操作至关重要
1.keys *
查看本库所有key
keys *
1) "myzset"
2) "String"
3) "mylist"
4) "myhash"
5) "myset"
2.exists key
查看是否存在该key
存在返回(integer)1
没有返回(integer)0
exists myset
(integer) 1
3.type key
获取key类型
type mylist
list
4.del key
删除key,直接删除
5.unlink key
删除key,不直接删除
直接返回删除成功,后续通过异步操作完成删除
6.expire key 10
为已存在的key设置有效时间“秒”
时间结束后key会删除
expire time 100
(integer) 1
7.ttl key
查看key的有效时间
(integer)-1 永久有效
(integer)-2 已过期
(integer)x 还有x秒过期
ttl time
(integer) 74
ttl myset
(integer) -1
ttl no
(integer) -2
8.flushdb
清除库
9.flushall
清除全部库
String
String是Redis最基本的数据类型,一个key对应一个value
value最大512M。
String是二进制安全的,也就是说可以存放,图片二进制,以及序列化的对象
String的底层是一个简单的动态字符串,类似java的ArrayList,通过预分配空间的方式节省空间。当超过1M是每次最多增加1M
1.添加key
1.set key value
创建一个String类型的数据
多次添加同一个key的值,会覆盖前value
set String stringvalue
OK
2.mset key1 value1 key2 value2 …
一次创建多个key-value
mset string1 value1 string2 value2
OK
3.setnx key value
和set不同的是,如果该key已经存在,那就不会在设置值
setnx string1 value1
(integer) 0
setnx string3 value3
(integer) 1
4.msetnx key1 value1 key2 value2 …
一次添加多个key-value ,重复则不再设置
5.setex key x value
添加一个有期限的key-value ,如果key存在,直接覆盖
2.获取value
1.get key
获取对应的value
get string1
"value1"
2.mget key1 key2
一次获取多个value
mget string1 string2
1) "value1"
2) "value2"
3.getrange key star end
例:getrange k1 0 2
获取指定value的指定长度,角标从0开始
getrange string1 0 3
"valu"
3.修改value
1.appen key value
追加
对当前key的value进行追加,返回追加后的长度
127.0.0.1:6379> get string1
"value1"
127.0.0.1:6379> append string1 123
(integer) 9
127.0.0.1:6379> get string1
"value1123"
2.incr key
对数字类型的value进行+1
如果该key不存在那么,会自动创建key并赋值为1
127.0.0.1:6379> set number 100
OK
127.0.0.1:6379> get number
"100"
127.0.0.1:6379> incr number
(integer) 101
3.incrby key 步长
对数字类型的value进行+步长操作
127.0.0.1:6379> set number 101
OK
127.0.0.1:6379> get number
"101"
127.0.0.1:6379> incrby number 100
(integer) 201
4.decr key
对数字类型的value-1
127.0.0.1:6379> get number
"201"
127.0.0.1:6379> decr number
(integer) 200
5.decrby key 步长
对数字类型的value进行-步长操作
6.setrange key star value
覆盖指定value指定位置以及之后n位的值,索引从0开始
127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379> setrange name 2 mark
(integer) 6
127.0.0.1:6379> get name
"jamark"
7.getset key value
以新值换旧值
返回指定key原来的value,将现在的value赋值给key
127.0.0.1:6379> get string1
"value1123"
127.0.0.1:6379> getset string1 change
"value1123"
127.0.0.1:6379> get string1
"change"
4.strlen key
返回当前key的长度
List
list是一个双向链表,所以在查询是效率很高,并且有序(放入顺序和取出顺序一样)。
1.添加key
lpush/rpush key value1 value2 …
创建list
也可以用来添加value,在该list左/右边添加一个value
添加方向不同,初始value的顺序也会不同
127.0.0.1:6379> lpush list1 value1 value2 value3 value4
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value1"
127.0.0.1:6379> rpush list2 value1 value2 value3 value4
(integer) 4
127.0.0.1:6379> lrange list2 0 -1
1) "value1"
2) "value2"
3) "value3"
4) "value4"
2.获取value
1.lrange key star end
获取指定key指定位置的value
0 表示左边第一个元素
-1表示右边第一个元素
所以0 -1是所有元素
127.0.0.1:6379> lrange list2 0 -1
1) "value1"
2) "value2"
3) "value3"
4) "value4"
2.lindex k1 x
获取指定key指定位置的value
角标从0开始
127.0.0.1:6379> lindex list2 2
"value3"
3.修改value
1.lpop/rpop
取出左/右第一个value,相当于删除
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value1"
127.0.0.1:6379> lpop list1
"value4"
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value1"
2.linsert key before/after value newvalue
在指定value前/后插入newvalue
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> linsert list1 after value2 value0.5
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value0.5"
4) "value1"
3.lrem key x value
从左向右删除x个value值 0表示删除全部
127.0.0.1:6379> lrange list1 0 -1
1) "value0.5"
2) "value2"
3) "value3"
4) "value2"
5) "value0.5"
6) "value1"
127.0.0.1:6379> lrem list1 1 value2
(integer) 1
127.0.0.1:6379> lrem list1 0 value0.5
(integer) 2
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value1"
4.rpoplpush key1 key2
取出key1右边第一个值放到key2左边
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> lrange list2 0 -1
1) "value1"
2) "value2"
3) "value3"
4) "value4"
127.0.0.1:6379> rpoplpush list2 list1
"value4"
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value1"
//一番操作将list2右边第一个取出放到list1左边
llen key
获取指定key的value长度
Set
set集合,是一个无序集合(放入顺序和取出顺序不一样)且元素不重复,放入元素后它会根据hashcode进行元素排序。
底层是一个Hash表,key放元素,value为null,所以元素不能重复
1.添加key
sadd key value1 value2 …
创建/添加集合元素
127.0.0.1:6379> sadd set1 value1 value2 value3 //创建
(integer) 3
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> sadd set1 value4 //添加
(integer) 1
127.0.0.1:6379> smembers set1
1) "value3"
2) "value4"
3) "value2"
4) "value1"
2.获取value
smembers key
获取指定集合的全部元素
127.0.0.1:6379> sadd set1 value1 value2 value3
(integer) 3
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
srandmember key n
从指定key中随机获取n个value不删除
127.0.0.1:6379> srandmember set1
"value3"
127.0.0.1:6379> srandmember set1
"value4"
127.0.0.1:6379> srandmember set1
"value3"
3.修改value
1.srem key value …
删除指定key的指定value,可以一次删除多个元素
127.0.0.1:6379> smembers set1
1) "value3"
2) "value4"
3) "value2"
4) "value1"
127.0.0.1:6379> srem set1 value1
(integer) 1
127.0.0.1:6379> smembers set1
1) "value3"
2) "value4"
3) "value2"
2.spop key
从指定key中随机吐出一个value(删除)
127.0.0.1:6379> smembers set1
1) "value3"
2) "value4"
3) "value2"
127.0.0.1:6379> spop set1
"value2"
127.0.0.1:6379> spop set1
"value4"
127.0.0.1:6379> smembers set1
1) "value3"
4.多集合操作
1.smove key1 key2 value
将key1中的value移到key2中
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> smembers set2
1) "value3"
2) "value5"
3) "value4"
127.0.0.1:6379> smove set1 set2 value1 //移动
(integer) 1
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
127.0.0.1:6379> smembers set2
1) "value3"
2) "value5"
3) "value1"
4) "value4"
2.sinter key1 key2
取两集合的交集
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> smembers set2
1) "value3"
2) "value5"
3) "value4"
127.0.0.1:6379> sinter set1 set2
1) "value3"
3.sunion key1 key2
取两集合的并集
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> smembers set2
1) "value3"
2) "value5"
3) "value4"
127.0.0.1:6379> sunion set1 set2
1) "value3"
2) "value5"
3) "value1"
4) "value2"
5) "value4"
4.sdiff key1 key2
取两集合的差集
key1中有,key2中没有
127.0.0.1:6379> smembers set1
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> smembers set2
1) "value3"
2) "value5"
3) "value4"
127.0.0.1:6379> sdiff set1 set2
1) "value1"
2) "value2"
5.sismember key value
判断是否含有某元素
含有返回(integer)1
不包含返回(integer)0
127.0.0.1:6379> sismember set1 value1
(integer) 0
127.0.0.1:6379> sismember set1 value2
(integer) 1
6.scard key
返回指定集合的长度
Hash
hash的存储非常特殊,如图所示:
所以这种类型非常适合存储对象
Hsah底层数据少时是ziplist(压缩列表)数据多时是hashtable
判断条件为
- 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)
- 所有值都小于hash-max-ziplist-value配置(默认64字节
1.添加key
1.hset key field1 value1 field2 value2…
可以说是存储一个对象,多添加表示覆盖
127.0.0.1:6379> hset user1 id 1 name lll age 10
(integer) 3
127.0.0.1:6379> hgetall user1
1) "id"
2) "1"
3) "name"
4) "lll"
5) "age"
6) "10"
2.mhset key field1 value1 field2 value2…
可以一次存储多个hash,但是一般就使用hset就行了
3.hsetnx key field value
设置一个非存在字段
127.0.0.1:6379> hgetall user1
1) "id"
2) "1"
3) "name"
4) "lll"
5) "age"
6) "10"
127.0.0.1:6379> hsetnx user1 name ggg //存在存储失败
(integer) 0
127.0.0.1:6379> hsetnx user1 address m //不存在设置成功
(integer) 1
2.获取value
1.hget key field
获取指定field的value
127.0.0.1:6379> hget user1 name
"lll"
127.0.0.1:6379> hget user1 id
"1"
2.hmget key field1 field2…
一次获取多个value
127.0.0.1:6379> hmget user1 id name age address
1) "1"
2) "lll"
3) "10"
4) "m"
3.hvals key
获取指定key的所有value
127.0.0.1:6379> hvals user1
1) "1"
2) "lll"
3) "10"
4) "m"
4.hgetall key
获取指定key所有的 field 和value
127.0.0.1:6379> hgetall user1
1) "id"
2) "1"
3) "name"
4) "lll"
5) "age"
6) "10"
7) "address"
8) "m"
3.修改value
1.hdel key field
删除指定key的field
127.0.0.1:6379> hkeys user1
1) "id"
2) "name"
3) "age"
4) "address"
127.0.0.1:6379> hdel user1 address
(integer) 1
127.0.0.1:6379> hkeys user1
1) "id"
2) "name"
3) "age"
2.hincrby key field x
为指定field +x 命令带by,就表示带步长
127.0.0.1:6379> hget user1 age
"10"
127.0.0.1:6379> hincrby user1 age 10 //操作修改
(integer) 20
127.0.0.1:6379> hget user1 age
"20"
3.hset key field value
重新设置覆盖,也可以添加
127.0.0.1:6379> hgetall user1
1) "id"
2) "1"
3) "name"
4) "lll"
5) "age"
6) "20"
127.0.0.1:6379> hset user1 age 10 //操作修改
(integer) 0
127.0.0.1:6379> hgetall user1
1) "id"
2) "1"
3) "name"
4) "lll"
5) "age"
6) "10"
4.hexists key field
判读是否存在某个字段
127.0.0.1:6379> hkeys user1
1) "id"
2) "name"
3) "age"
127.0.0.1:6379> hexists user1 id
(integer) 1
127.0.0.1:6379> hexists user1 address
(integer) 0
5.hkeys key
获取指定key的所有字段
127.0.0.1:6379> hkeys user1
1) "id"
2) "name"
3) "age"
6.hlen key
获取指定key的字段长度
7.hstrlen key field
获取指定key指定field的value长度
127.0.0.1:6379> hkeys user1
1) "id"
2) "name"
3) "age"
127.0.0.1:6379> hstrlen user name
(integer) 4
Zset
zset和set特性基本一致,不过他是个有序集合。
底层也是hash 一个key对应多个value
一个value分为 score value
该集合会根据score将各个元素进行从大到小的排序
此外在hash的基础上添加跳跃表
1.添加key
1.zadd key score1 value1 score2 value2 …
添加/创建一个Zset集合
127.0.0.1:6379> zadd language 1 c 5 c++ 10 java 20 php 100 python
(integer) 5
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
2.获取value
1.zrange key star end
通过索引值获取value
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
2.zrevrange key star end
通过倒序索引获取value
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
127.0.0.1:6379> zrevrange language 0 -1 //倒序查询
1) "python"
2) "php"
3) "java"
4) "c++"
5) "c"
3.zrangebyscore key star end
通过score获取指定区间的value
127.0.0.1:6379> zrangebyscore language 0 10
1) "c"
2) "c++"
3) "java"
127.0.0.1:6379> zrangebyscore language 0 100
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
127.0.0.1:6379> zrangebyscore language (1 100 //做不包含写法
1) "c++"
2) "java"
3) "php"
4) "python"
3.删除value
1.zrem key value
删除删除指定value
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
127.0.0.1:6379> zrem language java //删除
(integer) 1
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "php"
4) "python"
2.zremrangebyscore key star end
通过score删除指定区间内的value
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "php"
4) "python"
127.0.0.1:6379> zremrangebyscore language 5 50 //删除执行
(integer) 2
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "python"
3.zremrangebyrank key star end
通过索引删除指定区间内的value
127.0.0.1:6379> zrange language 0 -1
1) "c"
2) "c++"
3) "java"
4) "php"
5) "python"
127.0.0.1:6379> zremrangebyrank language 0 3 //删除
(integer) 4
127.0.0.1:6379> zrange language 0 -1
1) "python"
4.获取score
1.zscore key value
通过value获取对应的score
127.0.0.1:6379> zscore language python
"100"
2.zincrby key x value
增减指定value的score
127.0.0.1:6379> zscore language python
"100"
127.0.0.1:6379> zincrby language 100 python //执行操作
"200"
127.0.0.1:6379> zscore language python
"200"
5.获取索引/数量
1.zrank key value
通过value获取该value的索引
127.0.0.1:6379> zrange language 0 -1
1) "c++"
2) "java"
3) "php"
4) "python"
127.0.0.1:6379> zrank language java
(integer) 1
2.zrevrank key value
获取指定value在集合中倒序索引
127.0.0.1:6379> zrange language 0 -1
1) "c++"
2) "java"
3) "php"
4) "python"
127.0.0.1:6379> zrank language java //正序
(integer) 1
127.0.0.1:6379> zrevrank language java //倒序
(integer) 2
3.zcount key score_star score_end
通过score获取指定区间的value个数
127.0.0.1:6379> zcount language 5 50
(integer) 3
4.zcard key
获取该集合中的元素个数
127.0.0.1:6379> zcard language
(integer) 4
127.0.0.1:6379> zrange language 0 -1
1) "c++"
2) "java"
3) "php"
4) "python"
Bitmaps
bitmaps是一个可以对位操作的数据类型,底层是一个字符串,不过他有一套特定的命令用于操作。
只能0/1存储。这种方式非常适合存放,比如,用户是否上线等数据。长度很长。
1.setbit key index 0/1
添加/创建key
index 表示偏移量 就是在value中的位置
127.0.0.1:6379> setbit user3 2 1
(integer) 0
127.0.0.1:6379> setbit user3 10 1
(integer) 0
1.getbit key index
获取指定偏移位上的0/1
127.0.0.1:6379> getbit user3 1
(integer) 0
127.0.0.1:6379> getbit user3 2
(integer) 1
3.bitcount key star end
0 -1 代表所有 分区按照字节来分,也就是一个区8位
0 0 表示 1-8位中有几个1
0 -1 表示全部所有有几个1
127.0.0.1:6379> bitcount user3 0 -1
(integer) 2
127.0.0.1:6379> bitcount user3 0 0
(integer) 1
HyperLolgLog
主要用于计数,没别的用处元素不重复
1.pfadd key value1 value2…
127.0.0.1:6379> pfadd program java c c++
(integer) 1
127.0.0.1:6379> pfadd program python mysql
(integer) 1
127.0.0.1:6379> pfadd program java //重复添加失败
(integer) 0
2.pfcount key
统计个数
127.0.0.1:6379> pfcount program
(integer) 5
3.pfmerge key/newkey key1 key2
合并两个key 可以合并到已存在的key,也可以合并到newkey
127.0.0.1:6379> pfcount language
(integer) 3
127.0.0.1:6379> pfcount program
(integer) 5
127.0.0.1:6379> pfmerge program language //合并
OK
127.0.0.1:6379> pfcount program
(integer) 8
Geospatial
用来存储地理位置
底层是Zset,所以类型就是Zset
key对应一组城市
value包含两个字段 维度(经度纬度) 城市名
1.geoadd key 经度 纬度 name
添加/创建key 可以同时添加多个
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen
(integer) 2
2.geopos key name
通过name获取指定的经度纬度
只能获取已有的
127.0.0.1:6379> geopos china:city zhengzhou //不存在的城市
1) (nil)
127.0.0.1:6379> geopos china:city shanghai
1) 1) "121.47000163793563843"
2) "31.22999903975783553"
127.0.0.1:6379> geopos china:city shanghai shenzhen
1) 1) "121.47000163793563843"
2) "31.22999903975783553"
2) 1) "114.04999762773513794"
2) "22.5200000879503861"
3.geodist key name1 name2 company
通过经纬度计算两地的直线距离
单位有
m km fm(英尺) mi(英里)
127.0.0.1:6379> geodist china:city shanghai shenzhen m
"1215922.3698"
127.0.0.1:6379> geodist china:city shanghai shenzhen km
"1215.9224"
4.georaduis key 经度 维度 raduis conpany
定点搜索周围城市
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqing"
2) "shenzhen"
第四部分 Redis配置文件
1. NETWORK 网络配置
重要
1.bind 127.0.0.1 -::1 绑定本机;表示只能本地访问;一般注掉
2.protected-mode yes 保护模式;只能本机访问;一般改为no
3.port 6379 端口号
4.tcp-backlog 511 tcp链接队列
5.timeout 0 连接超时时间,0代表永不超时。
6.tcp-keepalive 300 心跳检测时间
2. GENERAL 常见配置
重要
1.daemonize yes 是否开启 后台启动服务
2.pidfile /var/run/redis_6379.pid 进程号
3.loglevel notice 日志级别
4.logfile “” 日志文件位置,默认不保存
5.databases 16 默认数据库个数
3. SNAPSHOTTING 持久化RDB
重要
1.stop-writes-on-bgsave-error yes 当磁盘无法写入时是否停止redis写操作
2.dbfilename dump.rdb 默认持久化文件名字
3.dir ./ 持久化存储的路径 ./ 代表当前路径,也就是启动目录
4.rdbcompression yes 持久化文件是否需要压缩,redis使用L2F算法
5.rdbchecksum yes 持久化完整性检查;持久化前redis会使用CRC64算法进行校验,会有10%的性能消耗
6.save 保存频率,手动进行(一般不用);save 300 100 300s检测一下如果有100个key变化那就持久化
4. APPEND ONLY MODE 持久化AOF
重要
1.appendonly no 是否开启aof持久化模式
2.appendfilename “appendonly.aof” 持久化文件名
3.appendfsync always/everysecno 同步日志的频率 时刻同步/每秒同步/让操作系统决定同步(跟自己的情况而定)
4.no-appendfsync-on-rewrite no 重写时是否要使用 appendfsync
5.auto-aof-rewrite-percentage 100 设置重写时文件大小 阈值
6.auto-aof-rewrite-min-size 64mb 设置重写最小文件的大小
5. REDIS CLUSTER 集群
1.cluster-enabled yes 开启集群,默认是注掉的
2.cluster-config-file nodes-6379.conf 集群节点名称
3.cluster-node-timeout 15000 连接超时时间
10. 配置模块概述
-
REPLICATION 复制配置
-
SECURITY 安全设置
可以设置链接密码啥的 默认没有设置
-
CLIENTS 用户连接数
maxclients 10000 默认10000
-
MEMORY MANAGEMENT 内存管理
-
LAZY FREEING 文件释放配置
-
KERNEL transparent hugepage CONTROL 内核控件
-
APPEND ONLY MODE 附加模块
。。。。。。
-
LUA SCRIPTING 反入侵脚本
有点高级!不了解
-
SLOW LOG 慢日志
应该是一些关于日志的配置
-
EVENT NOTIFICATION 事件通知
没看懂
-
ADVANCED CONFIG 高级配置
可配置数据类型的最大拆分值
不如hash在ziplist和hashtable中切换的条件
第五部分 Redis集群模式
redis集群模式一共有三种
- 主从复制(集群基础)
- 哨兵模式
- 集群
作用就是读写分离,提高服务器性能,增加缓存容错
1. 主从复制
最简单的集群模式,这是redis集群的基础
实现方式就是使用数据同步技术,同步主从之间的数据
redis采用乐观复制策略,允许短时间内主-从数据内容不同,当然最后还是相同的。
特点
- 读写分离(优点)
- 从机不能写(缺点)
- 主机宕机,集群不能写(缺点)
实现步骤
- 修改主机配置文件
- 修改从机配置文件
- 测试
① 修改主机配置文件
复制一份配置文件
修改:
:69 bind 192.168.170.31 绑定访问IP,就是本机ip
:92 port 6379 例端口号
:136 daemonize yes 守护方式运行
② 修改从机配置文件
复制配置文件
修改:
:69 bind 192.168.170.32 绑定IP
:92 port 6379 实例端口号
:136 daemonize yes 守护方式运行
:G slaveof 192.168.170.31 6666 主机ip以及端口,指定自己是谁的从机
③ 测试
启动测试
/usr/redis/bin/redis-server /usr/redis/bin/master.conf // 主机启动
/usr/redis/bin/redis-server /usr/redis/bin/slave1.conf // 从机启动
可以使用info来查看,集群状态
④ 原理
2. 哨兵模式
哨兵模式是主从复制的升级版,用来解决主从复制的痛点
概念
哨兵对Redis集群进行监控,当主机宕机后,根据一些配置将从机却换为主机,保证集群正常运行
特点
- 防止单点故障(优点)
- 读写分离(优点/缺点)
- 主机宕机后,会自动补救(优点)
- 宕机时(系统不能写)
实现
- 修改Redis哨兵配置文件
- 以哨兵模式启动服务
① 哨兵模式实现
修改配置文件
sentinel.conf
修改:
:17 protected-mode no 关闭本机保护
:21 port 26379 哨兵服务端口,一般不改
:26 daemonize yes 后台运行
:36 logfile "./temp.log" 日志文件位置
:84 sentinel monitor mymaster 192.168.170.31 6666 2 要监视的主机ip、端口以及集群哨兵总数/2
:103 sentinel auth-pass mymaster 123456 校验密码
:113 sentinel down-after-milliseconds mymaster 30000 主机宕机最长时长,一般不改
② 启动
./redis-sentinel sentinel.conf
③ 原理
3. 集群
将多台redis相互连接,并按一定规则将其分为主从关系,数据互通。
特点
- 高可用,防止单点故障,去中心化
- 高性能,集群后,集群中每个一节点和单机版能力同级的(可读可写)
- 扩展方便,按需动态增加或者减少集群中节点的数量
- 解决master写压力,每个master都可以读写
实现
- 修改配置文件
- 启动所有节点
- 使用集群命令连接节点
- 连接集群
① 修改配置文件
每一个节点的配置文件都要修改,以一份为例
:69 bind 本机ip
:92 port 端口
:136 daemonize yes 后端运行
:158 pidfile /var/run/redis_端口.pid 进程id
:699 appendonly yes aop 日志开启
:832 cluster-enabled yes 开启集群(重要)
:cluster-config-file nodes_端口.conf 集群配置文件
:cluster-node-timeout 15000 节点连接超时时长,超过此时长表示宕机
② 启动节点
./redis-serve redis.conf
...
③ 串联所有节点
只用在第一次串联集群时执行
/usr/redis/bin/redis-cli --cluster create --cluster-replicas 1 192.168.37.100:8001 192.168.37.100:8002 192.168.37.101:8003 192.168.37.101:8004 192.168.37.102:8005 192.168.37.102:8006
④ 连接集群
一定要携带 -c
不然不是集群状态
/usr/redis/bin/redis-cli -c -h 192.168.37.100 -p 8001
获取集群中所有的key
⑤ 集群常用命令
- redis-cli --cluster create --cluster-replicas 1 ip… 设置集群
- cluster nodes 获取节点详情
- cluster meet 将指定redis服务 添加到集群
- cluster forget <node_id> 将指定节点移除集群
第六部分 Redis消息订阅
1. 订阅
① subscribe channel1 channel2…
订阅频道,一次性可以订阅多个
127.0.0.1:6379> subscribe channel1 channel2 //订阅多个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "subscribe"
2) "channel2"
3) (integer) 2
② psubscribe channel1 channel2
订阅频道,支持通配符
③ unsubscribe channel1 …
取消订阅,不指定频道视为全部取消
④ punsubscribe channel1 …
取消订阅,支持通配符
2. 发布
2.publish channel message
127.0.0.1:6379> publish channel1 hello
(integer) 1
第七部分 Redis事务
1. 特点
事务就是串一些命令,防止他人插队
- redis的事务是一个单独的隔离的空间
- redis事务没有隔离级别
- redis事务没有原子性,也就是有命令执行错误,不会发生回滚
- redis使用乐观锁
事务中的命令会序列化,然后按顺序执行
事务分三步 1.组队命令 2.编译命令 3.执行命令
2. 事务操作
① multi
开启事务
开启事务之后可以开始添加命令
127.0.0.1:6379> multi //开启事务
OK
127.0.0.1:6379(TX)> set k1 llz
QUEUED //命令添加成功
127.0.0.1:6379(TX)> set k2 mmm
QUEUED
127.0.0.1:6379(TX)>
② exec
编译命令
之后立刻执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 llz
QUEUED
127.0.0.1:6379(TX)> set k2 mmm
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
③ discard
中断命令组队
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k5 va
QUEUED
127.0.0.1:6379(TX)> set k6 ga
QUEUED
127.0.0.1:6379(TX)> discard //中断
OK
127.0.0.1:6379>
④ watch
监视属性,就相当于给key上锁
当他发生改变时,执行他的命令会失败
用户1
127.0.0.1:6379> watch k1 //监视k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby k1 100 //改变k1
QUEUED
127.0.0.1:6379(TX)> exec //执行操作 k1增加
1) (integer) 200
127.0.0.1:6379>
用户2
127.0.0.1:6379> watch k1 //监视k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby k1 100
QUEUED
127.0.0.1:6379(TX)> exec //因为k1已经改变所以执行错误
(nil)
127.0.0.1:6379>
⑤ 错误提醒
- 在multi阶段出现代码错误,则整个事务代码作废
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 //错误代码
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec //强制之行全错
(error) EXECABORT Transaction discarded because of previous errors.
- 在exec阶段存在错误代码,则之影响一行代码
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> incr k1 //k1自增 明显不能自增,但是加入成功
QUEUED
127.0.0.1:6379(TX)> exec
1) OK //第一行执行成功
2) (error) ERR value is not an integer or out of range //执行失败
3. 事务锁
① 悲观锁
悲观锁就是操作一次加个锁,操作结束在把锁去掉
效率低
② 乐观锁
乐观锁就是加版本号,执行命令前先检查一下版本号是否匹配
非常适合多读的数据
第八部分 Redis持久化
1. RDB
rdb 写时拷贝技术
流程如下:
rdb持久化不会直接将内容写入dump文件
而是开一个子进程,子进程会写一个临时文件,完成之后在同步到dump文件
优点
- 适合大规模数据恢复
- 节省磁盘空间
- 数据恢复速度快
缺点
- 因为要写临时文件,所以会占用双倍空间
- rdb最后一次更新的数据可能会丢失
配置文件
SNAPSHOTTING
1.stop-writes-on-bgsave-error yes 当磁盘无法写入时是否停止redis写操作
2.dbfilename dump.rdb 默认持久化文件名字
3.dir ./ 持久化存储的路径 ./ 代表当前路径,也就是启动目录
4.rdbcompression yes 持久化文件是否需要压缩,redis使用L2F算法
5.rdbchecksum yes 持久化完整性检查;持久化前redis会使用CRC64算法进行校验,会有10%的性能消耗
6.save 保存频率,手动进行(一般不用);save 300 100 300s检测一下如果有100个key变化那就持久化
2. AOF
aof 会以追加的形式将指令集添加到appendonly.aof文件
当appendonly.aof文件过大时redis会启动重写
会将文件指令进行压缩
例如:
set k1 v1
set k2 v2
=> set k1 v1 k2 v2 //指令集
重写阈值由自己设置
注:重写触发时,redis会创建一个子进程完成该工作,类似rdb过程
appenonly.aof文件修复
如果appendonly.aof文件不小心损坏可以修复(断电,磁盘满了)
使用
redis-check-aof --fix appendonly.aof
并且redis会给出比较结果
配置文件
APPEND ONLY MODE
1.appendonly no 是否开启aof持久化模式
2.appendfilename “appendonly.aof” 持久化文件名
3.appendfsync always/everysecno 同步日志的频率 时刻同步/每秒同步/让操作系统决定同步(跟自己的情况而定)
4.no-appendfsync-on-rewrite no 重写时是否要使用 appendfsync
5.auto-aof-rewrite-percentage 100 设置重写时文件大小 阈值
6.auto-aof-rewrite-min-size 64mb 设置重写最小文件的大小
3. 持久化总结
- 推荐两种一起用,两个都开启默认使用appendonly
- 对数据不敏感,推荐RDB
- 不建议单独用AOF,有bug
- 只做缓存都不用
第十部分 Redis 失效问题
1. 缓存穿透
什么是缓存穿透?
缓存穿透是指,缓存失去意义,甚至不如不加
**场景:**大量请求访问一个redis不存在,数据库也不存在的key
例如:
- 不停查询一个不存在的数据,redis缓存未命中,也不能缓存数据,导致一直访问持久层,缓存失去意义
解决办法
- 缓存空对象 key null ,会占用很多空间,一般会设置过期值
- 运用bitmap布隆缓存拦截器,将存在的key保存到bitmap中。访问时先判断key是否存在,存在再放过去
2. 缓存击穿
什么是缓存击穿?
某一key失效的瞬间,后端启用大量线程来补救,导致后端负载过大,甚至导致服务器崩坏
**场景:**大量请求访问一个缓存不存在,数据库存在的key
例如:
-
双11购物,瞬间高并发一个没有缓存的key,会导致后端启用多线程重建缓存
-
或者就是重建缓存
解决办法
- 加锁,单线程重构,其他线程等待(set n/x)
- 延长热点key的过期时间,甚至是不过期,属于是物理解决,具体情况比价复杂
3. 缓存雪崩
什么是缓存雪崩?
缓存层因为某些情况挂掉,或者大量key在短时间内过期,导致持久层压力剧增进而导致系统崩溃成为雪崩
**场景:**大量key同时失效
解决办法
- 设置多层缓存。比如nignx缓存,redis缓存,等
- 多节点缓存
- key的过期时间随机
第十一部分 Redis+springboot
1. 整合一(只操作String)
这个整合比较简单,不能缓存对象、不能使用注解缓存
步骤
- 导入依赖
- 配置yml文件
- 编写测试类
① 导入依赖
<!-- redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 链接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
② 配置yml文件
spring:
redis:
host: 192.168.37.100
port: 6379
# 使用的数据库
database: 0
# 链接超时时间
# connect-timeout:
lettuce:
pool:
# 最大连接数
max-active: 20
# 最大等待时间 负数为永久
max-wait: -1
# 最大空闲连接数
max-idle: 5
# 最小空闲连接数
min-idle: 0
#第二种redis连接方法
spring:
redis:
username: root
url: redis://@192.168.37.100:6379
③ 编写测试类
使用Spring提供的工具类StringRedisTemplate
@Autowired
private StringRedisTemplate redisTemplate;
@Test
@Rollback(false) // 防止测试数据回滚 配个Transactional使用
@Transactional
void stringTest() {
ValueOperations<String, String> opsForString = redisTemplate.opsForValue();
}
2. 整合二(缓存对象)
思路:
- 导入依赖
- 配置yml
- 配置自定义的RedisTemplate,CacheManager
- 编写demo 测试
① 导入依赖
//redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//链接池依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
//json格式依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.5</version>
</dependency>
② 配置
- 配置application.yml
spring:
redis:
host: localhost
port: 6379
# 使用的数据库
database: 0
# 链接超时时间
# connect-timeout:
lettuce:
pool:
# 最大连接数
max-active: 20
# 最大等待时间 负数为永久
max-wait: -1
# 最大空闲连接数
max-idle: 5
# 最小空闲连接数
min-idle: 0
③ 配置文件
package com.example.redisspring2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* reids配置类
*/
@Configuration
// 开启redis缓存
@EnableCaching
public class redisConfig extends CachingConfigurerSupport {
// 所有进行redis存储的对象以及字符串都要序列化。所以在这里我们自定义序列化规则,使得我们自定义的其他实体类不用去实现序列化接口
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
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);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
// 缓存管理器对我们缓存的数据进行一些设置,在使用注解缓存时,可以让spring帮我们实现一些缓存步骤
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间30秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(1800000))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
④ 使用StringRedisTemplate
注入使用RedisTemplate或StringRedisTemplate对象(第一个专门操作String)
一般情况下只操作StringRedisTemplate就行了
@Autowired
StringRedisTemplate stringRedisTemplate;
void example(){
//操作String类型 K-V
ValueOperations valueOperations = stringRedisTemplate.opsForValue();
//操作List类型 K-[v1,v2...] 有序列表(放入顺序和存放顺序一致)
ListOperations listOperations = stringRedisTemplate.opsForList();
//操作Set类型 k-[v1,v2,v3...] 无序列表且元素不重复(放入顺序和存放顺序不一致)
SetOperations setOperations = stringRedisTemplate.opsForSet();
//操作Hash类型 K-[f1,v1,f2,v2...] 适合对象存储
HashOperations hashOperations = stringRedisTemplate.opsForHash();
//操作Zset类型 K-[s1,v1,s2,v2...] 和list相似不过内部元素会根据s的值进行排序
ZSetOperations zSetOperations = stringRedisTemplate.opsForZSet();
//操作HyperLogLog类型 K-[v1,v2,v3...] 元素不重复,主要用于统计数量
HyperLogLogOperations hyperLogLogOperations = stringRedisTemplate.opsForHyperLogLog();
//操作Geospatial类型 K-[经度 维度 name...] 存放地理位置
GeoOperations geoOperations = stringRedisTemplate.opsForGeo();
}
3. 整合三(注解缓存)
思路:
- 导入依赖
- 配置yml
- 编写RedisConfig
- 编写测试demo
- 添加CacheManager
- 修改测试demo
① 导入依赖
<!-- spring boot redis缓存引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lecttuce 缓存连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
② 配置yml
spring:
redis:
host: 192.168.37.128
port: 6379
database: 0
#password: 123456 #默认为空
lettuce:
pool:
max-active: 20 #最大连接数,负值表示没有限制,默认8
max-wait: -1 #最大阻塞等待时间,负值表示没限制,默认-1
max-idle: 8 #最大空闲连接,默认8
min-idle: 0 #最小空闲连接,默认0
③ 编写RedisConfig
/**
* 我们自定义一个 RedisTemplate,设置序列化器,这样我们可以很方便的操作实例对象。
* 否则redis自动使用对象的jdk序列化
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化方式
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
④ 编写测试demo
public R getById(@PathVariable String id) {
Gson gson = new Gson();
// 当我们处理一个请求时 先去redis中获取一下请求数据是否存在
Serializable object = redisTemplate.opsForValue().get("ky1");
Ad ad = null;
// 没有的话就去数据库里找
if (ObjectUtil.isNull(object)){
ad = adService.getById(id);
// 将数据存入缓存
redisTemplate.opsForValue().set("ky1",gson.toJson(ad));
}
return R.ok().data("item", object);
}
⑤ 添加CacheManager
在第④步中。我们获取一个数据 先去缓存找,没有的话再去数据库,最后将数据存到缓存中。 这一步每一个需要缓存的数据请求几乎都要写,所以我们可以借助Spring切面编程
- Spring给我们提供了 面向切面请求缓存的接口,即CacheManager
- Redis实现了Spring提供的接口
- 我们要做的就是把Redis实现的类加入到Spring容器中
在RedisConfig中添加,在这里我们也可以进行一些其他统一设置
@Bean
public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
//过期时间600秒
.entryTtl(Duration.ofSeconds(600))
// 配置序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
return cacheManager;
}
另外还要为RedisConfig 添加注解 @EnableCaching,接下来就可以使用Spring提供的注解了
⑥ 修改测试demo
@Cacheable(value = "AdInfo")
public R getById(@PathVariable String id) {
Ad ad = adService.getById(id);
return R.ok().data("item", ad);
}
从以上代码我们可以看出,代码非常少,只有自己的核心业务代码,但是缓存的功能并没有少
@Cacheable(value = “AdInfo”)的意思:
- Cacheable表示 先去缓存找,没有的话再去数据库,最后将数据存到缓存中
- 属性value 表示 将数据存入Redis时 Key为 value值::参数值
例如:
“AdInfo::1194556896025845762”
“AdInfo::1194607458461216769”
非常灵活
4. StringRedisTemplate
StringRedisTemplate是Spring提供操作String的工具类
获取 各种数据类型存储String 工具类
//操作String类型 K-V
ValueOperations valueOperations = stringRedisTemplate.opsForValue();
//操作List类型 K-[v1,v2...] 有序列表(放入顺序和存放顺序一致)
ListOperations listOperations = stringRedisTemplate.opsForList();
//操作Set类型 k-[v1,v2,v3...] 无序列表且元素不重复(放入顺序和存放顺序不一致)
SetOperations setOperations = stringRedisTemplate.opsForSet();
//操作Hash类型 K-[f1,v1,f2,v2...] 适合对象存储
HashOperations hashOperations = stringRedisTemplate.opsForHash();
//操作Zset类型 K-[s1,v1,s2,v2...] 和list相似不过内部元素会根据s的值进行排序
ZSetOperations zSetOperations = stringRedisTemplate.opsForZSet();
//操作HyperLogLog类型 K-[v1,v2,v3...] 元素不重复,主要用于统计数量
HyperLogLogOperations hyperLogLogOperations = stringRedisTemplate.opsForHyperLogLog();
//操作Geospatial类型 K-[经度 维度 name...] 存放地理位置
GeoOperations geoOperations = stringRedisTemplate.opsForGeo();
// 操作Key ,所有的Operations都可以获取RedisOperations 可以使用操作key的命令
RedisOperations<String, String> operations = opsForString.getOperations();
① ValueOperations
redis的String类型存储String
ValueOperations<String, String> opsForString = redisTemplate.opsForValue();
// 获取指定key
opsForString.get("className");
// 设置一个key
opsForString.set("llz","llzaaa");
// 如果key存在则会修改值
opsForString.setIfPresent("desk1", "123");
// 如果key不存在则会创建成功
opsForString.setIfAbsent("desk1", "111");
// 批量添加字符串
Map<String,String> map = new HashMap<>();
map.put("aaa","111");
map.put("bbb","222");
map.put("ccc","333");
opsForString.multiSet(map);
// 批量获取value 不存在为null
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("nnn");
opsForString.multiGet(list);
// 自减 如果key不存在,则初始化为0,再-1
opsForString.decrement("aaa");
opsForString.decrement("aaa", 100L);
// 自增 如果key不存在,则初始化为0,再+1
opsForString.increment("eee");
opsForString.increment("ddd", 100L);
// 获取key的长度 不存在则值为0
opsForString.size("aaa");
opsForString.size("bbb");
opsForString.size("nnn");
// 在指定key后追加value
opsForString.append("aaa", "100");
② ListOperations
ListOperations<String, String> listOperations = redisTemplate.opsForList();
// 在列表右侧放入数据,返回列表元素个数
opsForList.rightPush("strList","aaa");
// 在列表左侧放入数据,返回列表元素个数
opsForList.leftPush("strList", "ccc");
// 一次存入多个元素
opsForList.leftPushAll("strList",ArrayList<>())
// 从左侧开始,获取指定角标的元素
opsForList.index("strList", 2);
// 从左侧开始,获取指定范围内的所有值(0 -1,为所有元素)
opsForList.range("strList",0,-1);
// 返回列表的长度
opsForList.size("strList");
// 对列表进行截取, -1 为最后一个元素,-2为倒数第二个元素
opsForList.trim("strList",1,-1);
// 移除并返回左侧第一个元素
opsForList.leftPop("strList");
// 设置指定索引位置的值
opsForList.set("strList",0,"一般玩");
③ Set
SetOperations<String, String> setOperations = redisTemplate.opsForSet();
// 添加元素,重复元素无法添加,返回添加成功的个数
setOperations.add("setList2", "aaa","111","222");
// 获取集合中所有的value
setOperations.members("setList");
// 判断指定元素是否在集合之中
setOperations.isMember("setList", "aaa");
// 返回集合并集
setOperations.union("setList", "setList2");
// 返回集合交集
setOperations.intersect("setList", "setList2");
// 返回集合一相较于集合二的差集
setOperations.difference("setList2", "setList");
// 移除一个或多个元素返回移除个数
setOperations.remove("setList", "aaa", "bbb");
// 随机获取并移除一个元素
setOperations.pop("setList");
④ Hash
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
// 放入/覆盖单个 k-v
hashOperations.put("strMap","key2","aaa");
// 通过k获取v
hashOperations.get("strMap", "key1");
// 批量放入v-k
hashOperations.put("strMap",Map);
// 批量获取v
hashOperations.multiGet("strMap",List)
// 是否存在指定k
hashOperations.hasKey("strMap", "key1");
// 返回map的长度
hashOperations.size("strMap");
// 删除一个或多个k 返回删除成功的个数
hashOperations.delete("strMap", "key1", "ccc");
// 获取所有的k
hashOperations.keys("strMap");
// 获取所有的v
hashOperations.values("strMap");
⑤ zSetOperations
ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
// 添加一个值,并指定他的分数值,集合按照分数值进行排序(如果分数值存在,则会放在其后)
zSetOperations.add("zSet", "zzzz", 10);
// 获取指定范围内的值,(0,-1为全部)
zSetOperations.range("zSet", 0, -1);
// 获取指定分数范围内的元素值
zSetOperations.rangeByScore("zSet", 2, 10);
// 倒叙获取指定角标区间内的元素(先倒叙,再按区间找元素)
zSetOperations.reverseRange("zSet", 0, 2);
// 倒叙获取指定分数区间内的元素
zSetOperations.reverseRangeByScore("zSet", 2, 10);
// 移除一个或多个指定元素,返回移除个数
zSetOperations.remove("zSet","zzzz");
// 移除指定角标范围内的元素
zSetOperations.removeRange("zSet",0,-1);
// 移除指定分数范围内的元素
zSetOperations.removeRangeByScore("zSet",0,100);
// 获取指定分数范围内元素的个数,闭区间
zSetOperations.count("zSet", 2, 10);
// 获取一个或多个元素的分数值
zSetOperations.score("zSet","zzz","a");
// 获取指定角标范围内的元素值与分数
zSetOperations.rangeWithScores("zSet", 0, 3);
// 获取集合长度
zSetOperations.zCard("zSet");
zSetOperations.size("zSet");
⑧ RedisOperations
用来执行redis中key的相关命令,比如:获取所有key,为key添加过期时间等等
可用通过以上7个类的 getPoerations()
获取对象
// 获取redisOperations 可以使用keys相关命令 以String为例
RedisOperations<String, String> operations = opsForString.getOperations();
// 获取所有key
operations.keys("*").forEach(System.out::println);
// 删除key
operations.delete("llz");
// 修改key名称
operations.rename("className","deskMate");
// 为key设置过期时间
operations.expire("deskMate",-1L,TimeUnit.SECONDS);
// 获取过期时间
operations.getExpire("desk2");
4. 连接redis集群
- 确保redis集群启动
- 配置yml
spring:
redis:
lettuce:
pool:
# 最大连接数
max-active: 20
# 最大等待时间 负数为永久
max-wait: -1
# 最大空闲连接数
max-idle: 5
# 最小空闲连接数
min-idle: 0
cluster:
nodes:
- 192.168.37.100:8001
- 192.168.37.100:8002
- 192.168.37.101:8003
- 192.168.37.101:8004
- 192.168.37.102:8005
- 192.168.37.102:8006
max-redirects: 3
其他语法使用不变
第十二部分 Redis+Mybatis-Plus缓存
开启mybatis缓存,以redis集群为数据存储处。
1. Mapper层缓存
也就是对Mapper中SQL语句查出的结果进行缓存,并不是对Service层的结果进行缓存
实现步骤
- 导入依赖
- 配置yml
- 编写MyBatis缓存配置文件
- 编写Redis配置文件
- 在需要缓存的Mapper.xml文件中开启缓存
- 测试
① 导入依赖
<!-- redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 链接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- json格式依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.5</version>
</dependency>
② 配置yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#如果驱动版本为8.0选择带‘cj’的名称 5.0不用
driver-class-name: com.mysql.cj.jdbc.Driver
#mysql是8.0版本需要带时区serverTimezone=GMT%2B8
url: jdbc:mysql://localhost:3306/test_local2?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
# redis集群配置
redis:
lettuce:
pool:
# 最大连接数
max-active: 20
# 最大等待时间 负数为永久
max-wait: -1
# 最大空闲连接数
max-idle: 5
# 最小空闲连接数
min-idle: 0
cluster:
nodes:
- 192.168.37.100:8001
- 192.168.37.100:8002
- 192.168.37.101:8003
- 192.168.37.101:8004
- 192.168.37.102:8005
- 192.168.37.102:8006
max-redirects: 3
#mybatis-plus配置
mybatis-plus:
configuration:
#日志配置,控制台可以输出sql语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
#mapper.xml映射文件路径 这是默认路径,可以不写
mapper-locations: classpath*:/mapper/**/*.xml
③ 编写MyBatis缓存配置文件
注意实现的 Cache
接口为Mybatis的,别写错了
import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.ValueOperations;
public class MybatisRedisCacheConfig implements Cache {
private String id;
// 对象不交给Spring管理,需要使用静态方法注入
private static ValueOperations<String, Object> valueOperations;
public static void setValueOperations(ValueOperations<String, Object> valueOperationsParam) {
valueOperations = valueOperationsParam;
}
public MybatisRedisCacheConfig(String id) {
if (StrUtil.isBlank(id)) {
throw new IllegalArgumentException("id不能为空");
}
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object o, Object o1) {
valueOperations.set(o.toString(), o1);
}
@Override
public Object getObject(Object o) {
return valueOperations.get(o.toString());
}
@Override
public Object removeObject(Object o) {
return valueOperations.getOperations().delete(o.toString());
}
@Override
public void clear() {
}
@Override
public int getSize() {
return 0;
}
}
④ 编写Redis配置文件
package com.example.security01.config.redis;
import com.example.security01.config.MybatisRedisCacheConfig;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
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);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
// 因为Myabtis的缓存配置文件不交给Spring管理,所以需要在这里手动复制
MybatisRedisCacheConfig.setValueOperations(redisTemplate.opsForValue());
return redisTemplate;
}
}
⑤ 在需要缓存的Mapper.xml文件中开启缓存
在需要缓存的Mapper文件中添加一下代码
<cache type="com.example.security01.config.MybatisRedisCacheConfig"/>
注意:MybatisPlus自带的方法不能缓存,必须是Mapper手写的查询语句