redis入门教程

[root@localhost redis-5.0.8]# redis-cli -p 6379  #使用redis客户端进行连接
127.0.0.1:6379> auth root #用密码登录
OK
127.0.0.1:6379> ping #看是否能连接
PONG
127.0.0.1:6379> set name zhangsan #往数据库中存放键值
OK
127.0.0.1:6379> get name #根据键获取值
"zhangsan"
127.0.0.1:6379> keys * #查看当前数据库中的所有key
1) "name"
2) "k2"
3) "k1"
127.0.0.1:6379> 

退出redis

27.0.0.1:6379> shutdown  #关闭redis
not connected> exit #退出redis
[root@localhost redis-5.0.8]# 

启动redis服务

[root@localhost redis-5.0.8]# systemctl restart redis_6379.service

测试并发

# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

基础的知识

redis默认有16个数据库

默认使用的是第0个数据库

当我们需要切换数据库是,可以使用select

127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> dbsize #查看数据库大小
(integer) 0
127.0.0.1:6379[3]>

操作数据库

127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name mmm
OK
127.0.0.1:6379[3]> get name
"mmm"
127.0.0.1:6379[3]> select 13
OK
127.0.0.1:6379[13]> get name
(nil)
127.0.0.1:6379[13]> select 3
OK
127.0.0.1:6379[3]> get name
"mmm"
127.0.0.1:6379[3]>

清除当前数据库

127.0.0.1:6379[3]> keys * #查看当前数据库的所有key
1) "name"
127.0.0.1:6379[3]> flushdb #清空当前数据库的数据
OK
127.0.0.1:6379[3]> keys *
(empty list or set)
127.0.0.1:6379[3]> 

清除全部数据库的内容

127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "name"
127.0.0.1:6379> select 11
OK
127.0.0.1:6379[11]> keys *
1) "name"
127.0.0.1:6379[11]> flushall
OK
127.0.0.1:6379[11]> keys *
(empty list or set)
127.0.0.1:6379[11]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> 

redis是单线程的,为什么还这么快

核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,
(多线程CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!

1.五大数据类型

String(字符串)

127.0.0.1:6379> set k1 v1 #设置值
OK
127.0.0.1:6379> get k1 #获得值
"v1"
127.0.0.1:6379> keys * #获得所有的key
1) "k1"
127.0.0.1:6379> exists k1 #判断一个key是否存在 0不存在 1存在
(integer) 1
127.0.0.1:6379> append k1 hello # 追加字符串,如果当前key不存在,相当于set key
(integer) 7
127.0.0.1:6379> get k1
"v1hello"
127.0.0.1:6379> strlen k1 #获取字符串的长度
(integer) 7
127.0.0.1:6379> append k1 world
(integer) 12
127.0.0.1:6379> get k1
"v1helloworld"
127.0.0.1:6379> strlen k1
(integer) 12
127.0.0.1:6379>
#############################################################################
# 自增自减操作
# 设置步长
127.0.0.1:6379> set goods 0 # 设置goods初始为0
OK
127.0.0.1:6379> get goods
"0"
127.0.0.1:6379> incr goods # 自增1 数量+1
(integer) 1
127.0.0.1:6379> incr goods
(integer) 2
127.0.0.1:6379> decr goods # 自减1 数量-1
(integer) 1
127.0.0.1:6379> decr goods
(integer) 0
127.0.0.1:6379> decr goods
(integer) -1
127.0.0.1:6379> incrby goods 10 #可以设置增加的多少 指定增量
(integer) 9
127.0.0.1:6379> incrby goods 10
(integer) 19
127.0.0.1:6379> decrby goods 5
(integer) 14
127.0.0.1:6379>
##########################################################################
# 字符串范围 range
127.0.0.1:6379> set k1 "hello,word"  # 设置k1的值
OK
127.0.0.1:6379> get k1
"hello,word"
127.0.0.1:6379> getrange k1 0 3 #截取字符串[0-3]
"hell"
127.0.0.1:6379> getrange k1 0 -1 #获取全部的字符串 和get k1是一样的效果
"hello,word"#替换
127.0.0.1:6379> set k2 nihao,shijie
OK
127.0.0.1:6379> get k2
"nihao,shijie"
127.0.0.1:6379> setrange k2 6 world #替换指定位置开始的字符串 即替换k2 从第六位开始
(integer) 12
127.0.0.1:6379> get k2
"nihao,worlde"
127.0.0.1:6379> ##########################################################################
# setex (set with expire) # 设置过期时间
# setnx (set if not exist) # 不存在在设置 (在分布式锁中会常常使用!)127.0.0.1:6379> setex key3 30 "hello" # 设置key3 的值为 hello,30秒后过期
OK
127.0.0.1:6379> ttl key3  #查看key3的ttl
(integer) 25
127.0.0.1:6379> ttl key3
(integer) 16
127.0.0.1:6379> get key3  #没过期时能够查到key3
"hello"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> get key3 #过期后就查不到key3
(nil)
127.0.0.1:6379> setnx k4 zhangsan #如果k4不存在,创建k4
(integer) 1
OK
127.0.0.1:6379> keys *
1) "k4"
2) "k1"
127.0.0.1:6379> setnx k1 v1 #如果k1不存在,创建k1,如果存在创建失败
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> get k1
"hhhh"
127.0.0.1:6379>
##########################################################################
# mset  批量设置
# mget  批量获取
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  #同时设置多个键值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3  #同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4  # msetnx 是一个原子性的操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
##########################################################################
# 对象设置
127.0.0.1:6379> set user:1 {name:zhagnsan,age:3} # 设置一个user:1 对象 值为 json字符来保存一个对象!
OK
127.0.0.1:6379> get user:1
"{name:zhagnsan,age:3}"
127.0.0.1:6379> 127.0.0.1:6379> mset user:1:name lisi user:1:age 18
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lisi"
2) "18"
127.0.0.1:6379>
########################################################################## getset # 先get然后在set
127.0.0.1:6379> getset rdb redisdatabase #如果不存在值,则返回nil
(nil)
127.0.0.1:6379> get rdb #已经设置值
"redisdatabase"
127.0.0.1:6379> getset rdb redis #如果存在值,返回原来的值,并设置新的值
"redisdatabase"
127.0.0.1:6379> get rdb #已经是新的值
"redis"
127.0.0.1:6379>

