Redis官网
http://www.redis.cn/
教程地址
https://www.bilibili.com/video/BV1S54y1R7SB?p=36&share_source=copy_web
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT where
# dbid is a number between 0 and 'databases'-1
databases 16
127.0.0.1:6379> select 8 # 切换数据库
OK
127.0.0.1:6379[8]> set name Mashiro # 设置key name value Mashiro
OK
127.0.0.1:6379[8]> get name # 获取name属性
"Mashiro"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> get name # 不同数据库之间 数据是不能互通的
(nil)
127.0.0.1:6379> select 8
OK
127.0.0.1:6379[8]> keys * # 查看当前数据库中所有的key。
1) "name"
127.0.0.1:6379[8]> FLUSHDB # 清空当前数据库中的键值对。
OK
127.0.0.1:6379[8]> FLUSHALL # 清空所有数据库的键值对。
OK
[root@Mashiro bin]# ls
dump.rdb redis-check-aof redis-sentinel
myredisconfig redis-check-rdb redis-server
redis-benchmark redis-cli
[root@Mashiro bin]# pwd
/usr/local/bin
[root@Mashiro bin]# redis-server myredisconfig/redis.conf
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> ping # 启动成功后返回PONG
PONG
127.0.0.1:6379>
# 测试:100个并发连接 100000请求
[root@Mashiro bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000
====== SET ======
100000 requests completed in 0.91 seconds
100 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": no
multi-thread: no
96.24% <= 1 milliseconds
99.84% <= 2 milliseconds
99.90% <= 5 milliseconds
100.00% <= 5 milliseconds
109769.48 requests per second
====== GET ======
100000 requests completed in 0.82 seconds
100 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": no
multi-thread: no
97.54% <= 1 milliseconds
100.00% <= 2 milliseconds
100.00% <= 2 milliseconds
121506.68 requests per second
#################################################################
127.0.0.1:6379> set name Mashiro
OK
127.0.0.1:6379> set age 22
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
# 判断键是否存在,如果存在则返回1 不存在则返回0
127.0.0.1:6379> EXISTS name
(integer) 1
127.0.0.1:6379> EXISTS birth
(integer) 0
#################################################################
127.0.0.1:6379> MOVE name 1 # MOVE key index 将键name 移动到1号数据库
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "name"
#################################################################
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> get name
"Mashiro"
127.0.0.1:6379> EXPIRE name 5 # EXPIRE key seconds 设置一个键的有效时间 单位为秒
(integer) 1
127.0.0.1:6379> ttl name # 查看剩余秒数
(integer) 2
127.0.0.1:6379> ttl names
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
# Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
# 当前key没有设置过期时间,所以会返回-1.
# 当前key有设置过期时间,而且key已经过期,所以会返回-2.
# 当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.
#################################################################
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> TYPE name # TYPE key 查看键的类型
string
127.0.0.1:6379> TYPE age
string
#################################################################
127.0.0.1:6379> set name Mashiro
OK
127.0.0.1:6379> get name
"Mashiro"
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> EXISTS name
(integer) 1
127.0.0.1:6379> APPEND name ",hello" # 追加字符串
(integer) 13
127.0.0.1:6379> get name
"Mashiro,hello"
127.0.0.1:6379> STRLEN name # 查看字符串长度
(integer) 13
127.0.0.1:6379> APPEND key1 "good" # 如果append的键不存在 相当于set一个key
(integer) 4
127.0.0.1:6379> keys *
1) "key1"
2) "name"
#################################################################
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCR views # 相当于views++
(integer) 1
127.0.0.1:6379> INCR views
(integer) 2
127.0.0.1:6379> DECR views # 相当于views--
(integer) 1
127.0.0.1:6379> DECR views
(integer) 0
127.0.0.1:6379> DECR views
(integer) -1
127.0.0.1:6379> INCRBY views 10 # 相当于views += 10
(integer) 9
127.0.0.1:6379> DECRBY views 5 # 相当于 views -= 5
(integer) 4
#################################################################
127.0.0.1:6379> set key1 "hello,Mashiro"
OK
127.0.0.1:6379> get key1
"hello,Mashiro"
127.0.0.1:6379> GETRANGE key1 0 3 # 截取字符串[0,3]位置的字符串
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1 # 获取全部字符串
"hello,Mashiro"
#################################################################
127.0.0.1:6379> set key2 ABCDEFG
OK
127.0.0.1:6379> get key2
"ABCDEFG"
127.0.0.1:6379> SETRANGE key2 2 WWW # 从字符串的2位置开始 替换字符串
(integer) 7
127.0.0.1:6379> get key2
"ABWWWFG"
#################################################################
127.0.0.1:6379> SETEX key3 10 "Good" # setex (set with expire)设置过期时间
OK
127.0.0.1:6379> ttl key3
(integer) 6
127.0.0.1:6379> SETNX key4 "Java" # setnx(set if not exist) 不存在则设置,防止重复设置而覆盖数据
(integer) 1
127.0.0.1:6379> SETNX key4 "Python"
(integer) 0
127.0.0.1:6379> get key4
"Java"
#################################################################
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 # 批量Set
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> MGET k1 k2 k3 # 批量Get
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> MSETNX k1 v1 k4 v4 # 遵循原子性原则 要么都成功 要么都失败
(integer) 0
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
#################################################################
# 对象
127.0.0.1:6379> set user:1 "{name:Mashiro,age:18}" # 使用JSON字符串格式存入对象 set user:{id} {filed}
OK
127.0.0.1:6379> get user:1
"{name:Mashiro,age:18}"
127.0.0.1:6379> mset user:1:name Nagisa user:1:age 18
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "Nagisa"
2) "18"
#################################################################
# Get Set组合命令 获取旧的值并交换新的值
127.0.0.1:6379> getset lang "Java"
(nil)
127.0.0.1:6379> get lang
"Java"
127.0.0.1:6379> getset lang "Python"
"Java"
127.0.0.1:6379> get lang
"Python"
#################################################################
Redis中,List的用法可以是栈、队列、阻塞队列等
所有的list命令都是 l
开头
#################################################################
127.0.0.1:6379> LPUSH mylist one # 从列表的头部插入一个或多个值 (left push)
(integer) 1
127.0.0.1:6379> LPUSH mylist two
(integer) 2
127.0.0.1:6379> LPUSH mylist three
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1 # 获取列表的所有值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE mylist 0 1 # 获取列表[0,1]的值(现进后出,获取的值为倒序)
1) "three"
2) "two"
127.0.0.1:6379> RPUSH mylist zero # 从列表尾部插入一个或多个值 (right push)
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
#################################################################
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> LPOP mylist # 从头部弹出一个值 (left pop)
"three"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "two"
2) "one"
3) "zero"
127.0.0.1:6379> RPOP mylist # 从尾部弹出一个值 (right pop)
"zero"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "two"
2) "one"
127.0.0.1:6379>
#################################################################
127.0.0.1:6379> LPUSH mylist three
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> RPUSH mylist zero
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> LINDEX mylist 2 # 通过下标获取列表中的值
"one"
127.0.0.1:6379> LINDEX mylist 0
"three"
127.0.0.1:6379> LLEN mylist # 获取列表值的个数
(integer) 4
#################################################################
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
5) "zero"
127.0.0.1:6379> LREM mylist 1 zero # List Remove 在mylist中移除1个"zero"
(integer) 1
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> LREM mylist 1 three
(integer) 1
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LPUSH mylist three
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> LREM mylist 2 three # List Remove 在mylist中移除2个"Three"
(integer) 2
127.0.0.1:6379> LRANGE mylist 0 -1
1) "two"
2) "one"
#################################################################
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
5) "hello5"
127.0.0.1:6379> LTRIM list 1 3 # 裁剪list中的[1,3]元素。(ltrim修改了list的值,而lrange不修改)
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello2"
2) "hello3"
3) "hello4"
#################################################################
127.0.0.1:6379> RPUSH firstList "hello0" "hello1" "hello2" "hello3" "hello4"
(integer) 5
127.0.0.1:6379> LRANGE firstList 0 -1
1) "hello0"
2) "hello1"
3) "hello2"
4) "hello3"
5) "hello4"
127.0.0.1:6379> RPOPLPUSH firstList firstList # 从一个list的底部弹出值,并添加到另一个list的头部
"hello4"
127.0.0.1:6379> LRANGE firstList 0 -1
1) "hello4"
2) "hello0"
3) "hello1"
4) "hello2"
5) "hello3"
127.0.0.1:6379> LREM firstList 1 hello4
(integer) 1
127.0.0.1:6379> LRANGE firstList 0 -1
1) "hello0"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> RPUSH secoundList "hi0" "hi1"
(integer) 2
127.0.0.1:6379> RPOPLPUSH firstList secoundList
"hello3"
127.0.0.1:6379> LRANGE firstList 0 -1
1) "hello0"
2) "hello1"
3) "hello2"
127.0.0.1:6379> LRANGE secoundList 0 -1
1) "hello3"
2) "hi0"
3) "hi1"
#################################################################
127.0.0.1:6379> EXISTS list # 判断list是否存在
(integer) 0
127.0.0.1:6379> LSET list 0 value # 设置list的第0个元素的值为"value",如果不存在则返回error
(error) ERR no such key
127.0.0.1:6379> LPUSH list 0 value1
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "value1"
2) "0"
127.0.0.1:6379> LSET list 0 hello
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "0"
#################################################################
127.0.0.1:6379> RPUSH mylist "one" two three four five
(integer) 5
127.0.0.1:6379> LRANGE mylist 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
127.0.0.1:6379> LINSERT mylist before one oneoneone # 在mylist列表中的"one"前面添加"oneoneone"
(integer) 6
127.0.0.1:6379> LRANGE mylist 0 -1
1) "oneoneone"
2) "one"
3) "two"
4) "three"
5) "four"
6) "five"
127.0.0.1:6379> LINSERT mylist after five helloworld # 在mylist列表中的"five"后面添加"helloworld"
(integer) 7
127.0.0.1:6379> LRANGE mylist 0 -1
1) "oneoneone"
2) "one"
3) "two"
4) "three"
5) "four"
6) "five"
7) "helloworld"
#################################################################
# push 和 pop 才分左右。其它命令开头的‘l’都是‘list’
无序的Set集合
#################################################################
127.0.0.1:6379> SADD myset 'hello' # 向set中插入值
(integer) 1
127.0.0.1:6379> SADD myset 'Mashiro'
(integer) 1
127.0.0.1:6379> SADD myset 'world'
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查询set中的成员
1) "world"
2) "hello"
3) "Mashiro"
127.0.0.1:6379> SISMEMBER myset hello # 判断set中该成员是否存在
(integer) 1
127.0.0.1:6379> SISMEMBER myset 666
(integer) 0
127.0.0.1:6379> SADD myset 'Mashiro' # set中不能插入相同的值
(integer) 0
127.0.0.1:6379> SCARD myset # 查询set中成员的数量
(integer) 3
#################################################################
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "hello"
3) "Mashiro"
127.0.0.1:6379> SREM myset hello # 移除set中的元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "Mashiro"
#################################################################
127.0.0.1:6379> SRANDMEMBER myset # 随机抽出一个元素
"Mashiro"
127.0.0.1:6379> SRANDMEMBER myset
"world"
127.0.0.1:6379> SMEMBERS myset
1) "[A,B,C,D,E]"
2) "world"
3) "Mashiro"
127.0.0.1:6379> SPOP myset # 随机删除某一个元素
"Mashiro"
#################################################################
127.0.0.1:6379> SADD myset1 "Mashiro"
(integer) 1
127.0.0.1:6379> SADD myset1 "Nagisa"
(integer) 1
127.0.0.1:6379> SADD myset1 "Hello"
(integer) 1
127.0.0.1:6379> SADD myset2 "world"
(integer) 1
127.0.0.1:6379> SMOVE myset1 myset2 "Hello" # 将一个set中的元素移动到另一个set中
(integer) 1
127.0.0.1:6379> SMEMBERS myset1
1) "Nagisa"
2) "Mashiro"
127.0.0.1:6379> SMEMBERS myset2
1) "world"
2) "Hello"
#################################################################
127.0.0.1:6379> SADD set1 a b c d e
(integer) 5
127.0.0.1:6379> sadd set2 d e f g h
(integer) 5
127.0.0.1:6379> SMEMBERS set1
1) "b"
2) "d"
3) "a"
4) "c"
5) "e"
127.0.0.1:6379> SMEMBERS set2
1) "g"
2) "e"
3) "d"
4) "f"
5) "h"
127.0.0.1:6379> SDIFF set1 set2 # 查询set1 中set2没有的成员 【差集】
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> SINTER set1 set2 # 查询set1和set2中共有的成员 【交集】
1) "d"
2) "e"
127.0.0.1:6379> SUNION set1 set2 # 查询set1和set2的并集
1) "b"
2) "f"
3) "c"
4) "h"
5) "g"
6) "e"
7) "d"
8) "a"
Map集合。
Hash更合适对象的存储
#################################################################
127.0.0.1:6379> HSET myhash field mashiro # 向Hash中添加key-value
(integer) 1
127.0.0.1:6379> HGET myhash field # 获取hash中对应key的value
"mashiro"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> HMSET myhash x mashiro y nagisa z hello # 向hash中插入多个值
OK
127.0.0.1:6379> HMGET myhash x y z # 获取hash中的多个值
1) "mashiro"
2) "nagisa"
3) "hello"
127.0.0.1:6379> HGETALL myhash # 获取hash中的所有值 返回的形式为 key value key value ...
1) "x"
2) "mashiro"
3) "y"
4) "nagisa"
5) "z"
6) "hello"
#################################################################
127.0.0.1:6379> HGETALL myhash
1) "x"
2) "mashiro"
3) "y"
4) "nagisa"
5) "z"
6) "hello"
127.0.0.1:6379> HDEL myhash x # 删除hash中指定的key-value
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "y"
2) "nagisa"
3) "z"
4) "hello"
#################################################################
127.0.0.1:6379> HLEN myhash # 查询hash中有多少key-value
(integer) 2
#################################################################
127.0.0.1:6379> HEXISTS myhash y # 判断hash中key是否存在
(integer) 1
127.0.0.1:6379> HEXISTS myhash x
(integer) 0
#################################################################
127.0.0.1:6379> HKEYS myhash # 获取hash中所有的key
1) "y"
2) "z"
127.0.0.1:6379> HVALS myhash # 获取hash中所有的value
1) "nagisa"
2) "hello"
#################################################################
127.0.0.1:6379> HSET myhash number 0
(integer) 1
127.0.0.1:6379> HINCRBY myhash number 10 # 指定hash中的key,value增加10
(integer) 10
127.0.0.1:6379> HGET myhash number
"10"
127.0.0.1:6379> HINCRBY myhash number -5 # 指定hash中的key,value减少5
(integer) 5
127.0.0.1:6379> HGET myhash number
"5"
#################################################################
127.0.0.1:6379> HSETNX myhash mykey Java # 如果key不存在,则创建key-value成功
(integer) 1
127.0.0.1:6379> HSETNX myhash mykey Python # 如果key存在,则创建失败
(integer) 0
#################################################################
#################################################################
127.0.0.1:6379> ZADD myzset 1 one # 向zset中添加元素, 1是排序序号
(integer) 1
127.0.0.1:6379> ZADD myzset 2 two 3 three
(integer) 2
127.0.0.1:6379> ZRANGE myzset 0 -1 # 读取zset中所有元素
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ZADD salary 2500 A 3000 B 1500 C 6000 D
(integer) 4
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 升序排序读取 [-inf,+inf]:负无穷到正无穷
1) "C"
2) "A"
3) "B"
4) "D"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 升序排序读取 [带值]
1) "C"
2) "1500"
3) "A"
4) "2500"
5) "B"
6) "3000"
7) "D"
8) "6000"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf withscores # 降序排序读取 [带值]
1) "D"
2) "6000"
3) "B"
4) "3000"
5) "A"
6) "2500"
7) "C"
8) "1500"
#################################################################
127.0.0.1:6379> ZRANGE salary 0 -1
1) "C"
2) "A"
3) "B"
4) "D"
127.0.0.1:6379> ZREM salary A # 移除zset中的一个元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "C"
2) "B"
3) "D"
127.0.0.1:6379> ZCARD salary # 查询Zset中有多少元素
(integer) 3
#################################################################
127.0.0.1:6379> ZADD myzset 1 A 2 B 3 C 4 D 5 E
(integer) 5
127.0.0.1:6379> ZCOUNT myzset 1 2 # 获取区间内的元素数量
(integer) 2
127.0.0.1:6379> ZCOUNT myzset 0 5
(integer) 5
127.0.0.1:6379> ZCOUNT myzset 3 5
(integer) 3
127.0.0.1:6379> ZCOUNT myzset 0 7
(integer) 5
#################################################################
规则:两级无法直接添加。一般直接下载城市地理数据,直接使用Java程序写入
#################################################################
127.0.0.1:6379> GEOADD china:city 113.27324 23.15792 GuangZhou 113.29673 22.20907 ZhuHai # 添加多个key,城市与城市经纬度
(integer) 2
127.0.0.1:6379> GEOADD china:city 116.67809 23.46244 ChaoZhou # 添加一个key,城市与城市经纬度
(integer) 1
127.0.0.1:6379> GEODIST china:city GuangZhou ChaoZhou # 计算两个地理位置的距离
"349433.8927"
127.0.0.1:6379> GEODIST china:city GuangZhou ChaoZhou km # 计算两个地理位置的距离 单位为千米
"349.4339"
127.0.0.1:6379> GEODIST china:city GuangZhou ChaoZhou m # 计算两个地理位置的距离 单位为米
"349433.8927"
127.0.0.1:6379> GEORADIUS china:city 116 23 200 km # 计算某个经纬度方圆XX km内的所有地点
1) "ChaoZhou"
127.0.0.1:6379> GEORADIUS china:city 116 23 400 km
1) "ZhuHai"
2) "GuangZhou"
3) "ChaoZhou"
# 获取某一点方圆XX km内的10个元素(附带经度,纬度和距离)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord withdist count 10
1) 1) "GuangZhou"
2) "827.6084"
3) 1) "113.27324062585830688"
2) "23.1579209662846921"
2) 1) "ZhuHai"
2) "926.8499"
3) 1) "113.29673141241073608"
2) "22.20907091277658907"
3) 1) "ChaoZhou"
2) "983.8174"
3) 1) "116.67809039354324341"
2) "23.46243983164651326"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord withdist count 1 # 获取某一点方圆XX km内的1个元素
1) 1) "GuangZhou"
2) "827.6084"
3) 1) "113.27324062585830688"
2) "23.1579209662846921"
#################################################################
127.0.0.1:6379> GEOHASH china:city ChaoZhou GuangZhou ZhuHai # 返回11个字符长度的GEOHASH字符串
1) "ws4vrp4qse0"
2) "ws0e9xg09v0"
3) "webw65h1jn0"
127.0.0.1:6379> GEOPOS china:city ChaoZhou GuangZhou ZhuHai
1) 1) "116.67809039354324341"
2) "23.46243983164651326"
2) 1) "113.27324062585830688"
2) "23.1579209662846921"
3) 1) "113.29673141241073608"
2) "22.20907091277658907"
127.0.0.1:6379> GEOPOS china:city ChaoZhou 300 km
1) 1) "116.67809039354324341"
2) "23.46243983164651326"
2) (nil)
3) (nil)
127.0.0.1:6379> GEOPOS china:city ChaoZhou 400 km
1) 1) "116.67809039354324341"
2) "23.46243983164651326"
2) (nil)
3) (nil)
127.0.0.1:6379> GEORADIUSBYMEMBER china:city ChaoZhou 100 km # 找出指定元素方圆XX km内的元素
1) "ChaoZhou"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city ChaoZhou 1000 km
1) "ZhuHai"
2) "GuangZhou"
3) "ChaoZhou"
#################################################################
# Geo底层是ZSet 所以可以使用ZSet命令操作Geo
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "ZhuHai"
2) "GuangZhou"
3) "ChaoZhou"
127.0.0.1:6379> ZREM china:city ZhuHai # 使用ZSet的删除命令移除GEO中的元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "GuangZhou"
2) "ChaoZhou"
#################################################################
用于基数统计的算法
#################################################################
127.0.0.1:6379> PFADD hll a b c d e f g
(integer) 1
127.0.0.1:6379> PFCOUNT hll
(integer) 7
127.0.0.1:6379> PFADD hll1 abc def ghi jkl
(integer) 1
127.0.0.1:6379> PFADD hll2 a123 b123 c123 d123
(integer) 1
127.0.0.1:6379> PFCOUNT hll1
(integer) 4
127.0.0.1:6379> PFCOUNT hll2
(integer) 4
127.0.0.1:6379> PFMERGE hll3 hll1 hll2
OK
127.0.0.1:6379> PFCOUNT hll3
(integer) 8
127.0.0.1:6379> PFADD pf1 a b c d a b c
(integer) 1
127.0.0.1:6379> PFCOUNT pf1 # 统计不重复的元素数量
(integer) 4
#################################################################
#################################################################
127.0.0.1:6379> SETBIT testbit 0 0 # 插入一个Bit数据
(integer) 0
127.0.0.1:6379> SETBIT testbit 1 1
(integer) 0
127.0.0.1:6379> SETBIT testbit 2 1
(integer) 0
127.0.0.1:6379> GETBIT testbit 0 # 读取指定的Bit
(integer) 0
127.0.0.1:6379> GETBIT testbit 1
(integer) 1
127.0.0.1:6379> SETBIT testbit 3 1
(integer) 0
127.0.0.1:6379> SETBIT testbit 4 0
(integer) 0
127.0.0.1:6379> SETBIT testbit 5 0
(integer) 0
127.0.0.1:6379> SETBIT testbit 6 0
(integer) 0
127.0.0.1:6379> BITCOUNT testbit # 查看testbit里面有多少值为1的bit
(integer) 3
#################################################################
开启事务:MULTI,提交:EXEC
Marks the start of a transaction block. Subsequent commands will be queued for atomic execution using EXEC.
Redis事务单条命令保持原子性,事务不保持原子性
127.0.0.1:6379> MULTI # 开启
OK
# 加入队列Begin #
127.0.0.1:6379> set mykey v1
QUEUED
127.0.0.1:6379> set mykey v2
QUEUED
127.0.0.1:6379> set mykey2 v3
QUEUED
127.0.0.1:6379> get mykey
QUEUED
127.0.0.1:6379> set myset Mashiro
QUEUED
127.0.0.1:6379> get mykey2
QUEUED
127.0.0.1:6379> get myset
QUEUED
# 加入队列End #
127.0.0.1:6379> exec # 提交
1) OK
2) OK
3) OK
4) "v2"
5) OK
6) "v3"
7) "Mashiro"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 编译错误代码
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors. # 事务提交失败
127.0.0.1:6379> get k1 # 事务代码全部不执行
(nil)
127.0.0.1:6379> get k4 # 事务代码全部不执行
(nil)
127.0.0.1:6379> set k1 'hello'
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR k1 # 运行错误代码
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
1) (error) ERR value is not an integer or out of range # 运行错误
2) OK
3) OK
127.0.0.1:6379> get k2 # 事务正确代码执行
"v2"
127.0.0.1:6379> get k3 # 事务正确代码执行
"v3"
127.0.0.1:6379> WATCH money # 对money开启监控
OK
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> DECRBY money 500
QUEUED
127.0.0.1:6379> INCRBY out 500
QUEUED
127.0.0.1:6379> # 未提交
127.0.0.1:6379> INCRBY money 1000 # 对money进行了操作
(integer) 2000
127.0.0.1:6379> exec
(nil) # 事务提交失败
127.0.0.1:6379> unwatch
OK
public static void main(String[] args) {
String host = "我的服务器地址";
int port = 6379;
String password = "我的密码";
Jedis jedis = new Jedis(host,port);
jedis.auth(password);
System.out.println(jedis.ping());
}
//jedis String
// Set
System.out.println("Jedis-String-Set-name:" + jedis.set("name", "Mashiro"));
System.out.println("Jedis-String-Set-age:" + jedis.set("age", "22"));
// Get
System.out.println("Jedis-String-Get-name:" + jedis.get("name"));
System.out.println("Jedis-String-Get-age:" + jedis.get("age"));
// keys
System.out.println("Jedis-String-Keys:" + jedis.keys("*"));
// exists
System.out.println("Jedis-String-Exists-name:" + jedis.exists("name"));
System.out.println("Jedis-String-Exists-age:" + jedis.exists("age"));
// append
System.out.println("Jedis-String-append-name:" + jedis.append("name","Shina"));
System.out.println("Jedis-String-Get-name:" + jedis.get("name"));
// strlen
System.out.println("Jedis-String-Strlen-name:" + jedis.strlen("name"));
// incr && incrby
System.out.println("Jedis-String-Incr-age:" + jedis.incr("age"));
System.out.println("Jedis-String-Incr-age:" + jedis.incrBy("age",10));
System.out.println("Jedis-String-Get-age:" + jedis.get("age"));
// decr && decrby
System.out.println("Jedis-String-Decr-age:" + jedis.decr("age"));
System.out.println("Jedis-String-Decr-age:" + jedis.decrBy("age",10));
System.out.println("Jedis-String-Get-age:" + jedis.get("age"));
// flushDB
System.out.println("Jedis-FlushDB:" + jedis.flushDB());
# 运行结果
Jedis-String-Set-name:OK
Jedis-String-Set-age:OK
Jedis-String-Get-name:Mashiro
Jedis-String-Get-age:22
Jedis-String-Keys:[age, name]
Jedis-String-Exists-name:true
Jedis-String-Exists-age:true
Jedis-String-append-name:12
Jedis-String-Get-name:MashiroShina
Jedis-String-Strlen-name:12
Jedis-String-Incr-age:23
Jedis-String-Incr-age:33
Jedis-String-Get-age:33
Jedis-String-Decr-age:32
Jedis-String-Decr-age:22
Jedis-String-Get-age:22
Jedis-FlushDB:OK
Jedis jedis = new Jedis(host,port);
jedis.auth(password);
JSONObject jsonObject = new JSONObject();
jsonObject.put("name1","hello");
jsonObject.put("name2","world");
String s = jsonObject.toJSONString();
Transaction multi = jedis.multi();
try {
multi.set("user1",s);
multi.set("user2",s);
multi.exec();
} catch (Exception e){
multi.discard();
} finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.4.5version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
<version>2.4.8version>
<scope>compilescope>
dependency>
<dependency>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
<version>6.0.4.RELEASEversion>
<scope>compilescope>
dependency>
dependencies>
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-transportartifactId>
<version>4.1.63.Finalversion>
<scope>compilescope>
dependency>
Jedis直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接。
Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
使用Lettuce的性能比Jedis更高。
1.Maven:org.springframework.boot:spring-boot-autoconfigure:2.4.5 2.org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 3.RedisProperties.class
# Springboot-Redis
spring:
redis:
host: **********
port: 6379
password: 123456
@Bean
// 可自定义一个redisTemplate来替换这个Template
@ConditionalOnMissingBean(
name = {
"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 泛型为
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
// 由于String类型使用频繁,单独提供了StringRedisTemplate的Bean来供平时使用
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Autowired
private RedisTemplate redisTemplate;
// 操作五大基本数据类型
// 操作String
ValueOperations value = redisTemplate.opsForValue();
// 操作List
ListOperations list = redisTemplate.opsForList();
// 操作Hash
HashOperations hash = redisTemplate.opsForHash();
// 操作Set
SetOperations set = redisTemplate.opsForSet();
// 操作ZSet
ZSetOperations zSet = redisTemplate.opsForZSet();
// 操作三种特殊数据类型
// 操作Geo
GeoOperations geo = redisTemplate.opsForGeo();
// 操作HyperLogLog
HyperLogLogOperations hyperLogLog = redisTemplate.opsForHyperLogLog();
ClusterOperations cluster = redisTemplate.opsForCluster();
// 事务三个方法
redisTemplate.multi();
redisTemplate.exec();
redisTemplate.discard();
// 获取连接对象操作Redis
RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
RedisConnection connection = connectionFactory.getConnection();
connection.flushDb();
connection.flushAll();
// 测试运行
RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
RedisConnection connection = connectionFactory.getConnection();
System.out.println(connection.ping());
// Result: PONG
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private String name;
private Integer id;
private Integer age;
}
@Test
void contextLoads() throws JsonProcessingException {
User user = new User("Mashiro",1,22);
// 真实开发中一般采用JSON格式传递对象
String value = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",value);
System.out.println(redisTemplate.opsForValue().get("user"));
}
{
"name":"Mashiro","id":1,"age":22}
47.115.170.206:6379> get user
(nil)
47.115.170.206:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04user" # 乱码
@Test
void contextLoads() throws JsonProcessingException {
User user = new User("Mashiro",1,22);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
# 异常报错
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException:
@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User implements Serializable {
private String name;
private Integer id;
private Integer age;
}
@Test
void contextLoads() throws JsonProcessingException {
User user = new User("Mashiro",1,22);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
# IDEA测试结果
User(name=Mashiro, id=1, age=22)
# Redis查看结果
47.115.170.206:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04user"
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<Object>(Object.class);
template.setKeySerializer(serializer);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
StringRedisSerializer redisSerializer = new StringRedisSerializer();
// Key采用String的序列化方式
template.setKeySerializer(redisSerializer);
// Hash的Key采用String的序列化方式
template.setHashKeySerializer(redisSerializer);
// Value采用Jackson的序列化方式
template.setValueSerializer(serializer);
// Hash的Value采用Jackson的序列化方式
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
# Redis对大小写不敏感
# include /path/to/local.conf
# include /path/to/other.conf
# 可以包含其它redis配置文件
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
# 绑定 .so文件
bind 127.0.0.1 # 绑定ip地址
protected-mode no # 是否开启保护(如果是yes则不能远程访问)
port 6379 # 端口号
daemonize no # 是否以守护进程的方式运行(是否后台运行)
pidfile /var/run/redis_6379.pid # 如果以后台方式运行,则需要指定一个pid文件
# 配置数据库数量 默认为16个
databases 16
# 是否开启Redis的Logo
always-show-logo yes
# Redis日志级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing) 测试/开发环境
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) 生产环境
# warning (only very important / critical messages are logged)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
# 日志输出位置和文件名
# Specify the log file name. Also the empty string can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
# Save the DB on disk:
#
# save
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behavior will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
# 在一段时间内进行了XX次操作就会持久化到文件.rdb .rof
# Redis是内存数据库,如果没有持久化,里面的数据断电即失。
# 900s内有1次操作
save 900 1
# 300s内有10次操作
save 300 10
# 60s内有10000次操作
save 60 10000
# 持久化失败时,Redis是否继续工作
stop-writes-on-bgsave-error yes
# 是否压缩RDB文件
rdbcompression yes
# RDB数据
rdbchecksum yes
复制/主从复制
# 设置登录验证密码
requirepass 123456
# 默认不开启AOF 使用RDB进行持久化
appendonly no
# aof持久化的文件名称
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
# 配置是否同步aof
# appendfsync always # 每次修改都会同步
appendfsync everysec # 每秒同步
# appendfsync no # 不同步
在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
# 配置文件中配置了RDB的保存文件名
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000
save 60 5
[root@Mashiro bin]# ls
dump.rdb myredisconfig redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@Mashiro bin]# rm dump.rdb
rm: remove regular file ‘dump.rdb’? y
[root@Mashiro bin]# ls
myredisconfig redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
4) OK
5) OK
[root@Mashiro bin]# ls
dump.rdb myredisconfig redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
# dumo.rdb出现了
将dump.rdb文件放到redis的bin目录下
以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
# 配置文件中配置了AOF的保存文件名
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
appendonly no # yes
appendonly yes
[root@Mashiro bin]# ls
appendonly.aof redis-benchmark redis-check-rdb redis-sentinel
myredisconfig redis-check-aof redis-cli redis-server
# 订阅一个或多个符合给定模式的频道。
PSUBSCRIBE pattern [pattern ...]
# 查看订阅与发布系统状态。
PUBSUB subcommand [argument [argument ...]]
# 将信息发送到指定的频道。
PUBLISH channel message
# 退订所有给定模式的频道。
PUNSUBSCRIBE [pattern [pattern ...]]
# 订阅给定的一个或多个频道的信息。
SUBSCRIBE channel [channel ...]
# 指退订给定的频道。
UNSUBSCRIBE [channel [channel ...]]
127.0.0.1:6379> PSUBSCRIBE Mashiro
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "Mashiro"
3) (integer) 1
# 频道订阅成功
127.0.0.1:6379> PUBLISH Mashiro "Hello world"
(integer) 1
# 频道消息发布
127.0.0.1:6379> PSUBSCRIBE Mashiro
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "Mashiro"
3) (integer) 1
# 获取刚刚发布方发布的信息
1) "pmessage"
2) "Mashiro"
3) "Mashiro"
4) "Hello world"
127.0.0.1:6379> info REPLICATION
# Replication
role:master
connected_slaves:0
master_replid:b787e7cb04e67dc4ca671e56a2b2c5ec7c6bfe64
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@Mashiro myredisconfig]# ls
redis.conf
[root@Mashiro myredisconfig]# cp redis.conf redis-6379.conf
[root@Mashiro myredisconfig]# cp redis.conf redis-6380.conf
[root@Mashiro myredisconfig]# cp redis.conf redis-6381.conf
[root@Mashiro myredisconfig]# ls
redis-6379.conf redis-6380.conf redis-6381.conf redis.conf
# redis-6379.conf
logfile "redis6379.log"
dbfilename dump6379.rdb
# redis-6380.conf
port 6380
pidfile /var/run/redis_6380.pid
logfile "redis6380.log"
dbfilename dump6380.rdb
# redis-6381.conf
port 6381
pidfile /var/run/redis_6381.pid
logfile "redis6381.log"
dbfilename dump6381.rdb
[root@Mashiro bin]# redis-server myredisconfig/redis-6379.conf
[root@Mashiro bin]# redis-server myredisconfig/redis-6380.conf
[root@Mashiro bin]# redis-server myredisconfig/redis-6381.conf
[root@Mashiro bin]# ls
dump.rdb redis6379.log redis6381.log redis-check-aof redis-cli redis-server
myredisconfig redis6380.log redis-benchmark redis-check-rdb redis-sentinel
# 三个Redis启动成功
[root@Mashiro bin]# ps -ef|grep redis
root 14794 1 0 15:10 ? 00:00:00 redis-server *:6379
root 14801 1 0 15:11 ? 00:00:00 redis-server *:6380
root 14808 1 0 15:12 ? 00:00:00 redis-server *:6381
root 14815 14695 0 15:12 pts/0 00:00:00 grep --color=auto redis
默认情况下每一台Redis都是主节点
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:b7305f55deff1254d41c535443a68f6cb5c87679
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@Mashiro bin]# redis-cli -p 6380
127.0.0.1:6380> auth 123456
OK
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:15ee54582ad276d2d5eb33fe8fd487325c774911
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@Mashiro bin]# redis-cli -p 6381
127.0.0.1:6381> auth 123456
OK
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:0
master_replid:265b6a0279a8fc51bf93ada5d8b91b0fb50abf8c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1619076037
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:15ee54582ad276d2d5eb33fe8fd487325c774911
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6381> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1619076061
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:265b6a0279a8fc51bf93ada5d8b91b0fb50abf8c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=84,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=84,lag=1
master_replid:a453013d69c6ce006e40abf29a2ff3068cc6b4ba
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
如果Redis配置了认证密码,则配置文件中需要额外配置master的密码 “masterauth 123456”
这里的配置使用的是命令配置【暂时的】,真实集群搭建,使用配置文件进行配置【永久的】
127.0.0.1:6379> shutdown
not connected> exit
127.0.0.1:6380> info replication
# Replication
role:slave # 依旧是从机
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1763
master_link_down_since_seconds:36
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:a453013d69c6ce006e40abf29a2ff3068cc6b4ba
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1763
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1763
[root@Mashiro bin]# redis-server myredisconfig/redis-6379.conf
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=14,lag=0
slave1:ip=127.0.0.1,port=6380,state=online,offset=14,lag=1
master_replid:1a0116cead6c3ef059b858a65564be338b35eee9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
127.0.0.1:6379> set hello world
OK
127.0.0.1:6380> get hello
"world"
# 6379 主机
[root@Mashiro bin]# redis-server myredisconfig/redis-6379.conf
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=26924,lag=0
slave1:ip=127.0.0.1,port=6380,state=online,offset=26924,lag=1
master_replid:1a0116cead6c3ef059b858a65564be338b35eee9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:26924
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:26924
# 6380 从机1
[root@Mashiro bin]# redis-server myredisconfig/redis-6380.conf
[root@Mashiro bin]# redis-cli -p 6380
127.0.0.1:6380> auth 123456
OK
127.0.0.1:6380> ping
PONG
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:26924
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:1a0116cead6c3ef059b858a65564be338b35eee9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:26924
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:26924
# 6381 从机2
[root@Mashiro bin]# redis-server myredisconfig/redis-6381.conf
[root@Mashiro bin]# redis-cli -p 6381
127.0.0.1:6381> auth 123456
OK
127.0.0.1:6381> ping
PONG
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:26910
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:1a0116cead6c3ef059b858a65564be338b35eee9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:26910
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:26910
[root@Mashiro myredisconfig]# ls -l
total 336
-rw-r--r-- 1 root root 0 Apr 22 15:23 redis
-rw-r--r-- 1 root root 85579 Apr 22 15:09 redis-6379.conf
-rw-r--r-- 1 root root 85566 Apr 22 15:41 redis-6380.conf
-rw-r--r-- 1 root root 85565 Apr 22 15:28 redis-6381.conf
-rw-r--r-- 1 root root 85561 Apr 22 14:01 redis.conf
# sentinel monitor 被监控的名称 host port
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor
sentinel monitor myredis 127.0.0.1 6379 1
sentinel auth-pass myredis 123456
[root@Mashiro bin]# redis-sentinel myredisconfig/sentinel.conf
# Result
15373:X 22 Apr 2021 21:32:57.278 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
15373:X 22 Apr 2021 21:32:57.278 # Redis version=6.0.10, bits=64, commit=00000000, modified=0, pid=15373, just started
15373:X 22 Apr 2021 21:32:57.278 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.10 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 15373
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
15373:X 22 Apr 2021 21:32:57.279 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
15373:X 22 Apr 2021 21:32:57.280 # Sentinel ID is ffc5f60581397b313f9ebaeedfffcc0682899fbe
15373:X 22 Apr 2021 21:32:57.280 # +monitor master myredis 127.0.0.1 6379 quorum 1
15373:X 22 Apr 2021 21:32:57.281 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
15373:X 22 Apr 2021 21:32:57.286 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
127.0.0.1:6379> shutdown
not connected> exit
[root@Mashiro bin]#
# 从机6380篡位成功 成为主机
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=54031,lag=1
master_replid:4bd92e5a565b40d969768b4846e7bdb080ebf0bf
master_replid2:1a0116cead6c3ef059b858a65564be338b35eee9
master_repl_offset:54031
second_repl_offset:52925
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:54031
# 从机6381
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380 # 主机端口变成了6380
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:55101
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:4bd92e5a565b40d969768b4846e7bdb080ebf0bf
master_replid2:1a0116cead6c3ef059b858a65564be338b35eee9
master_repl_offset:55101
second_repl_offset:52925
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:55101
[root@Mashiro bin]# redis-server myredisconfig/redis-6379.conf
[root@Mashiro bin]# redis-cli -p 6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> info replication
# Replication
role:slave # 原来的主机复活之后 变成了从机
master_host:127.0.0.1
master_port:6380
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1619098913
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:8c7e7eee73ce918f9e756cdb40fea62efa63da58
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
出现缓存穿透的原因
当用户请求查询一个数据时,缓存中没有这个数据(缓存Miss),于是就会去持久层数据库进行查询,此时持久层数据库也没有这个数据,本次查询失败。如果有大量的用户缓存Miss,则会给持久层数据库造成巨大压力,在分布式微服务环环相扣的架构体系中,可能会造成很严重的后果。
解决方案
方案1: 引入“布隆过滤器”
方案2:将用户查询的数据是一个空值,则将这个空值保存在缓存中(会占用内存空间,应该设置比较短暂的存在时间)
出现缓存击穿的原因
如果一个key有效时间到期,在失效的瞬间,大并发集中对这个点进行访问,持续的大并发就会击穿缓存,直接请求持久层数据库,给持久层数据库造成巨大压力。
解决方案
方案1:设置热点数据不过期
方案2:加入分布式锁
出现缓存雪崩的原因
某一个时间段,缓存过期失效。Redis宕机。假设数据缓存一个小时,一个小时之后一大批数据过期,而此时对这批数据进行访问查询,都会直接访问到持久层数据库上,产生周期性的压力波峰。所有的请求到达持久层,持久层调用暴增,持久层就有可能会去世。
解决方案
方案1:高可用。增加拓展Redis集群数量。
方案2:限流降级。服务降级或者停止某些服务,给缓存提供充足的资源。当缓存失效之后,通过加锁或者队列来控制请求。
方案3:数据预热。在正式部署之前,讲可能的热点数据进行遍历,讲大量的访问数据提前加载到缓存中。在发生大并发访问之前,手动触发加载缓存不同的key,并错开过期时间 (设置不同的过期时间),讲大并发的请求分摊开来。