常用命令
redis 常用命令
redis-cli 进入redis操作命令行
keys* 查看所有键
get "键名" 获取指定键
keys xxx* 获取xxx开头的键名
del key 删除指定键名的值
flushall 删除全部数据库的全部数据
move 键名 1 删除键值对 1 代表当前数据库
redis-cli -n 0 keys "*" | xargs redis-cli -n 0 del 下面的命令指定数据序号为0,即默认数据库
dbsize 查看key的数量:
ttl 键名 检查键名过期时间
type 键名 查看当前key的类型
五大基础类型
1.String类型 (字符串)
结构图:
①追加、拼接、字符串长度
myRedis:2>set key1 ab # 设置key
"OK"
myRedis:2>get key1 #查看 key
"ab"
myRedis:2>APPEND key1 hello #给key值 追加拼接字符串,如果本来不存在,就新建
"7"
myRedis:2>get key1
"abhello"
myRedis:2>strlen key1 # 获取key值的长度
"7"
myRedis:2>
② i++、增加、减去固定值
I++ 、固定步长的 i++
做 i++ 操作
myRedis:2>set view 0 # 设置浏览量 为 0
"OK"
myRedis:2>get view
"0"
myRedis:2>incr view # 加一
"1"
myRedis:2>incr view # 加一
"2"
myRedis:2>incr view # 加一
"3"
myRedis:2>get view #获取值
"3"
myRedis:2>decr view #减一
"2"
myRedis:2>decr view #减一
"1"
myRedis:2>get view
"1"
myRedis:2>incrby view 10 #加固定值
"11"
myRedis:2>incrby view 10 #加固定值
"21"
myRedis:2>decrby view 5 #减去固定值
"16"
myRedis:2>decrby view 5 #减去固定值
"11"
myRedis:2>
③ 截取字符串
截取字符串
myRedis:2>set key1 hello,chenchong
"OK"
myRedis:2>get key1
"hello,chenchong"
myRedis:2>getrange key1 0 3 #截取字符串 下标0 开始
"hell"
myRedis:2>getrange key1 0 -1 #截取字符串 下标0 开始 -1代表全部长度
"hello,chenchong"
myRedis:2>
④ 根据是否已经存在判断是否存入
根据键名是否已经存在,来决定操作跟返回值
myRedis:2>keys * #检查键
1) "key1"
myRedis:2>setnx key2 222 # 键不存在,直接创建
"1"
myRedis:2>setnx key1 111 #键已经存在,创建失败,返回 0
"0"
myRedis:2>
myRedis:2>mset k1 v1 k2 v2 # 一次设置多个键值对 空格键分开
"OK"
myRedis:2>keys *
1) "k1"
2) "k2"
myRedis:2>msetnx k3 v3 k2 v2 # 批量判断是否存在,不存在才插入,其中一个存在就全部失败 (原子性)
"0"
myRedis:2>
⑤ 组合命令 getset
先 get 再 set
myRedis:2>getset key 123 # 如果键名不存在,就创建赋值,并返回null
null
myRedis:2>get key
"123"
myRedis:2>getset key 456 #如果键名存在,就返回原来的值,替换为新值
"123"
myRedis:2>get key
"456"
2.list类型 (列表)
■ redis 中 list 相当于一个双端队列,它可以充当完成 堆、栈、队列的功能
■ list 中,数据是分左右操作的,
从左边进行的所有操作命令都是以 L 开头的。
从左边进行的所有操作命令都是以 R开头的
■ list中允许重复值
结构图:
① 存入、取出 - lpush rpush
redis中,向list中存入数据是以 栈 的存储方式进行的,先存入的在最里面(可能是左边也可能是右边)
myRedis:2>lpush list one # 从左边存入 第一个 one
"1"
myRedis:2>lpush list two # 从左边存入 第二个 two
"2"
myRedis:2>lpush list three # 从左边存入 第三个 three
"3"
myRedis:2>lrange list 0 1 # 根据下标来从左边获取前两个 得到 three、two
1) "three"
2) "two"
myRedis:2>rpush list four # 从右边存入 第一个 four
"4"
myRedis:2>rpush list five # 从右边存入 第二个 five
"5"
myRedis:2>
存储结构:
② 移除数据 - lpop rpop
myRedis:2>lrange list 0 -1 # 打印list中全部数据
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
myRedis:2>lpop list # 左边移除一个,返回移除的值
"three"
myRedis:2>lrange list 0 -1 # 打印list中全部数据
1) "two"
2) "one"
3) "four"
4) "five"
myRedis:2>rpop list # 右边移除一个,返回移除的值
"five"
myRedis:2>lrange list 0 -1 # 打印list中全部数据
1) "two"
2) "one"
3) "four"
myRedis:2>
③ 通过下标获取指定位置的值
myRedis:2>lrange list 0 -1 #打印数据
1) "two"
2) "one"
3) "four"
myRedis:2>lindex list 0 #获取左边第一个
"two"
myRedis:2>lindex list 1 #获取左边第二个
"one"
④ 获取列表长度 - llen
myRedis:2>llen list
"3"
⑤ 移除指定的元素
因为list中允许重复值的出现,因此移除的时候,需要指定移除几个
myRedis:2>lrange list 0 -1 # 打印list
1) "two"
2) "one"
3) "four"
myRedis:2>lrem list 1 two # 移除 一个 值 two
"1"
myRedis:2>lrange list 0 -1
1) "one"
2) "four"
myRedis:2>
⑥ 指定下标截断
截取的时候,已经修改了原本的list数据
myRedis:2>lrange list 0 -1 #打印
1) "d"
2) "c"
3) "b"
4) "a"
myRedis:2>ltrim list 1 2 # 从 下标为1 开始截取,取 2 个值
"OK"
myRedis:2>lrange list 0 -1
1) "c"
2) "b"
myRedis:2>
⑦ 移除并移动到新list
localhost:2>lpush list a # 向 list 中赋值
"1"
localhost:2>lpush list b
"2"
localhost:2>lpush list c
"3"
localhost:2>lrange list 0 -1 #打印
1) "c"
2) "b"
3) "a"
localhost:2> lpush list2 1 # 向list2 赋值
"1"
localhost:2>lrange list2 0 -1 #打印
1) "1"
localhost:2>rpoplpush list list2 # 从list中移除,并放入 list2中
"a"
localhost:2>lrange list 0 -1
1) "c"
2) "b"
localhost:2>lrange list2 0 -1
1) "a"
2) "1"
localhost:2>
⑧ 在某个元素之前插入
localhost:2>lpush list b # 插入数据
"1"
localhost:2>lpush list a
"2"
localhost:2>lrange list 0 -1 #打印
1) "a"
2) "b"
localhost:2>linsert list before b c # 从左边,在 b 之前插入 c
"3"
localhost:2>lrange list 0 -1
1) "a"
2) "c"
3) "b"
localhost:2>
localhost:2>lrange list 0 -1
1) "a"
2) "c"
3) "b"
localhost:2>linsert list after b d # 在后边插入
"4"
localhost:2>lrange list 0 -1
1) "a"
2) "c"
3) "b"
4) "d"
localhost:2>
总结:
3.Set类型 (集合)
set 里面存储的数据不允许重复,存储是无序的
set 的语句都是以 s 开头的
结构图:
①基础语句 插入 查询 判断是否存在
localhost:2>sadd set1 hello # 存入
"1"
localhost:2>sadd set1 world
"1"
localhost:2>smembers set1 #查看集合中所有成员
1) "world"
2) "hello"
localhost:2>sismember set1 hello # 判断成员是否存在 存在1 不存在0
"1"
localhost:2>sismember set1 hello123
"0"
localhost:2>
② 重复插入测试,集合长度,移除元素
localhost:2>smembers set1 #打印
1) "world"
2) "hello"
localhost:2>sadd set1 hello # 重复添加 返回0 插入失败
"0"
localhost:2>scard set1 #获得集合的长度
"2"
localhost:2>srem set1 hello #移除指定元素
"1"
localhost:2>scard set1
"1"
localhost:2>
③随机获取 , 随机删除
localhost:2>smembers set
1) "c"
2) "b"
3) "d"
4) "f"
5) "e"
6) "a"
localhost:2>srandmember set 1 #随机抽出指定个数的元素
"a"
localhost:2>srandmember set 1
"c"
localhost:2>srandmember set 1
"f"
localhost:2>srandmember set 1
"c"
localhost:2>srandmember set 1
"e"
localhost:2>srandmember set 1
"b"
localhost:2>
localhost:2>spop set #随机删除
"c"
localhost:2>spop set
"a"
localhost:2>smembers set
1) "b"
2) "d"
3) "f"
4) "e"
localhost:2>
④ 求交集 , 差集 , 并集
set1 : 1,2,3
set2 : 3,4,5
localhost:2>smembers set1 # 两个集合
1) "1"
2) "2"
3) "3"
localhost:2>smembers set2
1) "3"
2) "4"
3) "5"
localhost:2>sdiff set1 set2 #求差集 前面的比后面的差的是什么
1) "1"
2) "2"
localhost:2>sdiff set2 set1 #求差集
1) "4"
2) "5"
localhost:2>sinter set1 set2 #求交集
1) "3"
localhost:2>sunion set1 set2 #求并集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
4.Hash类型 (哈希)
map集合,它里面存得是键值对,前面的类型,要么是一个键对应一个string,要么是一个键对应很多数据,这个,一个键,对应的是很多 key-value
Hash类型的操作,一般都是以 h 字母开头
结构图:
① 单个存入,取出 多个存入,取出
myRedis:2>hset myHash key1 value1 #存入单个 key-value 键值对
"1"
myRedis:2>hget myHash key1 #取出单个 key 的值
"value1"
myRedis:2>hmset myHash key2 value2 key3 value3 key4 value4 #存入多个
"OK"
myRedis:2>hmget myHash key2 key3 #取出多个
1) "value2"
2) "value3"
myRedis:2>
②查看所有键值对,删除指定键值对
myRedis:2>hgetall myHash # 查看所有
1) "key1"
2) "value1"
3) "key2"
4) "value2"
5) "key3"
6) "value3"
7) "key4"
8) "value4"
myRedis:2>hdel myHash key1 删除指定Hash中指定键值对
"1"
myRedis:2>hgetall myHash
1) "key2"
2) "value2"
3) "key3"
4) "value3"
5) "key4"
6) "value4"
myRedis:2>
③查看其中存在多个键值对的数量
myRedis:2>hgetall myHash #打印
1) "key2"
2) "value2"
3) "key3"
4) "value3"
5) "key4"
6) "value4"
myRedis:2>hlen myHash # 键值对数量
"3"
myRedis:2> hdel myHash key3 # 删除其中一对
"1"
myRedis:2>hlen myHash #再次检查
"2"
④判断是否存在某个key
myRedis:2>hgetall myHash #打印
1) "key2"
2) "value2"
3) "key4"
4) "value4"
myRedis:2>hexists myHash key2 #判断是否存在,存在返回1 不存在返回0
"1"
myRedis:2>hexists myHash key7
"0"
myRedis:2>
⑤获取所有key , 获取所有value
myRedis:2>hgetall myHash # 打印
1) "key2"
2) "value2"
3) "key4"
4) "value4"
myRedis:2>hkeys myHash # 获取所有key
1) "key2"
2) "key4"
myRedis:2>hvals myHash #获取所有value
1) "value2"
2) "value4"
myRedis:2>
⑥ 指定增量
localhost:2>hset myHash key 5 # 设置一个初始值
"1"
localhost:2>hgetall myHash # 检查
1) "key"
2) "5"
localhost:2>hincrby myHash key 2 #增量 指定为2
"7"
localhost:2>hgetall myHash # 检查
1) "key"
2) "7"
localhost:2>hincrby myHash key 2 #再次增量 指定为2
"9"
localhost:2>hgetall myHash # 检查
1) "key"
2) "9"
localhost:2>hincrby myHash key -1 #可以指定为负值,相当于减去
"8"
localhost:2>
5.Zset类型 (有序集合)
Zset 实际上是另外一种set 其中存入的数据也不允许重复,但是在此基础上增加了一个排序.
可以用来做一些排序功能
Zset的命令一般都是以 z 字母开头的
结构图:
①普通存入,读取
myRedis:2>zadd age 12 chen # 存入 : zadd 键名 权重 值
"1"
myRedis:2>zadd age 22 wang # 存入 : zadd 键名 权重 值
"1"
myRedis:2>zadd age 10 li # 存入 : zadd 键名 权重 值
"1"
myRedis:2>zadd age 35 ma # 存入 : zadd 键名 权重 值
"1"
myRedis:2>zrange age 0 -1 #打印,默认存储结构为升序
1) "li"
2) "chen"
3) "wang"
4) "ma"
myRedis:2>
②排序
myRedis:2>zrange age 0 -1 # 默认打印的是升序
1) "li"
2) "chen"
3) "wang"
4) "ma"
myRedis:2>ZREVRANGE age 0 -1 #ZREVRANGE 命令是降序
1) "ma"
2) "wang"
3) "chen"
4) "li"
③ 排序+操作
myRedis:2>zrange age 0 -1 # 普通排序,升序
1) "li"
2) "chen"
3) "wang"
4) "ma"
myRedis:2>ZRANGEBYSCORE age -inf +inf withscores # 升序排序,但是指定查询区间 ,如果不知道区间,就取 -inf +inf 取从最大到最小, withscores表示显示权重
1) "li"
2) "10"
3) "chen"
4) "12"
5) "wang"
6) "22"
7) "ma"
8) "35"
myRedis:2>ZRANGEBYSCORE age 10 30 withscores # 升序排序,查询权重在 10到30之间的.
1) "li"
2) "10"
3) "chen"
4) "12"
5) "wang"
6) "22"
myRedis:2>ZRANGEBYSCORE age -inf 20 withscores #搭配使用,查出所有权重小于20的
1) "li"
2) "10"
3) "chen"
4) "12"
查询 满足区间内的元素 数量
myRedis:2>ZRANGEBYSCORE age -inf +inf withscores #打印显示权重
1) "li"
2) "10"
3) "chen"
4) "12"
5) "wang"
6) "22"
7) "ma"
8) "35"
myRedis:2>zcount age 10 30 # 查询 10~30区间内的元素数量
"3"
myRedis:2>zcount age 10 20 # 查询 10~20区间内的元素数量
"2"
myRedis:2>zcount age 40 60 # 查询 40~60区间内的元素数量
"0"
myRedis:2>
④移除元素
myRedis:2>zrange age 0 -1 #打印
1) "li"
2) "chen"
3) "wang"
4) "ma"
myRedis:2>zrem age li #移除一个元素
"1"
myRedis:2>zrange age 0 -1 #打印
1) "chen"
2) "wang"
3) "ma"
myRedis:2>zcard age #元素个数检测
"3"
myRedis:2>
三大特殊数据类型
Geospatial 地理位置
事先存入redis 经纬度信息,reids可以直接算出需要的数据
结构图
一般命令是以 geo 开头的
① 添加地点经纬度 - geoadd
规则:两级(南极北极)无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
有效的经度从-180度到180度。 有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回一个错误。
http://www.jsons.cn/lngcode/ 查询经纬度
myRedis:2>geoadd china:city 113.66 34.75 zhenzhou # 添加地点 键 经纬度 地点名
"1"
myRedis:2>geoadd china:city 114.34 34.797 kaifeng
"1"
myRedis:2>geoadd china:city 120.15 38.28 hangzou
"1"
myRedis:2>geoadd china:city 114.64 33.62 zoukou
"1"
myRedis:2>
② 获取经纬度 - geopos
myRedis:2>geopos china:city zhenzhou # 获取经纬度
1) 1) "113.65999907255173"
2) "34.749999265106908"
myRedis:2>geopos china:city kaifeng
1) 1) "114.33999806642532"
2) "34.797000599564534"
myRedis:2>geopos china:city zhoukou
1) null
myRedis:2>geopos china:city zoukou
1) 1) "114.63999778032303"
2) "33.620000294504329"
myRedis:2>
③计算两点之间的距离 - geodist
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
myRedis:2>geodist china:city kaifeng zoukou km # 计算两地之间的距离
"133.7899"
myRedis:2>geodist china:city kaifeng zoukou m
"133789.9169"
myRedis:2>geodist china:city kaifeng zoukou mi
"83.1334"
myRedis:2>geodist china:city kaifeng zoukou ft
"438943.2969"
myRedis:2>
④ 查出某地 某个距离范围内的所有地点 - GEORADIUSBYMEMBER
myRedis:2>GEORADIUSBYMEMBER china:city zoukou 1000 km
1) "zoukou"
2) "zhenzhou"
3) "kaifeng"
4) "beijing"
5) "hangzou"
myRedis:2>GEORADIUSBYMEMBER china:city zoukou 500 km
1) "zoukou"
2) "zhenzhou"
3) "kaifeng"
myRedis:2>
Hyperloglog 基数
什么是基数
A {1,3,5,7,8,7}
B{1,3,5,7,8}
A、B集合的基数(不重复的元素) = 5,可以接受误差!
优点:占用的内存是固定,2^64 不同的元素的技术,只需要废 12KB内存!如果要从内存角度来比较的话 Hyperloglog 首选!
网页的 UV (一个人访问一个网站多次,但是还是算作一个人!)
传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 !
这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
0.81% 错误率! 统计UV任务,可以忽略不计的!
一般命令以 pf 开头
需要注意的是:基数求得是不相同的元素的个数 。 相当于去重后取个数
① 存入 , 单个计算基数 , 组合计算基数
将两个组合到一块之后,就会生成一个新的合并之后的组合
localhost:2>pfadd key1 a b c d e # 存入数据
"1"
localhost:2>pfcount key1 # 计算基数个数
"5"
localhost:2>pfadd key2 d e f f # 存入数据
"1"
localhost:2>pfcount key2 # 计算基数个数
"3"
localhost:2>pfmerge key3 key1 key2 #合并组合,计算基数
"OK"
localhost:2>pfcount key3
"6"
localhost:2>
生成了新的组合:
Bitmap 位存储
位存储 是一种位图的数据结构,都是操作二进制进行记录。就只有 0 和 1 两种状态
它因为只存储 0 1 因此占有内存极小
适合用来处理只有两个状态的情况。例如 打卡/未打卡 ,登录/未登录 ,活跃/不活跃
① 存入
记录周一到周五的打卡情况
localhost:2>setbit flag 1 0 # 周一 未打卡
"0"
localhost:2>setbit flag 2 0 # 周二 未打卡
"0"
localhost:2>setbit flag 3 1 # 周三 打卡
"0"
localhost:2>setbit flag 4 1 # 周四 打卡
"0"
localhost:2>setbit flag 5 1 # 周五 打卡
"0"
localhost:2>
②检查某一天的状态
localhost:2>getbit flag 3 # 获取周三打卡情况
"1"
localhost:2>getbit flag 1 # 获取周一打卡情况
"0"
localhost:2>
③统计 状态为 1 的数量
localhost:2>bitcount flag #统计 状态为 1 的数量
"3"
localhost:2>
事务
redis的单条命令是保证原子性的,但是多条命令组成的事务不保证原子性
开启事务
顺序
- 开启事务(multi)
- 命令入队 (…)
- 执行事务 (exec)
localhost:2>keys * #打印
localhost:2>multi #开启事务
"OK"
localhost:2>set k1 v1 #命令入队(正常输入操作命令)
"QUEUED"
localhost:2>set k2 v2 #命令入队(正常输入操作命令)
"QUEUED"
localhost:2>get k2 #命令入队(正常输入操作命令)
"QUEUED"
localhost:2>exec #执行事务,执行命令才会开启事务
1) "OK"
2) "OK"
3) "OK"
4) "OK"
5) "OK"
6) "v2"
7) "OK"
localhost:2>
- 取消事务 (discard)
localhost:2>multi # 开始事务
"OK"
localhost:2>set k3 v3
"QUEUED"
localhost:2>set k4 v4
"QUEUED"
localhost:2>discard # 取消事务的编写
"OK"
localhost:2>get k3 #打印
null
localhost:2>
异常:
- 编译型异常,(代码有问题,输入命令就是错误的),事务中所有的命令都不会执行
localhost:2>multi # 开启事务
"OK"
localhost:2>set k3 v3
"QUEUED"
localhost:2>set k4 v4
"QUEUED"
localhost:2>getset #输入错误的命令
"ERR wrong number of arguments for 'getset' command"
localhost:2>get k4 v4
"ERR wrong number of arguments for 'get' command"
localhost:2>set k5 v5
"QUEUED"
localhost:2>exec #事务也开启失败
"EXECABORT Transaction discarded because of previous errors."
localhost:2>
- 运行时异常,如果事务中,多个命令中有个别出现运行异常,那么此条错误语句会报错抛出异常,其他语句还是会正常运行 (redis中事务不保证原子性)
localhost:2>set k1 "v1" # 先创建一个值
"OK"
localhost:2>multi #开启事务
"OK"
localhost:2>incr k1 #对字符串进行 加以操作,会导致运行时错误
"QUEUED"
localhost:2>set k2 v2 #进行输入正确的命令入队
"QUEUED"
localhost:2>get k2
"QUEUED"
localhost:2>exec #执行
1) "OK"
2) "ERR value is not an integer or out of range" # 错误的语句提示 抛出异常
3) "OK" #后续其他正确的命令依旧执行
4) "OK" #后续其他正确的命令依旧执行
5) "OK" #后续其他正确的命令依旧执行
6) "v2" #后续其他正确的命令依旧执行
7) "OK" #后续其他正确的命令依旧执行
localhost:2>
redis的锁
乐观锁
很乐观,认为什么时候都不会出现问题,只在更新的时候去判断一下,在此期间有没有人懂这条数据,一般是通过 version 字段来处理
先获取version
在啊更新时判断version 是否一致 在redis中,使用 watch 字段来监视字段,实现事务。
正常流程
localhost:2>set money 100 # 余额设为 100
"OK"
localhost:2>set out 0 #花费设为0
"OK"
localhost:2>watch monry # 乐观锁,进行watch 监视
"OK"
localhost:2>multi #开启事务
"OK"
localhost:2>decrby money 20
"QUEUED"
localhost:2>incrby out 20
"QUEUED"
localhost:2>exec # 执行事务,此时没有其他线程来打扰,因此流程进行正常
1) "OK"
2) "80"
3) "OK"
4) "20"
5) "OK"
localhost:2>get money
"80"
localhost:2>get out
"20"
localhost:2>
多线程扰乱情况
localhost:2>watch money #正常流程,先不执行事务
"OK"
localhost:2>multi
"OK"
localhost:2>decrby money 20
"QUEUED"
localhost:2>incrby out 20
"QUEUED"
localhost:2>get money #在另一个线程对money 进行修改值
"100"
localhost:2>decrby money 50
"50"
localhost:2>
localhost:2>watch money
"OK"
localhost:2>multi
"OK"
localhost:2>decrby money 20
"QUEUED"
localhost:2>incrby out 20
"QUEUED"
localhost:2>exec #线程执行后没有任何返回,证明事务提交失败
localhost:2>exec
"ERR EXEC without MULTI"
localhost:2>
如果遇见事务提交失败,需要再次提交时,需要先放弃当前字段监视,重新获取该字段监视才可以。
localhost:2>unwatch money # 放弃当前监视
"OK"
localhost:2>watch money #从新监视
"OK"
悲观锁
很悲观,认为任何情况下都会出现问题,都会加锁。会导致性能低下。
redis 不使用悲观锁,在此不研究。
conf配置文件
1. 单位控制
# 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. #大小写不敏感
配置文件 unit 单位对大小写不敏感
2.包含/引入 其他文件
相当于Java中的 import
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include .\path\to\local.conf # 引入其他配置文件
# include c:\path\to\other.conf # 引入其他配置文件
3。网络配置
bind 127.0.0.1 #IP
port 6379 #端口
protected-mode yes #保护模式 默认yes 开启
4.通用配置
daemonize no #以守护进程的方式允许。默认是no,需要我们自己手动开启为 yes
pidfile /var/run/redis.pid # 如果以后台方式允许,就得指定一个文件
## 日志
# 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)
loglevel notice
# Specify the log file name. Also 'stdout' can be used to force
# Redis to log on the standard output.
logfile "" #指定生成文件的位置名
databases 16 #默认数据库的数量
5.快照
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb .aof
redis是内存数据库,如果不持久化,那么数据断电就会丢失
# 如果900s内,如果至少有一个key进行了修改,就进行持久化操作
save 900 1
#如果30s内,如果至少有10个key进行了修改,就进行持久化操作
save 300 10
#如果60s内,如果至少有10000个key进行了修改,就进行持久化操作
save 60 10000
# 一般实际应用时,会自己配置这个时间频率
#持久化如果出错,是否还需要继续工作
stop-writes-on-bgsave-error yes
#是否压缩 rdb 文件,需要消耗一些cpu资源
rdbcompression yes
#保存rdb文件时 进行错误的校验检查
rdbchecksum yes
# rdb文件的保存位置
dir ./
6.主从复制
在这里插入代码片
7.安全
# 设置密码 默认为空
# requirepass foobared
#设置密码 为 123456
requirepass 123456
8.限制
9.aof模式
appendonly no #默认关闭,默认是使用 rdb 方式来持久化的
Redis 持久化
RDB (Redis DataBase)
什么是 RDB
工作原理:
保存文件
触发条件
- save规则满足的情况下,会自动触发rdb规则
- 执行 flushdb 命令,也会触发rdb规则
- 退出redis,也会产生 rdb 文件
触发之后,都会自动生成一个 dump.rdb 文件
如何将rdb文件恢复为内存数据
- 只需要将rdb文件放在redis启动目录即可,redis启动时会自动检测 dump.rdb 恢复其中的数据
- 查看需要存放的位置的命令
myRedis:1>config get dir
1) "dir"
2) "D:\Program Files\Redis"
myRedis:1>
优点与缺点:
AOF (Append Only File)
什么是 AOF
将我们执行的所有命令都记录下来,history,恢复的时候把这个文件都执行一遍。
ps.默认是不开启使用 aof 的,需要自己手动修改配置文件来开启
工作原理
保存文件
触发条件
每进行一个命令的输入,就会追加保存到文件中。
优点与缺点
ps. AOF模式有校验机制,如果aof文件有错误,是redis启动不起来的,需要使用redis提供的修复工具来修复
redis -check-aof --fix appendonly.aof
扩展总结
Redis订阅发布
订阅频道
SUBSCRIBE
localhost:2>SUBSCRIBE chenchong # 订阅一个频道,频道不需要提前创建,订阅即可
切换到推送/订阅模式,关闭标签页来停止接收信息。 # 提示消息
1) "subscribe" #正在监听中
2) "chenchong"
3) "1"
频道发布消息
PUBLISH
localhost:2>PUBLISH chenchong hello,chen # 发布频道消息
"1" #代表有几个订阅者收到消息
localhost:2>
localhost:2>SUBSCRIBE chenchong
切换到推送/订阅模式,关闭标签页来停止接收信息。
1) "subscribe"
2) "chenchong"
3) "1"
1) "message" # 收到消息 提示
2) "chenchong" # 频道信息
3) "hello,chen" #信息内容
订阅多个频道
PSUBSCRIBE
localhost:2>PSUBSCRIBE chen chong # 同时订阅 chen 与 chong 频道
切换到推送/订阅模式,关闭标签页来停止接收信息。
1) "psubscribe"
2) "chen"
3) "1"
localhost:2>PUBLISH chen hello,123 # chen频道发布消息
"1"
localhost:2>PUBLISH chong hello,123 # chong频道发布消息
"1"
localhost:2>
localhost:2>PSUBSCRIBE chen chong
切换到推送/订阅模式,关闭标签页来停止接收信息。
1) "psubscribe"
2) "chen"
3) "1"
1) "pmessage" # 接受到 chen 频道的消息
2) "chen"
3) "chen"
4) "hello,123"
1) "pmessage" # 接受到 chong 频道的消息
2) "chong"
3) "chong"
4) "hello,123"
退订频道
PUNSUBSCRIBE
localhost:2>PUNSUBSCRIBE chenchong # 推定指定频道
1) "punsubscribe"
2) "chenchong"
3) "0"
localhost:2>PUBLISH chenchong hello,chen
"1"
localhost:2>PUBLISH chenchong hello,redis
"1"
localhost:2>PUBLISH chenchong hello,123 #频道继续发布,但是未订阅不会收到消息
"0"
localhost:2>
localhost:2>PUNSUBSCRIBE chenchong
1) "punsubscribe"
2) "chenchong"
3) "0"
localhost:2>
原理
Redis 主从复制
概念
为什么使用
环境配置
只配置主从库,不用配置主库(因为redis都默认自己是主库,所有只需要将当作从库的配置一下)
查看本机信息:info replication
localhost:2>info replication #查看当前库的信息
"# Replication
role:master # 角色 master (主库)
connected_slaves:0 #从机连接数
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
"
localhost:2>
到此,三台redis服务已经启动,开始配置 一主二从 的配置
配置从机
将主机修改为 认某一个主机为上位,则自己就变为从机。
SLAVEOF
localhost:2>SLAVEOF 127.0.0.1 6379 #设定 本机的 6379 端口的服务为主机,主机为从机
再次检查自己的信息,已经变为从机
1.这种命令行的形式配置的是临时的,如果从机断掉再启动,会自动变成主机。
2.要想永久生效得在配置文件中配置
3.主机才可以读写操作,从机只有读操作
从机复制数据原理
哨兵模式
概述
配置
与上面主从复制一样,也是新建一个配置文件,填写内容:
优点与缺点
哨兵模式的全部配置
# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 26379
# 哨兵sentinel的工作目录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
# 当在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无法正常启动成功。
#通知脚本
# 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
Redis 缓存穿透、击穿、雪崩
缓存穿透 (在数据库查不到,无法存入缓存,导致每次都去数据库查)
概念
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
解决方案
布隆过滤器
直接缓存为空对象
一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。
这样做有一个缺陷:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间
即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿 (穿透的特例,缓存过期,同时有大量访问请求该值。热点数据)
概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。
解决方案
设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。当线程访问结束后,缓存即生成,就不会对数据库造成过大压力。这样对锁的要求就十分高。
缓存雪崩 (大量key同一时间过期)
概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案
- redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
- 限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
- 数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
内存过期、淘汰机制
过期策略 (定期检测删除+惰性删除)
redis中的键被设置了过期时间,当到了过期时间mredis会将其删除,这个删除的策略就是过期策略.
- 定期检测删除
指Redis默认每隔 一定时间 就 随机抽取 一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删除。
而这个 一定时间+一些key 都是可以在配置文件 redis.conf 中设置的。
- 惰性删除
因为定期检测删除是检测一部分,这样就会造成有些key已经过期失效了,但是因为没有被检测所有仍然存在。因此就引入惰性删除。
惰性删除不是主动删除,是被动的在有请求访问这些key的时候,先去检测key是否过期失效,如果没有过期,就把key返回;如果过期就删除掉这个key,不会返回给你。
缺点:定期检测删除+惰性删除 的复合机制能够保证这个过期的key最终肯定会被删除,但是不保证时效性。如果这个key过期了,一直没有请求它,并且没有被定期删除掉,那么它还会大量占据内存。最后导致内存耗尽。
为了解决上面的问题,引入内存淘汰机制
内存淘汰机制
内存淘汰机制就是在redis内存占用量达到一定值时,去按照某种策略去进行内存淘汰(也就是删除一部分key),以此保证redis的内存占用率不会过高。
而这个 一定值+某种策略 也可以在redis.conf配置文件中进行配置
* 淘汰机制的执行时机以及过程
* 1.当有操作需要向redis中新增数据时。
* 2.redis会去检查当前redisn内存使用情况,如果当前内存超过了设定值,就会按照设定好的策略进行删除一些key。
* 3.然后再执行新增新数据的操作。
常见八种内存淘汰机制配置:
* ①no-eviction:当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,(一般不采用)。
*
* ②allkeys-lru:当内存不足以容纳新写入数据时,移除 ’最近+最少' 使用的key,(这个是最常用的)
* ③allkeys-random:当内存不足以容纳新写入的数据时,随机移除key。
* ④allkeys-lfu:当内存不足以容纳新写入数据时,移除 '最不经常(最少,但未必是最近的)'使用的key
*
* 下面开始,移除key都是在设定了过期时间的key中操作。上面是在所有的key中操作。
* ⑤volatile-lru:当内存不足以容纳新写入数据时,在'设置了过期时间的key'中,移除'最近最少使用'的key。
* ⑥volatile-random:内存不足以容纳新写入数据时,在设置了过期时间的key中,随机移除某个key 。
* ⑦volatile-lfu:当内存不足以容纳新写入数据时,在设置了过期时间的key中,移除最不经常(最少)使用的key
* ⑧volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,优先移除过期时间最早(剩余存活时间最短)的key。
*
* 过期策略与内存淘汰机制是相辅相成的。