String类似的使用场景:value除了字符串还可以是数字!

计数器
统计多单位的数量
粉丝数
对象缓存存储!

List(列表)

# lpush
# rpush
127.0.0.1:6379> lpush list 1  #将一个值或者多个值,插入到列表的头部(左边)
(integer) 1
127.0.0.1:6379> lpush list 2
(integer) 2
127.0.0.1:6379> lpush list 3
(integer) 3
127.0.0.1:6379> lrange list 0 -1 #获取list中的值
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> lrange list 0 1  #通过区间获取具体的值
1) "3"
2) "2"
127.0.0.1:6379> rpush list 4 #将一个值或者多个值,插入到列表尾部(youb)
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
127.0.0.1:6379>
##########################################################################
# LPOP
# RPOP
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
127.0.0.1:6379> lpop list #移除list的第一个元素(左)
"3"
127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
3) "4"
127.0.0.1:6379> rpop list #移除list的最后一个元素(右)
"4"
127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
127.0.0.1:6379>
#########################################################################
# Lindex
127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
127.0.0.1:6379> lindex list 1 #通过下标获得list中的某一个值
"1"
127.0.0.1:6379> lindex list 0
"2"
127.0.0.1:6379>
#########################################################################
# Llen127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
127.0.0.1:6379> lpush list 3
(integer) 3
127.0.0.1:6379> lpush list 4
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> llen list  # 返回列表的长度
(integer) 4
127.0.0.1:6379>
##########################################################################
移除多少个指定的值!
127.0.0.1:6379> lrange list 0 -1
1) "4"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lrem list 1 4 #移除list集合中1个4(移除集合中指定个数的value)
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lrem list 1 3  #移除list集合中1个3(移除集合中指定个数的value)
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "4"
2) "2"
3) "1"
127.0.0.1:6379>
##########################################################################
# trim 修剪
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist hello1
(integer) 2
127.0.0.1:6379> rpush mylist hello2
(integer) 3
127.0.0.1:6379> rpush mylist hello3
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,这个list已经被改变了,截断了 只剩下截取的元素!
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> ########################################################################## rpoplpush # 移除列表的最后一个元素,将他移动到新的列表中!127.0.0.1:6379> rpush mylist hello
(integer) 1
127.0.0.1:6379> rpush mylist hello1
(integer) 2
127.0.0.1:6379> rpush mylist hello2
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
127.0.0.1:6379> rpoplpush mylist otherlist # 移除mylist列表的最后一个元素(hello2),并将他移动到otherlist的列表中!
"hello2"
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello2"
127.0.0.1:6379>
##########################################################################
# lset 将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379> exists list # 判断这个列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 aaa  #设置列表的第0位的值为aaa, 如果不存在列表,我们去更新就会报错
(error) ERR no such key
127.0.0.1:6379> lpush list aaa
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "aaa"
127.0.0.1:6379> lset list 0 bbb  #设置列表的第0位的值为bbb,列表存在第0位,可以更新
OK
127.0.0.1:6379> lrange list 0 -1
1) "bbb"
127.0.0.1:6379> lset list 1 ccc #设置列表的第0位的值为bbb,列表不存在第1位,不可以更新
(error) ERR index out of range
127.0.0.1:6379>
#########################################################################
# linsert # 将某个具体的value插入到列把你中某个元素的前面或者后面!
127.0.0.1:6379> rpush mylist hello
(integer) 1
127.0.0.1:6379> rpush mylist world
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
127.0.0.1:6379> linsert mylist before world java
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "java"
3) "world"
127.0.0.1:6379> linsert mylist after world jump
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "java"
3) "world"
4) "jump"
127.0.0.1:6379> 
1.list实际上是一个链表,可以在头尾插入数据,也可以在中间插入数据
2.如果key不存在,创建新的链表
3.如果key不存在,新增内容
4.如果移除了所有值,空链表,也代表不存在
5.在两边插入或者改动值,效率最高,在中间插入元素,相对来说效率会低一点
作用:消息排队队列(先进先出 lpush rpop) 栈(先进后出 lpush lpop)

