Redis最全教程
1. Redis装置
1.1 从官网下载安装包。
这里咱们以Redis5.x为例。下载好后应用xftp上传到你的服务器上或者你的虚拟机中。目录能够任意,但倡议把本人装置的软件放在/opt
文件夹下。
应用命令tar -zxvf redis-5.0.8.tar.gz
把下载的软件包解压,前面的要写你本人下载的包。
1.2装置Redis
进入到刚刚解压的Redis的文件夹。因为Redis是用c写的,所以要保障曾经装置gcc了。(注:最新版的6.x在应用make命令可能会报错,须要降级下gcc就好。)
在Redis的文件夹中应用命令进行装置。
make make PREFIX=/usr/local/redis install
第二行 命令是指定把软件装置的地位,如果不指定默认是装置在/usr/local/bin
目录中。
装置好之后咱们的Redis就装置在/usr/local/redis
这个文件夹下了。装置好后,会发现没有redis.conf这个Redis的配置文件,这个配置文件在解压的时候的那个目录,把它拷贝到装置的目录中。这样每次启动的时候给他指定配置文件就好,而自带的配置文件不去动它。如果前期配置错了能够把这个删除而后从新拷贝过去。你不做这步复制,redis也能失常启动,只是的会用一套默认配置。
Redis装置之后的bin目录次要有一下的几个性能:
1.3启动测试Redis
首先批改一下拷贝过去的redis.conf文件。把后盾启动关上,默认是前台启动。
启动的时候,指定应用配置文件:# ./bin/redis-server ./redis.conf
呈现如下的就阐明启动胜利,否则会报错。
咱们能够应用Redis自带的redis-cli去连贯Redis。应用命令redis-cli -p 6379
留神须要指定端口。
而后如ping,他会回一个pong,阐明搭建是胜利的。
1.4 敞开redis
暴力的形式是查问redis的过程号,而后应用kill命令。
正确的形式是,如果进入redis-cli能够如下操作:
如果没有进入,则间接应用命令敞开./bin/redis-cli shutdown
1.5 redis的罕用设置
redis的设置次要就是通过他的配置文件进行设置,目前次要能够设置如下几项:
后盾启动
# 下面曾经说了这里,把这个改成yes就能够了。daemonize yes
设置用户名明码
## 在默认的状况下,redis是没有明码的,如果在测试的时候是没问题的,然而如果我的项目要共享进来或者是实在的我的项目,那么## 就会有安全隐患,所以这种状况下须要设置明码。## 设置的形式还是在配置文件中 redis.conf,找到requirepass 标签,把它后面的 `#`去掉,而后前面增加本人的明码即可requirepass yourpassword## 设置好之后重启redis即可,重启后如果不进行上面两种形式认证,会发现你没有权限在redis中进行任何操作## 应用redis-cli 登录的时候能够增加参数 -a 前面增加明码(这种是明文,然而会给正告,不论他)## 第二种是依照原来的 redis-cli -p 6379 命令 登录,登录后 应用命令 `auth yourpassword` 进行验证(也是明文)
设置容许近程连贯
## bind字段默认为: bind 127.0.0.1 这样只能本机拜访redis## 若容许近程主机拜访,可正文掉bind行 或者 将bind 127.0.0.1改为: bind 0.0.0.0
2.Redis的五大根本数据类型
基本操作
127.0.0.1:12138> keys * # 查看所有的key(empty list or set)127.0.0.1:12138> set user hello # 设置值OK127.0.0.1:12138> set age 18OK127.0.0.1:12138> EXISTS age # 判断键存不存在(integer) 1127.0.0.1:12138> move age 1 # 把键挪动到指定的数据库 ,redis有16个库,默认应用第一个也就是0库(integer) 1127.0.0.1:12138> keys *1) "user"127.0.0.1:12138> get user # 依据key获取value"hello"127.0.0.1:12138> type user # 判断以后key对应的value的类型string127.0.0.1:12138> EXPIRE user 10 # 设置key的过期工夫,单位是秒。-1示意没有过期工夫(integer) 1127.0.0.1:12138> TTL user # 查看key的剩余时间(integer) 6127.0.0.1:12138> TTL user(integer) 4127.0.0.1:12138> TTL user # 工夫为-2示意已过期,key曾经不存在(integer) -2127.0.0.1:12138> keys *(empty list or set)127.0.0.1:12138> 127.0.0.1:12138> select 1 # 切换到指定的数据库OK127.0.0.1:12138[1]> keys *1) "age"127.0.0.1:12138[1]> DBSIZE # 查看以后数据库的大小(integer) 1
2.1 String(字符串)
#################################################################################################################基本操作127.0.0.1:12138> set k1 v1 # 设置值OK127.0.0.1:12138> get k1 # 获取值"v1"127.0.0.1:12138> keys * # 获取所有的key1) "k1"127.0.0.1:12138> EXISTS k1 # 判断某个key是否存在(integer) 1127.0.0.1:12138> APPEND k1 "append" # 追加字符串,如果以后key不存在,就相当于set key(integer) 8127.0.0.1:12138> get k1 "v1append"127.0.0.1:12138> APPEND k2 "not exist" (integer) 9127.0.0.1:12138> keys *1) "k1"2) "k2"127.0.0.1:12138> STRLEN k1 # 获取key对应的value的长度(integer) 8################################################################################################################## i++ i-- 自增,自减 以及指定步长127.0.0.1:12138> set views 0 # 初始浏览量为0OK127.0.0.1:12138> incr views # 自增一(integer) 1127.0.0.1:12138> incr views(integer) 2127.0.0.1:12138> get views"2"127.0.0.1:12138> decr views # 自减一(integer) 1127.0.0.1:12138> decr views (integer) 0127.0.0.1:12138> decr views (integer) -1127.0.0.1:12138> get views"-1"127.0.0.1:12138> INCRBY views 10 # 设置步长为10(integer) 9127.0.0.1:12138> DECRBY views 15 # 设置步长为15(integer) -6127.0.0.1:12138> get views"-6"################################################################################################################## 字符串范畴 range127.0.0.1:12138> set key1 "you are good"OK127.0.0.1:12138> get key1"you are good"127.0.0.1:12138> GETRANGE key1 0 3 # 截取字符串 [0,3]"you "127.0.0.1:12138> 127.0.0.1:12138> GETRANGE key1 0 -1 # 获取全副的字符串 和 get key是一样的"you are good"# 替换!127.0.0.1:12138> set key2 abcdefgOK127.0.0.1:12138> get key2"abcdefg"127.0.0.1:12138> SETRANGE key2 1 123 # 替换指定地位开始的字符串!(integer) 7127.0.0.1:12138> get key2"a123efg"################################################################################################################## setex (set with expire) # 设置过期工夫# setnx (set if not exist) # 不存在在设置 (在分布式锁中会经常应用!)127.0.0.1:12138> setex key3 10 "hello" # 设置key3 的值为 hello,10秒后过期OK127.0.0.1:12138> ttl key3(integer) 4127.0.0.1:12138> ttl key3(integer) 2127.0.0.1:12138> ttl key3(integer) -2127.0.0.1:12138> get key3(nil)127.0.0.1:12138> setnx mykey "redis" # 如果mykey 不存在,创立mykey(integer) 1127.0.0.1:12138> setnx mykey "redisss" # 如果mykey存在,创立失败!不会批改之前的value(integer) 0127.0.0.1:12138> get mykey"redis"################################################################################################################## mset mget 批量操作127.0.0.1:12138> mset k1 v1 k2 v2 k3 v3 # 同时设置多个值OK127.0.0.1:12138> keys *1) "k1"2) "k2"3) "k3"127.0.0.1:12138> mget k1 k2 k3 # 同时获取多个值1) "v1"2) "v2"3) "v3"127.0.0.1:12138> mget k1 k2 k4 # 如果某个键不存在就返回nil空1) "v1"2) "v2"3) (nil)127.0.0.1:12138> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起胜利,要么一起失败!(integer) 0127.0.0.1:12138> get k4 (nil)127.0.0.1:12138> #################################################################################################################getset # 先get而后在set127.0.0.1:12138> getset db redis # 如果不存在值,则返回 nil,set是会执行的(nil)127.0.0.1:12138> get db"redis"127.0.0.1:12138> getset db kafka # 如果存在值,获取原来的值,并设置新的值"redis"127.0.0.1:12138> get db"kafka"#################################################################################################################
2.2 List(列表)
在redis外面,咱们能够把list玩成 ,栈、队列、阻塞队列! 音讯队列 (Lpush Rpop), 栈( Lpush Lpop)!
- 他实际上是一个链表,before Node after , left,right 都能够插入值
- 如果key 不存在,创立新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在!
- 在两边插入或者改变值,效率最高! 两头元素,相对来说效率会低一点~
#################################################################################################################127.0.0.1:12138> LPUSH list one two # 将一个值或者多个值,插入到列表头部 (左)(integer) 2127.0.0.1:12138> LPUSH list three(integer) 3127.0.0.1:12138> LRANGE list 0 -1 # 获取list中值!1) "three"2) "two"3) "one"127.0.0.1:12138> LRANGE list 0 1 # 通过区间获取具体的值!1) "three"2) "two"127.0.0.1:12138> RPUSH list right(integer) 4127.0.0.1:12138> LRANGE list 0 -1 # 将一个值或者多个值,插入到列表位部 (右)1) "three"2) "two"3) "one"4) "right"#################################################################################################################LPOPRPOP127.0.0.1:12138> LRANGE list 0 -11) "three"2) "two"3) "one"4) "right"127.0.0.1:12138> LPOP list # 移除list的第一个元素"three"127.0.0.1:12138> RPOP list # 移除list的最初一个元素"right"127.0.0.1:12138> LRANGE list 0 -11) "two"2) "one"#################################################################################################################Lindex127.0.0.1:12138> LRANGE list 0 -11) "two"2) "one"127.0.0.1:12138> LINDEX list 1 # 通过下标取得 list 中的某一个值!"one"127.0.0.1:12138> LINDEX list 0"two"#################################################################################################################LLEN 127.0.0.1:12138> LLEN list # 返回列表的长度(integer) 2#################################################################################################################移除指定的值!Lrem127.0.0.1:6379> LRANGE list 0 -11) "three"2) "three"3) "two"4) "one"127.0.0.1:6379> lrem list 1 one # 移除list汇合中指定个数的value,准确匹配(integer) 1127.0.0.1:6379> LRANGE list 0 -11) "three"2) "three"3) "two"127.0.0.1:6379> lrem list 1 three(integer) 1127.0.0.1:6379> LRANGE list 0 -11) "three"2) "two"127.0.0.1:6379> Lpush list three(integer) 3127.0.0.1:6379> lrem list 2 three(integer) 2127.0.0.1:6379> LRANGE list 0 -11) "two"## list外面 没有依据index去删除的,只有依据值删除的,如果想要删除指定index,有这两种办法### 办法一 先把想删的设置成本人的值,而后删除本人的值lset mylist index "del"lrem mylist 1 "del"### 办法二 也能够用事务管道合并成一次申请multilset mylist index "del"lrem mylist 1 "del"exec#################################################################################################################trim 修剪。; list 截断!127.0.0.1:6379> keys *(empty list or set)127.0.0.1:6379> Rpush mylist "hello"(integer) 1127.0.0.1:6379> Rpush mylist "hello1"(integer) 2127.0.0.1:6379> Rpush mylist "hello2"(integer) 3127.0.0.1:6379> Rpush mylist "hello3"(integer) 4127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,这个list曾经被扭转了,截断了只剩下截取的元素!OK127.0.0.1:6379> LRANGE mylist 0 -11) "hello1"2) "hello2"#################################################################################################################rpoplpush # 移除列表的最初一个元素,将他挪动到新的列表中!127.0.0.1:6379> rpush mylist "hello"(integer) 1127.0.0.1:6379> rpush mylist "hello1"(integer) 2127.0.0.1:6379> rpush mylist "hello2"(integer) 3127.0.0.1:6379> rpoplpush mylist myotherlist # 移除列表的最初一个元素,将他挪动到新的列表中!"hello2"127.0.0.1:6379> lrange mylist 0 -1 # 查看原来的列表1) "hello"2) "hello1"127.0.0.1:6379> lrange myotherlist 0 -1 # 查看指标列表中,的确存在改值!1) "hello2"#################################################################################################################lset 将列表中指定下标的值替换为另外一个值,更新操作127.0.0.1:6379> EXISTS list # 判断这个列表是否存在(integer) 0127.0.0.1:6379> lset list 0 item # 如果不存在列表咱们去更新就会报错(error) ERR no such key127.0.0.1:6379> lpush list value1(integer) 1127.0.0.1:6379> LRANGE list 0 01) "value1"127.0.0.1:6379> lset list 0 item # 如果存在,更新以后下标的值OK127.0.0.1:6379> LRANGE list 0 01) "item"127.0.0.1:6379> lset list 1 other # 如果不存在,则会报错!(error) ERR index out of range#################################################################################################################linsert # 将某个具体的value插入到列把你中某个元素的后面或者前面!127.0.0.1:6379> Rpush mylist "hello"(integer) 1127.0.0.1:6379> Rpush mylist "world"(integer) 2127.0.0.1:6379> LINSERT mylist before "world" "other"(integer) 3127.0.0.1:6379> LRANGE mylist 0 -11) "hello"2) "other"3) "world"127.0.0.1:6379> LINSERT mylist after world new(integer) 4127.0.0.1:6379> LRANGE mylist 0 -11) "hello"2) "other"3) "world"4) "new"#################################################################################################################
2.3 Set(汇合)
set中的值是不能反复的!利用场景:
微博,A用户将所有关注的人放在一个set汇合中!将它的粉丝也放在一个汇合中!
独特关注,共同爱好,二度好友,举荐好友!(六度宰割实践)
#################################################################################################################127.0.0.1:12138> sadd myset are # set汇合中增加value(integer) 1127.0.0.1:12138> sadd myset you(integer) 1127.0.0.1:12138> sadd myset ok(integer) 1127.0.0.1:12138> SMEMBERS myset # 查看指定set的所有值1) "you"2) "are"3) "ok"127.0.0.1:12138> SISMEMBER myset leijun # 判断某一个值是不是在set汇合中!(integer) 0127.0.0.1:12138> SISMEMBER myset are(integer) 1#################################################################################################################127.0.0.1:12138> SCARD myset # 获取set汇合中的内容元素个数!(integer) 3#################################################################################################################删除元素 srem127.0.0.1:6379> srem myset you # 移除set汇合中的指定元素(integer) 1127.0.0.1:6379> scard myset(integer) 3127.0.0.1:6379> SMEMBERS myset1) "are"2) "ok"#################################################################################################################set 无序不反复汇合。抽随机!127.0.0.1:12138> sadd myset you(integer) 1127.0.0.1:12138> SMEMBERS myset1) "you"2) "are"3) "ok"127.0.0.1:12138> SRANDMEMBER myset # 随机抽选出一个元素"ok"127.0.0.1:12138> SRANDMEMBER myset"ok"127.0.0.1:12138> SRANDMEMBER myset"ok"127.0.0.1:12138> SRANDMEMBER myset"are"127.0.0.1:12138> SRANDMEMBER myset"ok"127.0.0.1:12138> SRANDMEMBER myset 2 # 随机抽选出指定个数的元素1) "you"2) "ok"#################################################################################################################随机删除key!127.0.0.1:12138> SMEMBERS myset1) "you"2) "are"3) "ok"127.0.0.1:12138> spop myset # 随机删除一个key"you"127.0.0.1:12138> spop myset"are"127.0.0.1:12138> SMEMBERS myset 1) "ok"127.0.0.1:12138> spop myset 2 # 随机删除指定个数的key,如果汇合中的个数不够就会有多少删除多少1) "ok"127.0.0.1:12138> SMEMBERS myset(empty list or set)#################################################################################################################将一个指定的值,挪动到另外一个set汇合!127.0.0.1:12138> sadd myset are(integer) 1127.0.0.1:12138> sadd myset you(integer) 1127.0.0.1:12138> sadd myset ok(integer) 1127.0.0.1:12138> sadd myset leijun(integer) 1127.0.0.1:12138> smove myset newmyset leijun # 将一个指定的值,挪动到另外一个set汇合!(integer) 1127.0.0.1:12138> SMEMBERS myset1) "you"2) "are"3) "ok"127.0.0.1:12138> SMEMBERS newmyset1) "leijun"#################################################################################################################微博,B站,独特关注!(并集)数字汇合类:- 差集 SDIFF- 交加- 并集127.0.0.1:12138> sadd k1 a b c d f e g (integer) 7127.0.0.1:12138> sadd k2 a b c d h i j k (integer) 8127.0.0.1:12138> SDIFF k1 k2 # 差集 绝对于k1, k2不存在的值1) "f"2) "e"3) "g"127.0.0.1:12138> SINTER k1 k2 # 交加1) "c"2) "b"3) "d"4) "a"127.0.0.1:12138> SUNION k1 k2 # 并集 1) "j" 2) "c" 3) "g" 4) "h" 5) "e" 6) "i" 7) "f" 8) "d" 9) "b"10) "k"11) "a"#################################################################################################################
2.4 Hash(哈希)
Map汇合,key-map! 时候这个值是一个map汇合! 实质和String类型没有太大区别,还是一个简略的
key-vlaue!
set myhash myfield myvaluehash 更适宜于对象的存储,String更加适宜字符串存储!
#################################################################################################################127.0.0.1:12138> hset myhash field1 hello # set一个具体 key-vlaue(integer) 1127.0.0.1:12138> hget myhash field1 # 获取一个字段值"hello"127.0.0.1:12138> hset hash f1 v1 f2 v2 # 目前测试的hset也能够同时设置多个值,和hmset的惟一区别就是,他返回的是影响的条数,如果原来有则笼罩,没有则新增,而后者只会返回一个字符串OK(integer) 2127.0.0.1:12138> hmset hash f3 v3 f4 v4 # set多个 key-vlaueOK127.0.0.1:12138> HGETALL hash # 获取全副的数据1) "f1"2) "v1"3) "f2"4) "v2"5) "f3"6) "v3"7) "f4"8) "v4"127.0.0.1:12138> hdel hash f1 # 删除hash指定key字段!对应的value值也就隐没了!(integer) 1127.0.0.1:12138> hdel hash f2 f3 # 能够一次删除多个字段(integer) 2127.0.0.1:12138> hdel hash f2 # 删除不存在的会返回影响的条数(integer) 0 127.0.0.1:12138> HGETALL hash1) "f4"2) "v4"#################################################################################################################hlen # 获取hash的字段数量127.0.0.1:12138> HGETALL hash1) "f4"2) "v4"127.0.0.1:12138> hset hash f1 v1 f2 v2(integer) 2127.0.0.1:12138> HGETALL hash1) "f4"2) "v4"3) "f1"4) "v1"5) "f2"6) "v2"127.0.0.1:12138> hlen hash # 获取hash表的字段数量!(integer) 3#################################################################################################################127.0.0.1:12138> HEXISTS hash f5 # 判断hash中指定字段是否存在!(integer) 0127.0.0.1:12138> HEXISTS hash f1(integer) 1################################################################################################################## 只取得所有field# 只取得所有value127.0.0.1:12138> hkeys hash # 只取得所有field1) "f4"2) "f1"3) "f2"127.0.0.1:12138> HVALS hash # 只取得所有value1) "v4"2) "v1"3) "v2"################################################################################################################## incr decr hash 中只有HINCRBY实现减少的api,当然增量能够使负的127.0.0.1:12138> hset mytest age 5(integer) 1127.0.0.1:12138> HINCRBY myset age 1 #指定增量!(integer) 1127.0.0.1:12138> HINCRBY myset age 3(integer) 4127.0.0.1:12138> HINCRBY myset age -5 #指定增量!(integer) -1127.0.0.1:12138> hget myset age"-1"################################################################################################################## HSETNX 不为空的时候设置127.0.0.1:12138> HSETNX myset f6 v6 # 如果不存在则能够设置(integer) 1127.0.0.1:12138> HSETNX myset f6 v5 # 如果存在则不能设置(integer) 0#################################################################################################################
2.5 Zset(有序汇合)
在set的根底上,减少了一个值,set k1 v1 zset k1 score1 v1利用场景:set 排序 存储班级成绩表,工资表排序!
一般音讯,1, 重要音讯 2,带权重进行判断!
排行榜利用实现,取Top N 测试!
#################################################################################################################127.0.0.1:6379> zadd myset 1 one # 增加一个值(integer) 1127.0.0.1:6379> zadd myset 2 two 3 three # 增加多个值(integer) 2127.0.0.1:6379> ZRANGE myset 0 -11) "one"2) "two"3) "three"#################################################################################################################排序如何实现# zset中也能够应用zrange 进行排序,和一般的range一样。(默认是从小到大,range能指定索引,而rangebyscore能指定分数)127.0.0.1:6379> zadd salary 2500 xiaohong # 增加三个用户(integer) 1127.0.0.1:6379> zadd salary 5000 zhangsan(integer) 1127.0.0.1:6379> zadd salary 500 lisi(integer) 1# ZRANGEBYSCORE key min max127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 显示全副的用户 从小到大!1) "lisi"2) "xiaohong"3) "zhangsan"127.0.0.1:6379> ZREVRANGE salary 0 -1 # 从大到进行排序!1) "xiaohong"2) "zhangsan"3) "lisi"127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 显示全副的用户并且附带成绩1) "lisi"2) "500"3) "xiaohong"4) "2500"5) "zhangsan"6) "5000"127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # 显示工资小于2500员工的升序排序!1) "lisi"2) "500"3) "xiaohong"4) "2500"################################################################################################################## 移除rem中的元素127.0.0.1:6379> zrange salary 0 -11) "kaungshen"2) "xiaohong"3) "zhangsan"127.0.0.1:6379> zrem salary xiaohong # 移除有序汇合中的指定元素(integer) 1127.0.0.1:6379> zrange salary 0 -11) "kaungshen"2) "zhangsan"127.0.0.1:6379> zcard salary # 获取有序汇合中的个数(integer) 2#################################################################################################################127.0.0.1:6379> zadd myset 1 hello(integer) 1127.0.0.1:6379> zadd myset 2 world 3 kuangshen(integer) 2127.0.0.1:6379> zcount myset 1 3 # 获取指定区间的成员数量!(integer) 3127.0.0.1:6379> zcount myset 1 2(integer) 2#################################################################################################################
3.Redis的三种非凡数据类型
3.1Geospatial 地理位置
敌人的定位,左近的人,打车间隔计算?
Redis 的 Geo 在Redis3.2 版本就推出了! 这个性能能够推算地理位置的信息,两地之间的间隔,方圆
几里的人!一共就6个命令
补充
# geo没有删除命令,咱们能够应用zrem去删除,其实他底层应用的是 Zset!咱们能够应用Zset命令来操作geo! 当然geo的最底层还是跳跃链表127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全副的元素1) "chongqi"2) "xian"3) "shengzhen"4) "hangzhou"5) "shanghai"6) "beijing"127.0.0.1:6379> zrem china:city beijing # 移除指定元素! 这个罕用(integer) 1127.0.0.1:6379> ZRANGE china:city 0 -11) "chongqi"2) "xian"3) "shengzhen"4) "hangzhou"5) "shanghai"
################################################################################################################## getadd 增加地理位置# 命令:**GEOADD** key longitude latitude member [longitude latitude member ...]# 命令形容:将指定的天文空间地位(经度、纬度、名称)增加到指定的key中。# 规定:北极和南极无奈间接增加,咱们个别会下载城市数据,间接通过java程序一次性导入!# 无效的经度从-180度到180度。# 无效的纬度从-85.05112878度到85.05112878度。# 当坐标地位超出上述指定范畴时,该命令将会返回一个谬误。# 127.0.0.1:6379> geoadd china:city 39.90 116.40 beijin(error) ERR invalid longitude,latitude pair 39.900000,116.400000# 参数 key 值()127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing(integer) 1127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai(integer) 1127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi 114.05 22.52 shengzhen(integer) 2127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian(integer) 2################################################################################################################## getpos 取得以后定位:肯定是一个坐标值!27.0.0.1:6379> GEOPOS china:city beijing # 获取指定的城市的经度和纬度!1) 1) "116.39999896287918091"2) "39.90000009167092543"127.0.0.1:6379> GEOPOS china:city beijing chongqi1) 1) "116.39999896287918091"2) "39.90000009167092543"2) 1) "106.49999767541885376"2) "29.52999957900659211"################################################################################################################## GEODIST 两个地位之间的间隔 单位:m 示意单位为米。km 示意单位为千米。mi 示意单位为英里。ft 示意单位为英尺127.0.0.1:6379> GEODIST china:city beijing shanghai km # 查看上海到北京的直线间隔"1067.3788"127.0.0.1:6379> GEODIST china:city beijing chongqi km # 查看重庆到北京的直线间隔"1464.0708"################################################################################################################## georadius 以给定的经纬度为核心, 找出某一半径内的元素127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 以110,30 这个经纬度为核心,寻找方圆1000km内的城市1) "chongqi"2) "xian"3) "shengzhen"4) "hangzhou"127.0.0.1:6379> GEORADIUS china:city 110 30 500 km1) "chongqi"2) "xian"127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 显示到两头间隔的地位1) 1) "chongqi"2) "341.9374"2) 1) "xian"2) "483.8340"127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # 显示别人的定位信息1) 1) "chongqi"2) 1) "106.49999767541885376"2) "29.52999957900659211"2) 1) "xian"2) 1) "108.96000176668167114"2) "34.25999964418929977"127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1 #筛选出指定的后果!1) 1) "chongqi"2) "341.9374"3) 1) "106.49999767541885376"2) "29.52999957900659211"127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 21) 1) "chongqi"2) "341.9374"3) 1) "106.49999767541885376"2) "29.52999957900659211"2) 1) "xian"2) "483.8340"3) 1) "108.96000176668167114"2) "34.25999964418929977"################################################################################################################## GEORADIUSBYMEMBER 找出位于指定元素四周的其余元素!127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km1) "beijing"2) "xian"127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km1) "hangzhou"2) "shanghai"################################################################################################################## GEOHASH 命令 - 返回一个或多个地位元素的 Geohash 示意该命令将返回11个字符的Geohash字符串!127.0.0.1:6379> geohash china:city beijing chongqi1) "wx4fbxxfke0"2) "wm5xzrybty0"#################################################################################################################
3.2 Hyperloglog
什么是基数?A {1,3,5,7,8,7}
B{1,3,5,7,8}基数(不反复的元素) = 5,能够承受误差!
Redis Hyperloglog 基数统计的算法!(这个也能够去学习学习)
长处:占用的内存是固定,2^64 不同的元素的技术,只须要废占用12KB内存!如果要从内存角度来比拟的话 Hyperloglog 首选!
应用场景:网页的 UV (一个人拜访一个网站屡次,然而还是算作一个人!)
传统的形式, set 保留用户的id,而后就能够统计 set 中的元素数量作为规范判断 !
这个形式如果保留大量的用户id,就会比拟麻烦!咱们的目标是为了计数,而不是保留用户id;
0.81% 错误率! 统计UV工作,能够忽略不计的!如果容许容错,那么肯定能够应用 Hyperloglog !
如果不容许容错,就应用 set 或者本人的数据类型即可!
127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 创立第一组元素 mykey(integer) 1127.0.0.1:6379> PFCOUNT mykey # 统计 mykey 元素的基数数量(integer) 10127.0.0.1:6379> PFadd mykey2 i j z x c v b n m # 创立第二组元素 mykey2(integer) 1127.0.0.1:6379> PFCOUNT mykey2(integer) 9127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集OK127.0.0.1:6379> PFCOUNT mykey3 # 看并集的数量!(integer) 15
3.3 Bitmap 位存储
应用场景:统计用户信息,沉闷,不沉闷! 登录 、 未登录! 打卡,365打卡! 两个状态的,都能够应用
Bitmaps!
Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
365 天 = 365 bit 1字节 = 8bit 46 个字节左右!
# 应用bitmap 来记录 周一到周日的打卡!# 周一:1 周二:0 周三:0 周四:1 ......127.0.0.1:6379> setbit sign 0 1(integer) 0127.0.0.1:6379> setbit sign 1 0(integer) 0127.0.0.1:6379> setbit sign 2 0(integer) 0127.0.0.1:6379> setbit sign 3 1(integer) 0127.0.0.1:6379> setbit sign 4 1(integer) 0127.0.0.1:6379> setbit sign 5 0(integer) 0127.0.0.1:6379> setbit sign 6 0(integer) 0127.0.0.1:6379> getbit sign 3 # 查看某一天是否有打卡!(integer) 1127.0.0.1:6379> getbit sign 6(integer) 0127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就能够看到是否有全勤!(integer) 3
4.Redis中的事务
事务简介
Redis 事务实质:一组命令的汇合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会依照程序执行!一次性、程序性、排他性!执行一些列的命令!Redis事务没有没有隔离级别的概念!所有的命令在事务中,并没有间接被执行!只有发动执行命令的时候才会执行!ExecRedis单条命令式保留原子性的,然而事务不保障原子性!redis的事务:开启事务(multi)命令入队(......)执行事务(exec)
Redis的事务操作
################################################################################################################## 失常执行事务!127.0.0.1:6379> multi # 开启事务OK 127.0.0.1:6379> set k1 v1 # 命令入队QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> exec # 执行事务1) OK2) OK3) "v2"4) OK################################################################################################################## 放弃事务!127.0.0.1:6379> multi # 开启事务OK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> DISCARD # 勾销事务OK127.0.0.1:6379> get k4 # 事务队列中命令都不会被执行!(nil)################################################################################################################## 编译型异样(代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> getset k3 # 谬误的命令(error) ERR wrong number of arguments for 'getset' command127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> set k5 v5QUEUED127.0.0.1:6379> exec # 执行事务报错!(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k5 # 所有的命令都不会被执行!(nil)################################################################################################################## 运行时异样(1/0), 如果事务队列中存在语法性,那么执行命令的时候,其余命令是能够失常执行的,谬误命令抛出异样!127.0.0.1:6379> set k1 "v1"OK127.0.0.1:6379> multiOK127.0.0.1:6379> incr k1 # 会执行的时候失败!QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> get k3QUEUED127.0.0.1:6379> exec1) (error) ERR value is not an integer or out of range # 尽管第一条命令报错了,然而仍旧失常执行胜利了!2) OK3) OK4) "v3"127.0.0.1:6379> get k2"v2"127.0.0.1:6379> get k3"v3"#################################################################################################################
事务的乐观锁,不是另一个线程,是另一个客户端,对于乐观锁能够参考下这个:https://my.oschina.net/itommy...
https://www.cnblogs.com/marti...
证实:
127.0.0.1:12138> set money 100OK127.0.0.1:12138> set mon 0OK127.0.0.1:12138> watch moneyOK127.0.0.1:12138> incrby money 55(integer) 155127.0.0.1:12138> multiOK127.0.0.1:12138> decr moneyQUEUED127.0.0.1:12138> incr monQUEUED127.0.0.1:12138> exec(nil)127.0.0.1:12138> get money"155"127.0.0.1:12138>
Redis 的监控机制(监控! Watch (面试常问!))
能够应用Redis的监控机制实现乐观锁或者分布式锁
测试:
################################################################################################################## 失常执行胜利!127.0.0.1:6379> set money 100OK127.0.0.1:6379> set out 0OK127.0.0.1:6379> watch money # 监督 money 对象OK127.0.0.1:6379> multi # 事务失常完结,数据期间没有产生变动,这个时候就失常执行胜利!OK127.0.0.1:6379> DECRBY money 20QUEUED127.0.0.1:6379> INCRBY out 20QUEUED127.0.0.1:6379> exec1) (integer) 802) (integer) 20################################################################################################################## 测试多线程批改值 , 应用watch 能够当做redis的乐观锁操作!127.0.0.1:6379> watch money # 监督 moneyOK127.0.0.1:6379> multiOK127.0.0.1:6379> DECRBY money 10QUEUED127.0.0.1:6379> INCRBY out 10QUEUED127.0.0.1:6379> exec # 执行之前,再关上一个客户端,批改了咱们的值,这个时候,就会导致事务执行失败!(nil)#################################################################################################################
如果批改失败,获取最新的值就好
监控机制不会带来ABA问题
redis是单线程的,在应用watch进行监控的时候,一旦批改就会被watch,因为批改的时候就是那个线程,所以redis的watch不存在aba问题。 应用watch监督一个或者多个key,跟踪key的value批改状况,如果有key的value值在 事务exec执行之前被批改了,整个事务被勾销。exec返回提示信息,示意事务曾经失败。然而如果应用watch监督了一个带过期工夫的键,那么即便这个键过期了,事务依然能够失常执行。
5.Java中应用Redis
5.1通过jedis连贯Redis
Java操作redis最根底的就是用jedis。Jedis 是 Redis 官网举荐的 java连贯开发工具! 应用Java 操作Redis 中间件!如果你要应用
java操作redis,那么肯定要对Jedis 非常的相熟!
应用步骤:
导入相干的依赖
<!--导入jedis的包--><dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency><!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency></dependencies>
- 编写测试代码
public class JedisDemo { public static void main(String[] args) { // 1、 new Jedis 对象即可 Jedis jedis = new Jedis("www.njitzyd.com",12138); // 如果设置了明码须要进行验证 jedis.auth("zydredis"); // jedis 所有的命令就是咱们之前学习的所有指令!所以之前的指令学习很重要! System.out.println(jedis.ping()); }}
- 查看后果
能够看到连贯胜利!
Jedis中罕用的API
################################################################################################################## 所有的api命令,就是咱们对应的下面学习的指令,一个都没有变动!所有的都能够通过Jedis对象实现操作,和之前的命令行中的命令完全一致!StringListSetHashZset################################################################################################################## 事务 也是和之前在命令行中的统一public class TestTX {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1", 6379);jedis.flushDB();JSONObject jsonObject = new JSONObject();jsonObject.put("hello","world");jsonObject.put("name","leijun");// 开启事务Transaction multi = jedis.multi();String result = jsonObject.toJSONString();// jedis.watch(result)try {multi.set("user1",result);multi.set("user2",result);int i = 1/0 ; // 代码抛出异样事务,执行失败!multi.exec(); // 执行事务!} catch (Exception e) {multi.discard(); // 放弃事务e.printStackTrace();} finally {System.out.println(jedis.get("user1"));System.out.println(jedis.get("user2"));jedis.close(); // 敞开连贯}}}#################################################################################################################
5.2 SpringBoot整合Redis
在SpringBoot 2.x 版本中,应用lettuce代替了jedis来操作Redis。jedis : 采纳的直连,多个线程操作的话,是不平安的,如果想要防止不平安的,应用 jedis pool 连贯
池! 更像 BIO 模式
lettuce : 采纳netty,实例能够再多个线程中进行共享,不存在线程不平安的状况!能够缩小线程数据
了,更像 NIO 模式
源码解析
@Bean@ConditionalOnMissingBean(name = "redisTemplate") // 咱们能够本人定义一个redisTemplate来替换这个默认的!public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactoryredisConnectionFactory)throws UnknownHostException {// 默认的 RedisTemplate 没有过多的设置,redis 对象都是须要序列化!// 两个泛型都是 Object, Object 的类型,咱们后应用须要强制转换 <String, Object>RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBean // 因为 String 是redis中最常应用的类型,所以说独自提出来了一个bean!public StringRedisTemplate stringRedisTemplate(RedisConnectionFactoryredisConnectionFactory)throws UnknownHostException {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}
SpringBoot中应用案例
应用的步骤:
增加依赖(是springboot我的项目)
<!-- 操作redis --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
配置连贯
在spring.properties中配置如下
# 配置redisspring.redis.host=127.0.0.1spring.redis.port=6379
测试
@SpringBootTestclass Redis02SpringbootApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {// redisTemplate 操作不同的数据类型,api和咱们的指令是一样的// opsForValue 操作字符串 相似String// opsForList 操作List 相似List// opsForSet// opsForHash// opsForZSet// opsForGeo// opsForHyperLogLog// 除了进本的操作,咱们罕用的办法都能够间接通过redisTemplate操作,比方事务,和根本的CRUD// 获取redis的连贯对象// RedisConnection connection =redisTemplate.getConnectionFactory().getConnection();// connection.flushDb();// connection.flushAll();redisTemplate.opsForValue().set("mykey","myvalue");System.out.println(redisTemplate.opsForValue().get("mykey"));}}
自定义RedisTemplate
自带的RedisTemplate的问题:
- 默认的序列化形式是jdk自带的,当间接存入没有实现
Serializable
的类会间接报错序列化失败。 - 自带的两个泛型都是Object,而咱们常常应用的是key为string类型。
- 咱们无奈指定序列化的形式,而理论开发中常常应用fastjson或者Jackson来实现序列化。
自定义如下,根本满足需要:
// 申明为一个配置类@Configurationpublic class RedisConfig {// 本人定义了一个 RedisTemplate@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactoryfactory) {// 咱们为了本人开发不便,个别间接应用 <String, Object>RedisTemplate<String, Object> template = new RedisTemplate<String,Object>();template.setConnectionFactory(factory);// Json序列化配置Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = newJackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// String 的序列化StringRedisSerializer stringRedisSerializer = newStringRedisSerializer();// key采纳String的序列化形式template.setKeySerializer(stringRedisSerializer);// hash的key也采纳String的序列化形式template.setHashKeySerializer(stringRedisSerializer);// value序列化形式采纳jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化形式采纳jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}}
这样就能够实现自定义的RedisTemplate,当咱们自定义时,零碎自带的就不会初始化。(springboot的starter机制,在自带的RedisTemplate中有这个注解@ConditionalOnMissingBean(name = "redisTemplate")
)
自定义redis工具类
就是对下面自定义的RedisTemplate之后还能够再次封装,从而使得redis操作更不便。
// 在咱们实在的散发中,或者你们在公司,个别都能够看到一个公司本人封装RedisUtil@Componentpublic final class RedisUtil { // 这里要留神,注入的是咱们刚刚自定义的RedisTemplate,而不是官网默认的! @Autowired private RedisTemplate<String, Object> redisTemplate; // =============================common============================ /** * 指定缓存生效工夫 * @param key 键 * @param time 工夫(秒) */ public boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 依据key 获取过期工夫 * @param key 键 不能为null * @return 工夫(秒) 返回0代表为永恒无效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * @param key 能够传一个值 或多个 */ @SuppressWarnings("unchecked") public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete(CollectionUtils.arrayToList(key)); } } } // ============================String============================= /** * 一般缓存获取 * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 一般缓存放入 * @param key 键 * @param value 值 * @return true胜利 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 一般缓存放入并设置工夫 * @param key 键 * @param value 值 * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true胜利 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 递增 * @param key 键 * @param delta 要减少几(大于0) */ public long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } /** * 递加 * @param key 键 * @param delta 要缩小几(小于0) */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递加因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } // ================================Map================================= /** * HashGet * @param key 键 不能为null * @param item 项 不能为null */ public Object hget(String key, String item) { return redisTemplate.opsForHash().get(key, item); } /** * 获取hashKey对应的所有键值 * @param key 键 * @return 对应的多个键值 */ public Map<Object, Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); } /** * HashSet * @param key 键 * @param map 对应多个键值 */ public boolean hmset(String key, Map<String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * HashSet 并设置工夫 * @param key 键 * @param map 对应多个键值 * @param time 工夫(秒) * @return true胜利 false失败 */ public boolean hmset(String key, Map<String, Object> map, long time) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 * @return true 胜利 false失败 */ public boolean hset(String key, String item, Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创立 * @param key 键 * @param item 项 * @param value 值 * @param time 工夫(秒) 留神:如果已存在的hash表有工夫,这里将会替换原有的工夫 * @return true 胜利 false失败 */ public boolean hset(String key, String item, Object value, long time) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除hash表中的值 * @param key 键 不能为null * @param item 项 能够使多个 不能为null */ public void hdel(String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } /** * 判断hash表中是否有该项的值 * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } /** * hash递增 如果不存在,就会创立一个 并把新增后的值返回 * @param key 键 * @param item 项 * @param by 要减少几(大于0) */ public double hincr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递加 * @param key 键 * @param item 项 * @param by 要缩小记(小于0) */ public double hdecr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } // ============================set============================= /** * 依据key获取Set中的所有值 * @param key 键 */ public Set<Object> sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 依据value从一个set中查问,是否存在 * * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将数据放入set缓存 * * @param key 键 * @param values 值 能够是多个 * @return 胜利个数 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 将set数据放入缓存 * @param key 键 * @param time 工夫(秒) * @param values 值 能够是多个 * @return 胜利个数 */ public long sSetAndTime(String key, long time, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) expire(key, time); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 获取set缓存的长度 * * @param key 键 */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 移除值为value的 * * @param key 键 * @param values 值 能够是多个 * @return 移除的个数 */ public long setRemove(String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } // ===============================list================================= /** * 获取list缓存的内容 * * @param key 键 * @param start 开始 * @param end 完结 0 到 -1代表所有值 */ public List<Object> lGet(String key, long start, long end) { try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取list缓存的长度 * * @param key 键 */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推 */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * @param key 键 * @param value 值 */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 工夫(秒) */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 工夫(秒) * @return */ public boolean lSet(String key, List<Object> value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 依据索引批改list中的某条数据 * * @param key 键 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index, Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 移除N个值为value * * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ public long lRemove(String key, long count, Object value) { try { Long remove = redisTemplate.opsForList().remove(key, count, value); return remove; } catch (Exception e) { e.printStackTrace(); return 0; } }}
5.3 jedis和lu的比照
6.Redis的配置文件redis.conf详解
7.Redis长久化
Redis 是内存数据库,如果不将内存中的数据库状态保留到磁盘,那么一旦服务器过程退出,服务器中
的数据库状态也会隐没。所以 Redis 提供了长久化性能!
7.1 RDB(Redis DataBase)
7.1.1简介
在指定的工夫距离内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它复原时是将快照文件间接读到内存里。
Redis会独自创立(fork)一个子过程来进行长久化,会先将数据写入到一个临时文件中,待长久化过程都完结了,再用这个临时文件替换上次长久化好的文件。整个过程中,主过程是不进行任何IO操作的。这就确保了极高的性能。如果须要进行大规模数据的复原,且对于数据恢复的完整性不是十分敏感,那RDB形式要比AOF形式更加的高效。RDB的毛病是最初一次长久化后的数据可能失落。咱们默认的就是RDB,个别状况下不须要批改这个配置!
rdb保留的文件是默认的dump.rdb 就是在咱们的配置文件中快照中进行配置的!具体配置就是下面所形容的redis.conf文件。
7.1.2 触发机制
默认的save的规定:
# 如果900s内,如果至多有一个1 key进行了批改,咱们及进行长久化操作save 900 1# 如果300s内,如果至多10 key进行了批改,咱们及进行长久化操作save 300 10# 如果60s内,如果至多10000 key进行了批改,咱们及进行长久化操作save 60 10000
当满足上面的条件时就会触发生成dump.rdb:
- save的规定满足的状况下,会主动触发rdb规定(上述的规定是针对bgsave命令的,是对bgsave命令失效的)
- 当执行save或者bgsave命令的时候,会触发rdb生成rdb文件(save和bgsave的区别)
- 执行 flushall 命令,也会触发咱们的rdb规定!
- 退出redis(应用shutdown命令会触发,shutdown nosave 命令不会触发,应用kill命令强制退出也不会触发),也会产生 rdb 文件!
7.1.3 RDB文件保留过程
- redis调用fork,当初有了子过程和父过程。
- 父过程持续解决client申请,子过程负责将内存内容写入到临时文件。因为os的写时复制机制(copy on write)父子过程会共享雷同的物理页面,当父过程解决写申请时os会为父过程要批改的页面创立正本,而不是写共享的页面。所以子过程的地址空间内的数据是fork时刻整个数据库的一个快照。
- 当子过程将快照写入临时文件结束后,用临时文件替换原来的快照文件,而后子过程退出。
7.1.4数据恢复
1、只须要将rdb文件放在咱们redis启动目录就能够,redis启动的时候会主动查看dump.rdb 复原其中的数据!(默认就是在这个地位)
2、查看须要存在的地位
127.0.0.1:6379> config get dir1) "dir"2) "/usr/local/bin" # 如果在这个目录下存在 dump.rdb 文件,启动就会主动复原其中的数据
7.1.4 RDB的优缺点
劣势
- 一旦采纳该形式,那么你的整个Redis数据库将只蕴含一个文件,这样十分不便进行备份。比方你可能打算没1天归档一些数据。
- 不便备份,咱们能够很容易的将一个一个RDB文件挪动到其余的存储介质上
- RDB 在复原大数据集时的速度比 AOF 的复原速度要快。
- RDB 能够最大化 Redis 的性能:父过程在保留 RDB 文件时惟一要做的就是 fork 出一个子过程,而后这个子过程就会解决接下来的所有保留工作,父过程毋庸执行任何磁盘 I/O 操作。
劣势
- 如果你须要尽量避免在服务器故障时失落数据,那么 RDB 不适宜你。 尽管 Redis 容许你设置不同的保留点(save point)来管制保留 RDB 文件的频率, 然而, 因为RDB 文件须要保留整个数据集的状态, 所以它并不是一个轻松的操作。 因而你可能会至多 5 分钟才保留一次 RDB 文件。 在这种状况下, 一旦产生故障停机, 你就可能会失落好几分钟的数据。
- 每次保留 RDB 的时候,Redis 都要 fork() 出一个子过程,并由子过程来进行理论的长久化工作。 在数据集比拟宏大时, fork() 可能会十分耗时,造成服务器在某某毫秒内进行解决客户端; 如果数据集十分微小,并且 CPU 工夫十分缓和的话,那么这种进行工夫甚至可能会长达整整一秒。 尽管 AOF 重写也须要进行 fork() ,但无论 AOF 重写的执行距离有多长,数据的耐久性都不会有任何损失。
7.2 AOF(Append Only File)
将咱们的所有命令都记录下来,history,复原的时候就把这个文件全副在执行一遍!
以日志的模式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不能够改写文件,redis启动之初会读取该文件从新构建数据,换言之,redis重启的话就依据日志文件的内容将写指令从前到后执行一次以实现数据的复原工作
Aof保留的是 appendonly.aof 文件
默认是不开启的,咱们须要手动进行配置!咱们只须要将 appendonly 改为yes就开启了 aof!其余的默认就好,能够参考下面配置文件局部的解说。重启,redis 就能够失效了!
如果如果aof文件被毁坏,比方手动批改外面的内容,能够应用redis-check-aof来进行修复,具体命令是:redis-check-aof --fix appendonly.aof
(可能会把谬误的那条数据给删除,会造成失落数据。然而这种手动批改数据的场景不多见)
7.2.1 AOF 简介
redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。
当redis重启时会通过从新执行文件中保留的写命令来在内存中重建整个数据库的内容。当然因为os会在内核中缓存 write做的批改,所以可能不是立刻写到磁盘上。这样aof形式的长久化也还是有可能会失落局部批改。不过咱们能够通过配置文件通知redis咱们想要 通过fsync函数强制os写入到磁盘的机会。有三种形式如下(默认是:每秒fsync一次)
appendonly yes //启用aof长久化形式# appendfsync always //每次收到写命令就立刻强制写入磁盘,最慢的,然而保障齐全的长久化,不举荐应用appendfsync everysec //每秒钟强制写入磁盘一次,在性能和长久化方面做了很好的折中,举荐# appendfsync no //齐全依赖os,性能最好,长久化没保障
7.2.2 AOF的rewrite机制
aof 的形式也同时带来了另一个问题。长久化文件会变的越来越大。例如咱们调用incr test命令100次,文件中必须保留全副的100条命令,其实有99条都是多余的。因为要复原数据库的状态其实文件中保留一条set test 100就够了。
为了压缩aof的长久化文件。redis提供了bgrewriteaof命令。收到此命令redis将应用与快照相似的形式将内存中的数据 以命令的形式保留到临时文件中,最初替换原来的文件。具体过程如下
- redis调用fork ,当初有父子两个过程
- 子过程依据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 父过程持续解决client申请,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保障如果子过程重写失败的话并不会出问题。
- 当子过程把快照内容写入已命令形式写到临时文件中后,子过程发信号告诉父过程。而后父过程把缓存的写命令也写入到临时文件。
- 当初父过程能够应用临时文件替换老的aof文件,并重命名,前面收到的写命令也开始往新的aof文件中追加。
须要留神到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的形式重写了一个新的aof文件,这点和快照有点相似。
7.2.3 AOF的优缺点
劣势
- 应用 AOF 长久化会让 Redis 变得非常耐久(much more durable):你能够设置不同的 fsync 策略,比方无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 依然能够保持良好的性能,并且就算产生故障停机,也最多只会失落一秒钟的数据( fsync 会在后盾线程执行,所以主线程能够持续致力地解决命令申请)。
- AOF 文件是一个只进行追加操作的日志文件(append only log), 因而对 AOF 文件的写入不须要进行 seek , 即便日志因为某些起因而蕴含了未写入残缺的命令(比方写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也能够轻易地修复这种问题。
Redis 能够在 AOF 文件体积变得过大时,主动地在后盾对 AOF 进行重写: 重写后的新 AOF 文件蕴含了复原以后数据集所需的最小命令汇合。 整个重写操作是相对平安的,因为 Redis 在创立新 AOF 文件的过程中,会持续将命令追加到现有的 AOF 文件外面,即便重写过程中产生停机,现有的 AOF 文件也不会失落。 而一旦新 AOF 文件创建结束,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。 - AOF 文件有序地保留了对数据库执行的所有写入操作, 这些写入操作以 Redis 协定的格局保留, 因而 AOF 文件的内容非常容易被人读懂, 对文件进行剖析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只有 AOF 文件未被重写, 那么只有进行服务器, 移除 AOF 文件开端的 FLUSHALL 命令, 并重启 Redis , 就能够将数据集复原到 FLUSHALL 执行之前的状态。
劣势
- 对于雷同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 因为redis是单线程的,每次aof文件同步写入都要等(能够参考上面AOF长久化详解文章中的
文件写入和同步
模块)。依据所应用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在个别状况下, 每秒 fsync 的性能仍然十分高, 而敞开 fsync 能够让 AOF 的速度和 RDB 一样快, 即便在高负荷之下也是如此。 不过在解决微小的写入载入时,RDB 能够提供更有保障的最大延迟时间(latency)。 - AOF 在过来已经产生过这样的 bug : 因为个别命令的起因,导致 AOF 文件在从新载入时,无奈将数据集复原成保留时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就已经引起过这样的 bug 。) 测试套件里为这种状况增加了测试: 它们会主动生成随机的、简单的数据集, 并通过从新载入这些数据来确保一切正常。 尽管这种 bug 在 AOF 文件中并不常见, 然而比照来说, RDB 简直是不可能呈现这种 bug 的。
7.3 两者比照
两者的开启并不是抵触的,如果都开启,零碎默认是优先加载aof的文件来进行复原。那个两种长久化形式如何敞开,能够参考上面的办法。RDB和AOF的敞开办法
AOF长久化详解
参考
7.4扩大:
1、RDB 长久化形式可能在指定的工夫距离内对你的数据进行快照存储
2、AOF 长久化形式记录每次对服务器写的操作,当服务器重启的时候会从新执行这些命令来复原原始的数据,AOF命令以Redis 协定追加保留每次写的操作到文件开端,Redis还能对AOF文件进行后盾重写,使得AOF文件的体积不至于过大。
3、只做缓存,如果你只心愿你的数据在服务器运行的时候存在,你也能够不应用任何长久化
4、同时开启两种长久化形式在这种状况下,当redis重启的时候会优先载入AOF文件来复原原始的数据,因为在通常状况下AOF文件保留的数据集要比RDB文件保留的数据集要残缺。RDB 的数据不实时,同时应用两者时服务器重启也只会找AOF文件,那要不要只应用AOF呢?作者倡议不要,因为RDB更适宜用于备份数据库(AOF在一直变动不好备份),疾速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的伎俩。
5、性能倡议
因为RDB文件只用作后备用处,倡议只在Slave上长久化RDB文件,而且只有15分钟备份一次就够了,只保留 save 900 1 这条规定。如果Enable AOF ,益处是在最顽劣状况下也只会失落不超过两秒数据,启动脚本较简略只load本人的AOF文件就能够了,代价一是带来了继续的IO,二是AOF rewrite 的最初将 rewrite 过程中产生的新数据写到新文件造成的阻塞简直是不可避免的。只有硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的根底大小默认值64M太小了,能够设到5G以上,默认超过原大小100%大小重写能够改到适当的数值。如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也能够,能省掉一大笔IO,也缩小了rewrite时带来的零碎稳定。代价是如果Master/Slave 同时倒掉,会失落十几分钟的数据,启动脚本也要比拟两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
8.Redis公布订阅(临时简略理解)
公布订阅的命令
8.1 测试
订阅端:
127.0.0.1:6379> SUBSCRIBE mychannel # 订阅一个频道 mychannelReading messages... (press Ctrl-C to quit)1) "subscribe"2) "mychannel"3) (integer) 1# 期待读取推送的信息1) "message" # 音讯2) "mychannel" # 那个频道的音讯3) "hello,channel" # 音讯的具体内容1) "message"2) "mychannel"3) "hello,redis"
发送端:
127.0.0.1:6379> PUBLISH mychannel "hello,channel" # 发布者公布音讯到频道!(integer) 1127.0.0.1:6379> PUBLISH mychannel "hello,redis" # 发布者公布音讯到频道!(integer) 1
8.2 原理
Redis是应用C实现的,通过剖析 Redis 源码里的 pubsub.c 文件,理解公布和订阅机制的底层实现,籍此加深对 Redis 的了解。
Redis 通过 PUBLISH 、SUBSCRIBE 和 PSUBSCRIBE 等命令实现公布和订阅性能。
通过 SUBSCRIBE 命令订阅某频道后,redis-server 里保护了一个字典,字典的键就是一个个 频道!而字典的值则是一个链表,链表中保留了所有订阅这个 channel 的客户端。SUBSCRIBE 命令的要害,就是将客户端增加到给定 channel 的订阅链表中。
通过 PUBLISH 命令向订阅者发送音讯,redis-server 会应用给定的频道作为键,在它所保护的 channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将音讯公布给所有订阅者。
Pub/Sub 从字面上了解就是公布(Publish)与订阅(Subscribe),在Redis中,你能够设定对某一个key值进行音讯公布及音讯订阅,当一个key值上进行了音讯公布后,所有订阅它的客户端都会收到相应的音讯。这一性能最显著的用法就是用作实时音讯零碎,比方一般的即时聊天,群聊等性能。
9.Redis主从复制
9.1 概念
主从复制,是指将一台Redis服务器的数据,复制到其余的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。
Master以写为主,Slave 以读为主。
能够应用命令 info replication
查看redis的相干信息如下:
# Replicationrole:masterconnected_slaves:0master_replid:a63afb2b95c60ac0a9d20b5cb76d1505a54bac58master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0
默认状况下,每台独立Redis服务器都是主节点;
且一个主节点能够有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用次要包含:
1、数据冗余:主从复制实现了数据的热备份,是长久化之外的一种数据冗余形式。
2、故障复原:当主节点呈现问题时,能够由从节点提供服务,实现疾速的故障复原;实际上是一种服务的冗余。
3、负载平衡:在主从复制的根底上,配合读写拆散,能够由主节点提供写服务,由从节点提供读服务(即写Redis数据时利用连贯主节点,读Redis数据时利用连贯从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,能够大大提高Redis服务器的并发量。
4、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群可能施行的根底,因而说主从复制是Redis高可用的根底。
一般来说,要将Redis使用于工程项目中,只应用一台Redis是万万不能的(宕机),起因如下:
1、从构造上,单个Redis服务器会产生单点故障,并且一台服务器须要解决所有的申请负载,压力较大;
2、从容量上,单个Redis服务器内存容量无限,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大应用内存不应该超过20G。
电商网站上的商品,个别都是一次上传,无数次浏览的,说业余点也就是"多读少写"。
<img src="https://gitee.com/jsnucrh/blog-sharding_1/raw/master/img/20201219225402.png" alt="image-20200929224316529" style="zoom:80%;" />
9.2 环境配置
如果资源比拟少,只有一台服务器的话,能够复制3个配置文件,而后批改对应的信息
1、端口
2、pid 名字
3、log文件名字
4、dump.rdb 名字
批改结束之后,通过启动命令指定不同的配置文件实现主从复制。
daemonize yesport 6379pidfile /var/run/redis_6379.pidlogfile "redis_6379.log"dbfilename 6379.rdb
9.3 一主二从
默认状况下,每台Redis服务器都是主节点; 咱们个别状况下只用配置从机就好了!能够应用命令 SLAVEOF ip port
来实现主从,然而这样是长期的。如果要永恒失效就要在配置文件中配置,在REPLICATION下有个slaveof,配置主机 和 端口 就好,如果主有明码就把明码也配置上就好了。
从机只能读不能写
应用SLAVEOF命令进行主从设置的时候,如果中途从机断了,而后从机再写入数据,那么再次进行slaveof的时候,从机独有的数据会被革除,也就是从机的数据在进行主从的时候会同步和主机的数据完全一致,不多不少。
9.4 哨兵模式
主从切换技术的办法是:当主服务器宕机后,须要手动把一台从服务器切换为主服务器,这就须要人工干预,麻烦费劲,还会造成一段时间内服务不可用。这不是一种举荐的形式,更多时候,咱们优先思考哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。
哨兵模式可能后盾监控主机是否故障,如果故障了依据投票数主动将从库转换为主库。哨兵模式是一种非凡的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的过程,作为过程,它会独立运行。其原理是哨兵通过发送命令,期待Redis服务器响应,从而监控运行的多个Redis实例。
这里的哨兵有两个作用
- 通过发送命令,让Redis服务器返回监控其运行状态,包含主服务器和从服务器。
- 当哨兵监测到master宕机,会主动将slave切换成master,而后通过公布订阅模式告诉其余的从服务器,批改配置文件,让它们切换主机。
然而一个哨兵过程对Redis服务器进行监控,可能会呈现问题,为此,咱们能够应用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就造成了多哨兵模式。
假如主服务器宕机,哨兵1先检测到这个后果,零碎并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个景象成为主观下线。当前面的哨兵也检测到主服务器不可用,并且数量达到肯定值时,那么哨兵之间就会进行一次投票,投票的后果由一个哨兵发动,进行failover[故障转移]操作。切换胜利后,就会通过公布订阅模式,让各个哨兵把本人监控的从服务器实现切换主机,这个过程称为主观下线。
9.5 哨兵模式的设置
配置哨兵配置文件 sentinel.conf
# sentinel monitor 被监控的名称 host port 1# 哨兵模式的最初的一个 1 的意思是哨兵判断该节点多少次才算死亡,就是几个哨兵都失去他死了才算死(即几个哨兵认为他死了他才算死)sentinel monitor myredis 127.0.0.1 6379 1
- 指定本人的配置文件启动
./bin/redis-sentinel myconfig/sentinel.conf
- 察看日志即可
9.6 哨兵模式总结
长处:
1、哨兵集群,基于主从复制模式,所有的主从配置长处,它全有
2、 主从能够切换,故障能够转移,零碎的可用性就会更好
3、哨兵模式就是主从模式的降级,手动到主动,更加强壮!
毛病:
1、Redis 不好啊在线扩容的,集群容量一旦达到下限,在线扩容就非常麻烦!
2、实现哨兵模式的配置其实是很麻烦的,外面有很多抉择!
哨兵模式的全副配置!
# Example sentinel.conf# 哨兵sentinel实例运行的端口 默认26379port 26379# 哨兵sentinel的工作目录dir /tmp# 哨兵sentinel监控的redis主节点的 ip port# master-name 能够本人命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。# quorum 配置多少个sentinel哨兵对立认为master主节点失联 那么这时主观上认为主节点失联了# sentinel monitor <master-name> <ip> <redis-port> <quorum>sentinel monitor mymaster 127.0.0.1 6379 2# 当在Redis实例中开启了requirepass foobared 受权明码 这样所有连贯Redis实例的客户端都要提供明码# 设置哨兵sentinel 连贯主从的明码 留神必须为主从设置一样的验证明码# sentinel auth-pass <master-name> <password>sentinel auth-pass mymaster MySUPER--secret-0123passw0rd# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒# sentinel down-after-milliseconds <master-name> <milliseconds>sentinel down-after-milliseconds mymaster 30000# 这个配置项指定了在产生failover主备切换时最多能够有多少个slave同时对新的master进行 同步,这个数字越小,实现failover所需的工夫就越长,然而如果这个数字越大,就意味着越 多的slave因为replication而不可用。能够通过将这个值设为 1 来保障每次只有一个slave 处于不能解决命令申请的状态。# sentinel parallel-syncs <master-name> <numslaves>sentinel parallel-syncs mymaster 1# 故障转移的超时工夫 failover-timeout 能够用在以下这些方面:#1. 同一个sentinel对同一个master两次failover之间的间隔时间。#2. 当一个slave从一个谬误的master那里同步数据开始计算工夫。直到slave被纠正为向正确的master那里同步数据时。#3.当想要勾销一个正在进行的failover所须要的工夫。#4.当进行failover时,配置所有slaves指向新的master所需的最大工夫。不过,即便过了这个超时,slaves仍然会被正确配置为指向master,然而就不按parallel-syncs所配置的规定来了# 默认三分钟# sentinel failover-timeout <master-name> <milliseconds>sentinel failover-timeout mymaster 180000# SCRIPTS EXECUTION#配置当某一事件产生时所须要执行的脚本,能够通过脚本来告诉管理员,例如当零碎运行不失常时发邮件告诉相干人员。#对于脚本的运行后果有以下规定:#若脚本执行后返回1,那么该脚本稍后将会被再次执行,反复次数目前默认为10#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会反复执行。#如果脚本在执行过程中因为收到零碎中断信号被终止了,则同返回值为1时的行为雷同。#一个脚本的最大执行工夫为60s,如果超过这个工夫,脚本将会被一个SIGKILL信号终止,之后从新执行。#告诉型脚本:当sentinel有任何正告级别的事件产生时(比如说redis实例的主观生效和主观生效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等形式去告诉系统管理员对于零碎不失常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的形容。如果sentinel.conf配置文件中配置了这个脚本门路,那么必须保障这个脚本存在于这个门路,并且是可执行的,否则sentinel无奈失常启动胜利。#告诉脚本# shell编程# sentinel notification-script <master-name> <script-path>sentinel notification-script mymaster /var/redis/notify.sh# 客户端重新配置主节点参数脚本# 当一个master因为failover而产生扭转时,这个脚本将会被调用,告诉相干的客户端对于master地址已经产生扭转的信息。# 以下参数将会在调用脚本时传给脚本:# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port># 目前<state>总是“failover”,# <role>是“leader”或者“observer”中的一个。# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的# 这个脚本应该是通用的,能被屡次调用,不是针对性的。# sentinel client-reconfig-script <master-name> <script-path>sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 个别都是由运维来配置!
10.Redis缓存击穿、穿透和雪崩
这部分内容能够看我之前的博客