Set(集合)

set 无序不重复集合。

127.0.0.1:6379> sadd myset aaa # set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset bbb
(integer) 1
127.0.0.1:6379> sadd myset ccc
(integer) 1
127.0.0.1:6379> smembers myset # 查看指定set集合的所有值
1) "ccc"
2) "bbb"
3) "aaa"
127.0.0.1:6379> sismember myset aaa # 判断某一个值是不是在set集合中! 返回1代表存在
(integer) 1
127.0.0.1:6379> sismember myset fff # 判断某一个值是不是在set集合中! 返回0代表不存在
(integer) 0
127.0.0.1:6379>
##########################################################################
# scard  获取set集合的元素个数
127.0.0.1:6379> scard myset # 获取set集合中的内容元素个数!
(integer) 3
##########################################################################
srem
127.0.0.1:6379> smembers myset
1) "ccc"
2) "bbb"
3) "aaa"
127.0.0.1:6379> srem myset aaa # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "ccc"
2) "bbb"
127.0.0.1:6379>
##########################################################################
# 随机抽选出指定个数的元素
127.0.0.1:6379> smembers myset
1) "ddd"
2) "ccc"
3) "bbb"
4) "eee"
5) "fff"
127.0.0.1:6379> srandmember myset # myset集合中随机抽选出一个元素
"bbb"
127.0.0.1:6379> srandmember myset
"ddd"
127.0.0.1:6379> srandmember myset
"eee"
127.0.0.1:6379> srandmember myset
"ccc"
127.0.0.1:6379> srandmember myset 2  # myset集合中随机抽选出一个元素
1) "ddd"
2) "bbb"
127.0.0.1:6379> srandmember myset 2
1) "ccc"
2) "ddd"
127.0.0.1:6379>
##########################################################################
# 删除指定的key,
# 随机删除key!
127.0.0.1:6379> smembers myset
1) "fff"
2) "eee"
3) "ccc"
4) "ddd"
5) "bbb"
127.0.0.1:6379> spop myset
"eee"
127.0.0.1:6379> spop myset
"fff"
127.0.0.1:6379> smembers myset
1) "ccc"
2) "ddd"
3) "bbb"
127.0.0.1:6379>
#########################################################################
# 将一个指定的值,移动到另外一个set集合!
127.0.0.1:6379> smembers myset
1) "ccc"
2) "ddd"
3) "bbb"
127.0.0.1:6379> sadd myset2 set2_1
(integer) 1
127.0.0.1:6379> smove myset myset2 ccc #将myset集合的ccc元素移动到myset2集合中
(integer) 1
127.0.0.1:6379> smembers myset
1) "ddd"
2) "bbb"
127.0.0.1:6379> smembers myset2
1) "ccc"
2) "set2_1"
127.0.0.1:6379> ##########################################################################
# 作用:网站的共同关注!(并集)
# 数字集合类: # - 差集 sdiff# - 交集 sinter # - 并集 sunion
127.0.0.1:6379> sadd set1 a
(integer) 1
127.0.0.1:6379> sadd set1 b
(integer) 1
127.0.0.1:6379> sadd set1 c
(integer) 1
127.0.0.1:6379> sadd set1 d
(integer) 1
127.0.0.1:6379> smembers set1
1) "b"
2) "a"
3) "d"
4) "c"
127.0.0.1:6379> sadd set c
(integer) 1
127.0.0.1:6379> sadd set d
(integer) 1
127.0.0.1:6379> sadd set d
(integer) 0
127.0.0.1:6379> sadd set f
(integer) 1
127.0.0.1:6379> smembers set
1) "f"
2) "d"
3) "c"
127.0.0.1:6379> sdiff set set1 #set和set1集合的差集
1) "f"
127.0.0.1:6379> sinter set set1 #set和set1集合的交集
1) "d"
2) "c"
127.0.0.1:6379> sunion set set1  #set和set1集合的并集
1) "c"
2) "d"
3) "f"
4) "b"
5) "a"

Hash(哈希)

map集合,值是一个map集合(每一个值都是一个键值对)

127.0.0.1:6379> hset myhash field1 value1  #设置值
(integer) 1
127.0.0.1:6379> hget myhash field1 #获取一个字段值
"value1"
127.0.0.1:6379> hmset myhash field1 hello field2 world   # 设置key-value
OK
127.0.0.1:6379> hmget myhash field1 field2  # 获取集合中多个字段值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash # 获取集合全部的数据
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel myhash field1  #删除hash指定集合的key字段!对应的value值会被删除
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
127.0.0.1:6379>
#########################################################################
# hlen
127.0.0.1:6379> hmset myhash field1 value1 field2 value2
OK
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "value1"
3) "field2"
4) "value2"
127.0.0.1:6379> hlen myhash # 获取hash集合的字段数量
(integer) 2
##########################################################################
127.0.0.1:6379> hexists myhash field1 # 判断hash中指定字段是否存在!
(integer) 1
127.0.0.1:6379> hexists myhash field3
(integer) 0
##########################################################################
# hkeys 只获得所有field
# hvals 只获得所有value
127.0.0.1:6379> hkeys myhash
1) "field1"
2) "field2"
127.0.0.1:6379> hvals myhash
1) "value1"
2) "value2"
127.0.0.1:6379>
########################################################################## incr decr
127.0.0.1:6379> hset myhash field3 5 #指定增量!
(integer) 1
127.0.0.1:6379> hincrby myhash field3 2
(integer) 7
127.0.0.1:6379> hincrby myhash field3 -2
(integer) 5
127.0.0.1:6379> hsetnx myhash field4 value4 # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 hello # 如果存在则不能设置
(integer) 0
127.0.0.1:6379> hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息! hash 更适合于对象的
存储,String更加适合字符串存储!

Zset(有序集合)

127.0.0.1:6379> zadd myzset 100 a #给myzset集合添加一个元素
(integer) 1
127.0.0.1:6379> zadd myzset 200 b
(integer) 1
127.0.0.1:6379> zadd myzset 300 c
(integer) 1
127.0.0.1:6379> zrange myzset 0 -1  #查看集合的所有元素
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> zadd myzsest 400 e 500 f #给myzsest集合添加多个元素
(integer) 2
127.0.0.1:6379> zrange myzsest 0 -1
1) "e"
2) "f"
127.0.0.1:6379>
##########################################################################
# 利用zset对集合元素进行排序
127.0.0.1:6379> zadd money 7000 xiaohong
(integer) 1
127.0.0.1:6379> zadd money 5000 xiaoli
(integer) 1
127.0.0.1:6379> zadd money 4000 lily
(integer) 1
127.0.0.1:6379> zadd money 10000 xiaolan
(integer) 1
127.0.0.1:6379> zrange money 0 -1
1) "lily"
2) "xiaoli"
3) "xiaohong"
4) "xiaolan"
127.0.0.1:6379> zrangebyscore money -inf +inf #对集合元素的值进行从小到大排序
1) "lily"
2) "xiaoli"
3) "xiaohong"
4) "xiaolan"
127.0.0.1:6379> zrangebyscore money -inf +inf withscores #对集合元素的值进行从小到大排序并带上值
1) "lily"
2) "4000"
3) "xiaoli"
4) "5000"
5) "xiaohong"
6) "7000"
7) "xiaolan"
8) "10000"
127.0.0.1:6379> zrevrangebyscore money +inf -inf    #对集合元素的值进行从大到小排序
1) "xiaolan"
2) "xiaohong"
3) "xiaoli"
4) "lily"
127.0.0.1:6379> zrevrangebyscore money +inf -inf withscores #对集合元素的值进行从大到小排序并带上值
1) "xiaolan"
2) "10000"
3) "xiaohong"
4) "7000"
5) "xiaoli"
6) "5000"
7) "lily"
8) "4000"
127.0.0.1:6379> zrangebyscore money -inf 5000 withscores #对无穷小到5000的元素与值升序排序!
1) "lily"
2) "4000"
3) "xiaoli"
4) "5000"##########################################################################
#zrem 移除元素
#zcard  获取元素集合个数
127.0.0.1:6379> zrange money 0 -1
1) "lily"
2) "xiaoli"
3) "xiaohong"
4) "xiaolan"
127.0.0.1:6379>
127.0.0.1:6379> zrem money lily
(integer) 1
127.0.0.1:6379> zrange money 0 -1
1) "xiaoli"
2) "xiaohong"
3) "xiaolan"
127.0.0.1:6379> zrem money xiaohong # 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange money 0 -1
1) "xiaoli"
2) "xiaolan"
127.0.0.1:6379> zcard money # 获取有序集合中的个数
(integer) 2
##########################################################################
#zcount 区间的成员数量
127.0.0.1:6379> zadd myset 1 aaa
(integer) 1
127.0.0.1:6379> zadd myset 2 bbb 3 ccc
(integer) 2
127.0.0.1:6379> zcount myset 1 3 #获取指定区间的成员数量!
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2作用:
`set 排序 存储班级成绩表,工资表排序!
`普通消息,1, 重要消息 2,带权重进行判断!
`排行榜应用实现,取Top N

2.三种特殊类型

Geospatial 地理位置

#相关命令
geoadd
geodist
geohash
geopos
georadius
georadiusbymember
# getadd 添加地理位置
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误
127.0.0.1:6379> geoadd china:city 116.408 39.904 beijing #添加地理位置
(integer) 1
127.0.0.1:6379> geoadd china:city 121.445 31.213 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 113.265 23.108 guangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 114.109 22.544 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 112.967 28.197 changsha
(integer) 1
127.0.0.1:6379> geoadd china:city 118.07 24.445 xiamen
(integer) 1
127.0.0.1:6379> geopos china:city beijing #获取地理位置
1) 1) "116.40800267457962036"2) "39.90399988166036138"
127.0.0.1:6379> geopos china:city shanghai xiamen #获取多个地理位置
1) 1) "121.44499808549880981"2) "31.213001199663303"
2) 1) "118.07000130414962769"2) "24.44499914185266931"
127.0.0.1:6379> ##########################################################################
#geodist 两个位置之间的距离#单位:#m 表示单位为米。#km 表示单位为千米。#mi 表示单位为英里。#ft 表示单位为英尺。127.0.0.1:6379> geodist china:city beijing shanghai km # 查看上海到北京的直线距离 单位为km
"1068.2320"
127.0.0.1:6379> geodist china:city beijing xiamen km # 查看厦门到北京的直线距离 单位为km
"1726.4575"
##########################################################################
# georadius 以给定的经纬度为中心, 找出指定半径内的元素
# 应用:我附近的人 (获得所有附近的人的地址,定位!)通过半径来查询!
127.0.0.1:6379> georadius china:city 110 30 1000 km # 以110,30 这个经纬度为中心,寻找方圆1000km内的城市
1) "shenzhen"
2) "guangzhou"
3) "changsha"
127.0.0.1:6379> georadius china:city 110 30 500 km # 以110,30 这个经纬度为中心,寻找方圆500km内的城市
1) "changsha"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord ## 显示定位信息(经纬度)
1) 1) "changsha"2) 1) "112.96700209379196167"2) "28.1969998601948717"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 显示到中间距离的位置
1) 1) "changsha"2) "351.2102"127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1 # 筛选出指定的结果个数!
1) 1) "changsha"2) "351.2102"3) 1) "112.96700209379196167"2) "28.1969998601948717"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist withcoord count 2
1) 1) "changsha"2) "351.2102"3) 1) "112.96700209379196167"2) "28.1969998601948717"
2) 1) "guangzhou"2) "832.4234"3) 1) "113.26500087976455688"2) "23.10799963305151294"##########################################################################
#georadiusbymember
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km # 找出位于指定元素周围指定距离的其他元素!
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
2) "xian"
##########################################################################
# geohash 命令 - 返回一个或多个位置元素的 geohash 表示
127.0.0.1:6379> geohash china:city beijing xian # 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近
1) "wx4g0bm9xh0"
2) "wqj6zky6bn0"##########################################################################
GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!
127.0.0.1:6379> zrange china:city 0 -1 # 查看地图中全部的元素
1) "xian"
2) "shenzhen"
3) "guangzhou"
4) "xiamen"
5) "changsha"
6) "hangzhou"
7) "shanghai"
8) "beijing"
127.0.0.1:6379> zrem china:city shanghai # 移除指定元素!
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "xian"
2) "shenzhen"
3) "guangzhou"
4) "xiamen"
5) "changsha"
6) "hangzhou"
7) "beijing"

Hyperloglog

作用:统计一个网站被多少用户访问传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 !这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id
如果允许容错,那么可以使用 Hyperloglog !
如果不允许容错,可以使用 set 或者自己的数据类型即可
127.0.0.1:6379> pfadd user a b c d e f g h i j # 创建第一组元素 user
(integer) 1
127.0.0.1:6379> pfcount user
(integer) 10
127.0.0.1:6379> pfadd user2 h i j k l m n o p q # 创建第二组元素 user2
(integer) 1
127.0.0.1:6379> pfcount user2
(integer) 10
127.0.0.1:6379> pfmerge users user user2 # 合并两组 user user2 =>users 并集
OK
127.0.0.1:6379> pfcount users
(integer) 17

Bitmap(位存储)

统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,未打卡! 两个状态的,都可以使用Bitmaps
127.0.0.1:6379> setbit login 0 1 #第一天登录
(integer) 0
127.0.0.1:6379> setbit login 1 0 #第二天未登录
(integer) 0
127.0.0.1:6379> setbit login 2 1
(integer) 0
127.0.0.1:6379> setbit login 3 1
(integer) 0
127.0.0.1:6379> setbit login 4 1
(integer) 0
127.0.0.1:6379> setbit login 5 1
(integer) 0
127.0.0.1:6379> setbit login 6 1
(integer) 0
127.0.0.1:6379> getbit login 3  #查看某一天是否登录
(integer) 1
127.0.0.1:6379> getbit login 1
(integer) 0
127.0.0.1:6379> bitcount login  #查看登录的总天数
(integer) 6

3.事务

Redis 事务本质:一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!一次性、顺序性、排他性!执行一些列的命令!
Redis事务没有没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis单条命令式保存原子性的,但是事务不保证原子性!
redis的事务:开启事务(multi)命令入队(......)执行事务(exec)
#正常执行事务
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> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) OK
4) "v1"
5) "v2"
##########################################################################
# 放弃事务
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 k4 v4
QUEUED
127.0.0.1:6379> discard #取消事务
OK
127.0.0.1:6379> get k4 #取消事务后,队列中的命令并没有被执行,所以获取不到k4的值
(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> set k5 v5
QUEUED
127.0.0.1:6379> exec #执行事务,报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 #执行事务报错,所有的命令都不会被执行,所以获取不到k5的值
(nil)##########################################################################
# 运行时异常(1/0), 如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #对k1的值自增1,但是k1的值是字符串,不是int类型,不能进行自增操作,队列执行时会报错
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec #执行队列,由于队列中有运行时异常,所以会报错,但是报错不影响队列中其他命令的执行
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
##########################################################################

监控(watch)

悲观锁:
很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
很乐观,认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下,在此期间是否
有人修改过这个数据,获取version更新的时候比较 version
# 正常执行成功 ,事务开启时没有其他线程去改动对象
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set outmoney 0
OK
127.0.0.1:6379> watch money # 监控 money 对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby outmoney 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
# 执行失败 ,事务开启时有其他线程去改动对象
127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY outmoney 10
QUEUED
127.0.0.1:6379> exec # 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失 败!(nil)

Redis.conf详解

1.单位

配置文件unit单位对大小写不敏感

2.包含

3.网络

bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式
port 6379 # 端口设置

4.通用general

daemonize yes # 以守护进程的方式运行,默认是 no,我们需要自己开启为yes! pidfile /var/run/redis_6379.pid # 如果以后台的方式运行,我们就需要指定一个 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
logfile "" # 日志的文件位置名
databases 16 # 数据库的数量,默认是 16 个数据库
always-show-logo yes # 是否总是显示LOGO

5.快照

快照
持久化, 在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb. aof
redis 是内存数据库,如果没有持久化,那么数据断电会丢失
REPLICATION 复制
SECURITY 安全
可以在这里设置redis的密码,默认是没有密码!

6.security 安全

配置文件中设置用户密码

用命令设置用户密码

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> config get requirepass # 获取redis的密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456" # 设置redis的密码
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"

7.限制 CLIENTS

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

8.append only 模式 aof配置

appendonly no # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下, rdb完全够用!
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync always # 每次修改都会 sync。消耗性能
# appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据
appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快

4.Redis持久化

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

RDB(Redis DataBase)

在指定的时间间隔内将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程
都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。
这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那
RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是
RDB,一般情况下不需要修改这个配置!
有时候在生产环境我们会将这个文件进行备份!
rdb保存的文件是dump.rdb 都是在我们的配置文件中快照中进行配置的!

触发机制

1、save的规则满足的情况下,会自动触发rdb规则
2、执行 flushall 命令,也会触发我们的rdb规则!
3、退出redis,也会产生 rdb 文件!
触发机制即:备份就自动生成一个 dump.rdb

如何恢复rdb文件

1.查看需要存放的位置

2.将rdb文件放在redis启动目录,redis启动的时候会自动检查dump.rdb 恢复其中的数据

127.0.0.1:6379> config get dir
1) "dir"
2) "/var/lib/redis/6379"  # 如果在这个目录下存在 dump.rdb 文件,启动就会自动恢复其中的数据

1.执行 flushall 命令,也会触发我们的rdb规则!

2.退出redis,也会产生 rdb 文件!

3.save的规则满足的情况下,会自动触发rdb规则(未测试成功)

修改配置文件

重启redis服务

[root@localhost redis-5.0.8]# systemctl restart redis_6379.service

优缺点

优点:
1、适合大规模的数据恢复!
2、对数据的完整性要不高!
缺点:
1、需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了!
2、fork进程的时候,会占用一定的内容空间

AOF(Append Only File)

将我们的所有命令都记录下来,history,恢复的时候就把这个文件全部在执行一遍!

以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件
但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件
的内容将写指令从前到后执行一次以完成数据的恢复工作
Aof保存的是 appendonly.aof 文件
如果这个 aof 文件有错位,我们需要修复这个aof文件,redis 给我们提供了一个工具 redis-check-aof --fix

优缺点

优点:
1、每一次修改都同步,文件的完整会更加好!
2、每秒同步一次,可能会丢失一秒的数据
3、从不同步,效率最高的
缺点:
1、相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢
2、Aof 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化

拓展

扩展:
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文件,载入较新的那个,微博就是这种架构。

5.Redis发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信、
微博、关注系统!
Redis 客户端可以订阅任意数量的频道。
订阅/发布消息图:
第一个:消息发送者, 第二个:频道 第三个:消息订阅者!

演示发布与订阅

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应
的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
使用场景:
1、实时消息系统!
2、事实聊天!(频道当做聊天室,将信息回显给所有人即可!)
3、订阅,关注系统都是可以的!
稍微复杂的场景就会使用 消息中间件 MQ ()

6.Redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点
(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。
Master以写为主,Slave 以读为主。
默认情况下,每台Redis服务器都是主节点;
且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。()
主从复制的作用主要包括:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务
的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务
(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写
少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复
制是Redis高可用的基础。
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较
大;
2、从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有
内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是"多读少写"。

环境配置

只配置从库,不用配置主库!

127.0.0.1:6379> info replication # 查看当前库的信息
# Replication
role:master # 角色 master
connected_slaves:0  #连接的丛机数量
master_replid:9376f39b11db7fad27f6badeef47a34524e0c1cb
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

复制3个配置文件,然后修改对应的信息

1、端口

2、pid 名字

3、log文件名字

4、dump.rdb 名字

port 6379 # 端口设置
pidfile /var/run/redis_6379.pid #pid文件
logfile "" # 日志的文件位置名
dbfilename dump.rdb #dump.rdb 名字

修改完毕之后,启动我们的3个redis服务器,可以通过进程信息查看!

redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf

一主二从

默认情况下,每台Redis服务器都是主节点; 我们只用配置从机就好了!

一主 (79)二从(80,81)

6380:

6381:

6379:

主机读写 丛机读

项目中主从配置应该在配置文件中配置,这样的话是永久的,使用命令去配置是暂时的

主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存!

主机断开连接,从机依旧连接到主机,但是丛机没有写操作,但是可以读取之前从主机复制过来的数据,假设主机重新连接了,从机依旧可以直接获取到主机写的信息

如果是使用命令行,来配置的主从,这个时候如果重启了,就会变回主机,丛机有写数据,丛机立马就会从主机中获取值!

复制原理

Slave 启动成功连接到 master 后会发送一个sync同步命令
Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行
完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行, 我们的数据一定可以在从机中
看到.

如果主机断开了连接,我们可以使用 slaveof no one 让某一个redis服务变成主机!其他的节点就可以手动连接到最新的这个主节点(手动).

哨兵模式

(自动选举master的模式)

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独
立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

哨兵的作用

1.通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。2.当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机.

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

主观下线和客观下线

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行故障转移过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为**主观下线**。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为**客观下线**

1、配置哨兵配置文件sentinel.conf

vi sentinel.conf
# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1

后面的这个数字1是票数,多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了

2、启动哨兵

redis-sentinel sentinel.conf

如果Master 节点断开了,这个时候就会通过选举算法从从机中随机选择一个服务器

哨兵日志

从日志可看出哨兵选举出新的主机为127.0.0.1:6381,而其他两个服务(6379和6380)此时变成了丛机

如果之前的主机(6379)此时重新连接了,只能归并到新的主机(6381)下,当做从机,这就是哨兵模式的规则

查看三个redis服务的信息

6379:

6380:

6381:

总结:

1、配置哨兵配置文件 sentinel.conf sentinel monitor myredis 127.0.0.1 6379 1 #添加监控的主机
2.启动哨兵redis-sentinel sentinel.conf

优缺点

优点:
1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
2、主从可以切换,故障可以转移,系统的可用性就会更好
3、哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点:
1、Redis 不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦
2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择

哨兵模式的全部配置

# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 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

7.Redis缓存穿透和雪崩

缓存穿透(查不到)

缓存穿透,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JM8SF173-1626686767669)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210719124544438.png)]

解决方法

1.缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;
2.布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;
问题
但是这种方法会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存击穿(量太大,缓存过期!)

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访
问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。

解决方法

1.设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
2.加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!产生雪崩的原因之一.
例如:马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
其集中过期,不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方法

1.redis高可用
redis有可能挂掉,那么可以多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。(异地多活!)
2.限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3.数据预热
数据加热的含义就是在正式部署之前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

tip:

redis-server redis-6379.conf #开启redis服务
redis-cli -p 6379 #开启某个端口的redis客户端

详细可查看狂神说视频:https://space.bilibili.com/95256449

超详细 redis入门教程相关推荐

  1. 超强、超详细Redis入门教程【转】

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使用r ...

  2. 超详细Redis入门教程——Redis命令(下)

    前言 本文小新为大家带来 超详细Redis入门教程--Redis命令 相关知识,具体内容包括简单动态字符串 SDS,集合的底层实现原理,BitMap 操作命令,HyperLogLog 操作命令,Geo ...

  3. 超详细Redis入门教程——Redis命令(上)

    前言 本文小新为大家带来 超详细Redis入门教程--Redis命令(上) 相关知识,具体内容包括Redis 基本命令,Key 操作命令,String 型 Value 操作命令,Hash 型 Valu ...

  4. 超详细Redis入门教程——Redis概述

    前言 本文小新为大家带来 超详细Redis入门教程--Redis概述 相关知识,具体内容包括Redis简介,Redis的用途,Redis的特性,Redis的IO模型(包括:单线程模型,混合线程模型,多 ...

  5. 超详细Redis入门教程——Redis 的安装与配置

    前言 本文小新为大家带来 超详细Redis入门教程--Redis 的安装与配置 相关知识,具体内容包括Redis 的安装,连接前的配置,Redis 客户端分类(包括:命令行客户端,图形界面客户端,Ja ...

  6. 【转】超强、超详细Redis入门教程 ,建议收藏

    转载自: 这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3. ...

  7. 超详细Redis入门教程

    什么是NoSQL数据库 随着云计算.物联网等新一代技术的发展,在移动计算.社交网络的推动下,大数据技术产生并迅速地建立起生态体系.然而,大数据在推动技术变革的同时,企业对海量数据的存储.并发访问.扩展 ...

  8. 超详细Redis入门教程!

    一.引言 为啥需要redis?没有reids有啥问题?↓ 1.1 数据库压力过大 由于用户量增大,请求数量也随之增大,数据压力过大 1.2 数据不同步 多台服务器之间,数据不同步 1.3 传统锁失效 ...

  9. ECharts实现数据可视化超详细基础入门教程

    ECharts实现数据可视化超详细基础入门教程 ECharts介绍 ECharts官网:https://echarts.apache.org/zh/index.html ECharts是一款基于Jav ...

最新文章

  1. 读大叔深入理解javascript(2)
  2. 贝塞尔曲线(Bezier Curves)
  3. Iptables 入门进阶
  4. jQuery编写widget的一些窍门
  5. python批量修改labelme(COCO)标注的json文件的label标签名称
  6. Java读取excel文件 将Excel文件变为二维String数组 .
  7. rebuild myself rebuild my world
  8. 【matlab】结构体
  9. latex 图片整行居中 /centering无效
  10. MyBatis简介及下载
  11. STC15W408AS系列管脚说明
  12. AR VR MR 到底有啥区别?
  13. 当前 IT 行业,软件开发应该掌握的几种编程语言,你学会了几种?
  14. PHP常用的文件操作函数集锦
  15. Win10 NVIDIA Container占用CPU高的处理方法
  16. EasyExcel锁定指定单元格 禁止表格复制
  17. 少儿编程培训发展的重要趋势
  18. Ubuntu22.04更换国内镜像源(阿里、网易163、清华、中科大)
  19. 骚操作!程序员埋下每隔几年就触发的逻辑炸弹
  20. php 时间戳 精确到秒,时间戳与时间相互转换(php .net精确到毫秒)

热门文章

  1. 阴影:box-shodow 每个属性使用与分析
  2. 【深度学习】实验2答案:构建自己的多层感知机
  3. 剑指 Offer 46. 把数字翻译成字符串
  4. 【ABAP】OPEN SQL(六)「DELETE语句 | MODIFY语句」
  5. FP-growth:从FP树中挖掘频繁项集
  6. 星座高考成绩查询2021,2021年能金榜题名的星座,2021年学业运势旺盛的星座
  7. 推荐系统(3)—基于标签的推荐系统(Python)
  8. 索尔维会议记录软件测试,TBBT第4季2集:索尔维会议(附视频)
  9. 以下对python程序设计风格_以下对 Python 程序设计风格描述正确的选项是( )
  10. python豆瓣电影需研究的问题_python爬虫获取豆瓣电影——多线程问题