首页 > 数据库 >redis学习笔记

redis学习笔记

时间:2022-09-19 11:35:50浏览次数:92  
标签:127.0 0.1 redis 笔记 学习 6379 master integer localhost

Redis

一、rhel7安装redis6.0.6

[root@master redis-6.0.6]# cat /etc/redhat-release 
Red Hat Enterprise Linux Server release 7.6 (Maipo)

1、下载安装包

地址:https://www.redis.io/,在首页即可下载最新版本

image-20210825222232307

2、将文件拷贝至Linux服务器中

3、安装

# 将下载的文件拷贝至Linux服务器中
[root@node1 opt]# ls
redis-6.2.5.tar.gz  rh
[root@node1 opt]# tar -zxvf redis-6.2.5.tar.gz 

# make
[root@node1 opt]# cd redis-6.2.5/
[root@node1 redis-6.2.5]# make
...
...
Hint: It's a good idea to run 'make test' ;)

make[1]: 离开目录“/opt/redis-6.2.5/src”
[root@node1 redis-6.2.5]# 

# make install
[root@node1 redis-6.2.5]# make install

4、安装报错情况

我在安装6.0.6版本的Redis时有如下报错:

...
...
In file included from server.c:30:0:
server.h:1051:5: 错误:expected specifier-qualifier-list before ‘_Atomic’
     _Atomic unsigned int lruclock; /* Clock for LRU eviction */
     ^
server.c: 在函数‘serverLogRaw’中:
server.c:1032:31: 错误:‘struct redisServer’没有名为‘logfile’的成员
     int log_to_stdout = server.logfile[0] == '\0';
                               ^
server.c:1035:23: 错误:‘struct redisServer’没有名为‘verbosity’的成员
     if (level < server.verbosity) return;
                       ^
server.c:1037:47: 错误:‘struct redisServer’没有名为‘logfile’的成员
     fp = log_to_stdout ? stdout : fopen(server.logfile,"a");
                                               ^
server.c:1050:47: 错误:‘struct redisServer’没有名为‘timezone’的成员
         nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);
...
...

根据官网内容执行命令即可:

image-20210825224103983

5、默认安装路径

[root@node2 /]# ls /usr/local/bin/
redis-benchmark  redis-check-aof  redis-check-rdb  redis-cli  redis-sentinel  redis-server
[root@node2 /]# 

6、启动Redis服务

[root@node2 bin]# pwd
/usr/local/bin

# 创建自己的Redis启动配置文件
[root@node2 bin]# mkdir redis-install
[root@node2 bin]# cp /opt/redis-6.0.6/redis.conf redis-install/

# 修改配置文件,使Redis以后台形式运行
[root@node2 bin]# vim redis-install/redis.conf 
# 修改文件中的以下内容
daemonize yes

#使用新的配置文件启动程序
[root@node2 bin]# redis-server redis-install/redis.conf 
77097:C 25 Aug 2021 22:59:24.743 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
77097:C 25 Aug 2021 22:59:24.743 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=77097, just started
77097:C 25 Aug 2021 22:59:24.743 # Configuration loaded
[root@node2 bin]# 

7、测试

# 查看进程是否启动
[root@node2 bin]# ps -ef | grep redis
root      77098      1  0 22:59 ?        00:00:00 redis-server 127.0.0.1:6379
root      85735  37275  0 23:02 pts/0    00:00:00 grep --color=auto redis
[root@node2 bin]# 

# 连接测试
[root@node2 bin]# redis-cli -h localhost -p 6379
localhost:6379> ping
PONG
localhost:6379> set a b
OK
localhost:6379> get a
"b"
localhost:6379> keys *
1) "a"

8、停止Redis服务

# 查看此时进程
[root@node2 ~]# ps -ef | grep redis
root      77098      1  0 22:59 ?        00:00:00 redis-server 127.0.0.1:6379
root      95925  37275  0 23:07 pts/0    00:00:00 redis-cli -h localhost -p 6379
root     101508  99724  0 23:08 pts/2    00:00:00 grep --color=auto redis
[root@node2 ~]# 


# 关闭服务
localhost:6379> shutdown
not connected> exit
[root@node2 bin]# ps -ef | grep redis
root     104416  37275  0 23:09 pts/0    00:00:00 grep --color=auto redis
[root@node2 bin]# 

二、Redis基础

Redis默认有16个数据库,我们使用的是第0个

[root@node2 bin]# redis-cli -h localhost -p 6379
localhost:6379> select 15         #--选择第15个数据库
OK
localhost:6379[15]> dbsize       #--查看数据库大小
(integer) 0
localhost:6379[15]> 

image-20210825231500541

不同的数据库存不同的值

localhost:6379[15]> set myname lkf
OK
localhost:6379[15]> dbsize
(integer) 1
localhost:6379[15]> select 1
OK
localhost:6379[1]> get myname
(nil)
localhost:6379[1]> select 15
OK
localhost:6379[15]> get myname
"lkf"
localhost:6379[15]> 

清除当前的数据库

localhost:6379[15]> keys *
1) "myname"
localhost:6379[15]> flushdb
OK
localhost:6379[15]> keys *
(empty array)
localhost:6379[15]> 

清除全部的数据库

localhost:6379> keys *
1) "a"
localhost:6379> select 15
OK
localhost:6379[15]> flushall
OK
localhost:6379[15]> select 0
OK
localhost:6379> keys *
(empty array)
localhost:6379> 

Redis是单线程的

​ Redis很快,官方表示,Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的性能瓶颈是机器的内存和网络宽带,既然可使用单线程来实现,就是用单线程了。

Redis为什么单线程还这么快?

  • 误区1:高性能一定是多线程的

  • 误区2:多线程一定比单线程块(多线程CPU上下文会切换,耗时)

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

三、Redis数据类型

redis-key基本操作

[root@master bin]# redis-server myconf/redis.conf 
[root@master bin]# redis-cli -h localhost -p 6379
localhost:6379> keys *                 # 查看所有key
1) "a"
2) "c"
localhost:6379> exists a               # 查看key是否存在
(integer) 1                            # 存在返回1,不存在返回0
localhost:6379> exists b               # 不能用来查看value
(integer) 0
localhost:6379> move a 1               # 从当前数据库移除
(integer) 1
localhost:6379> set a b
OK
localhost:6379> expire a 20           # 设置key 值过期时间,单位为秒
(integer) 1
localhost:6379> ttl a                 # 查看当前key的剩余时间
(integer) 17
localhost:6379> ttl a
(integer) 13
localhost:6379> type c                # 查看key的数据类型
string
localhost:6379> 


查看命令帮助地址http://www.redis.cn/commands.html

3.1、五大数据类型

String(字符串)

localhost:6379> flushall
OK
localhost:6379> set a a1
OK
localhost:6379> type a                     # 字符串为String类型
string
localhost:6379> append a "a2"              # 在字符串后追加字符串
(integer) 4
localhost:6379> get a
"a1a2"
localhost:6379> STRLEN a                   # 获取字符串长度
(integer) 4
localhost:6379> keys *
1) "a"
localhost:6379> append a1 "lkf"            # 当key不存在时,append即为新增key
(integer) 3
localhost:6379> get a1
"lkf"
localhost:6379> 

# 自增
localhost:6379> set num 0                # 设定初始值
OK
localhost:6379> incr num                 # 自增加1
(integer) 1
localhost:6379> get num
"1"
localhost:6379> incrby num 10            # 设定步长,指定增长量
(integer) 11
localhost:6379> decrby num 10            # 设定步长,指定减少量
(integer) 1
localhost:6379> get num
"1" 
localhost:6379> decr num                 # 自减少1
(integer) 0
localhost:6379> get num
"0"
localhost:6379> 

# 查看某个范围的字符串
localhost:6379> set number "1234567890"
OK
localhost:6379> get number
"1234567890"
localhost:6379> GETRANGE number 0 4
"12345"
localhost:6379> getrange number 0 -1        # 查看全部,和get key一样
"1234567890"
localhost:6379> 

# 替换字符串
localhost:6379> set str abcdefg             
OK
localhost:6379> get str
"abcdefg"
localhost:6379> setrange str 1 23          # 将str从第二位开始替换字符串
(integer) 7
localhost:6379> get str
"a23defg"
localhost:6379> 


# setex(set with expire)   # 设置过期时间
localhost:6379> setex mykey 30 "hello"     #设置mykey,过期时间为30秒
OK
localhost:6379> get mykey
"hello"
localhost:6379> ttl mykey
(integer) -2
localhost:6379> get mykey
(nil)
localhost:6379> keys *
1) "number"
2) "str"
# setnx(set if not exist)  # 不存在再设置,可以在分布式锁中应用
localhost:6379> setnx mykey "123"        # 当准备设置的key值不存在时才会设置
(integer) 1
localhost:6379> setnx mykey "234"        # 当准备设置的key值存在时不会设置
(integer) 0
localhost:6379> 

# mset,设置多个key-value
localhost:6379> mset k1 v1 k2 v2 k3 v3
OK

# mget,获取多个key-value
localhost:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
localhost:6379> 
# msetnx
localhost:6379> msetnx k1 v1 k4 v4
(integer) 0
localhost:6379> keys *         # msetnx 是一个原子性的操作,要么一起成功,要么,一起失败
1) "k2"
2) "k3"
3) "k1"
localhost:6379> 

# 对象
set user:1{name:zhangsan, age:3}  # 设置一个user:1对象,值为json字符串来保存一个对象

# 这里的key是一个巧妙的设置:user:{id}{filed},如此设计在Redis中是完全OK的
localhost:6379> mset user:1name zhangsan user:1:age 27
OK
localhost:6379> msget user:1name user:1:age
localhost:6379> mget user:1name user:1:age
1) "zhangsan"
2) "27"
localhost:6379> 

# getset,先get一个key,再设置这个key值
localhost:6379> getset db redis
(nil)
localhost:6379> get db
"redis"
localhost:6379> getset db oracle
"redis"
localhost:6379> get db
"oracle"
localhost:6379> 

List(列表)

可以将Redis玩成栈、队列、阻塞队列

所有的list命令都是以l开头的

# 插入数据 lpush、rpush
localhost:6379> LPUSH l_num 1 2 3    # 将一个或者多个值从头部依次插入
(integer) 3
# 获取列表的所有值,注意到值得顺序,表示先插入的是1,后插入的是3
localhost:6379> LRANGE l_num 0 -1    
1) "3"
2) "2"
3) "1"
localhost:6379> LRANGE l_num 0 1     # 获取列表的值   
1) "3"
2) "2"
# 将一个或者多个值从尾部依次插入
localhost:6379> RPUSH l_num 4 5 6
(integer) 6
# 观察列表中的元素顺序
localhost:6379> LRANGE l_num 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "5"
6) "6"
localhost:6379> 

# 移除列表的第一个元素数据 l_pop、r_pop
localhost:6379> LPOP l_num 
"3"
localhost:6379> RPOP l_num
"6"
localhost:6379> 

# 通过下表获取值 lindex
localhost:6379> LRANGE l_num 0 -1
1) "2"
2) "1"
3) "4"
4) "5"
localhost:6379> LINDEX l_num 0
"2"
localhost:6379> LINDEX l_num 1
"1"
localhost:6379> 

# 返回列表的长度 llen
localhost:6379> LRANGE l_num 0 -1
1) "2"
2) "1"
3) "4"
4) "5"
localhost:6379> LLEN l_num
(integer) 4
localhost:6379> 

# 移除元素这种指定个数的元素(从左开始移除) lrem
localhost:6379> LRANGE l_num 0 -1
1) "1"
2) "2"
3) "1"
4) "4"
5) "5"
localhost:6379> LREM l_num 1 1         # 移除一个值为1 的元素
(integer) 1
localhost:6379> LRANGE l_num 0 -1
1) "2"
2) "1"
3) "4"
4) "5"
localhost:6379> lpush l_num 1
(integer) 5
localhost:6379> LRANGE l_num 0 -1
1) "1"
2) "2"
3) "1"
4) "4"
5) "5"
localhost:6379> LREM l_num 2 1      # 移除两个值为1 的元素
(integer) 2
localhost:6379> LRANGE l_num 0 -1
1) "2"
2) "4"
3) "5"
localhost:6379> 

# 通过下标截取列表元素
localhost:6379> rpush mylist "hell0" "hello1" "hello2" "hello3"
(integer) 4
localhost:6379> LRANGE mylist 0 -1
1) "hell0"
2) "hello1"
3) "hello2"
4) "hello3"
localhost:6379> LTRIM mylist 1 2
OK
localhost:6379> LRANGE mylist 0 -1
1) "hello1"
2) "hello2"
localhost:6379> 

# 移除列表中的一个元素,添加到新的列表中
localhost:6379> rpush mylist "hello" "hello1" "hello2"
(integer) 3
localhost:6379> LRANGE mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
localhost:6379> RPOPLPUSH mylist myotherlist
"hello2"
localhost:6379> LRANGE mylist 0 -1
1) "hello"
2) "hello1"
localhost:6379> LRANGE myotherlist 0 -1
1) "hello2"
localhost:6379> 

localhost:6379> EXISTS list                      # 判断list是否存在
(integer) 0
localhost:6379> EXISTS mylist
(integer) 1
localhost:6379> LRANGE mylist 0 -1
1) "hello"
2) "hello1"
localhost:6379> lset mylist 0 h                  # 更新下标对应元素值
OK
localhost:6379> LRANGE mylist 0 -1
1) "h"
2) "hello1"
localhost:6379> LRANGE mylist 2 hh               # 下标不存在时会报错
(error) ERR value is not an integer or out of range
localhost:6379> 

# 插入元素 linsert  before/after
localhost:6379> LRANGE mylist 0 -1
1) "h"
2) "hello1"
localhost:6379> LINSERT mylist before h begin        # before
(integer) 3
localhost:6379> LRANGE mylist 0 -1
1) "begin"
2) "h"
3) "hello1"
localhost:6379> LINSERT mylist after hello1 end      # after
(integer) 4
localhost:6379> LRANGE mylist 0 -1
1) "begin"
2) "h"
3) "hello1"
4) "end"
localhost:6379> 

Set(集合)

set中的值是不能重复的

localhost:6379> sadd myset "likf" "likf1"   # set 集合中添加值
(integer) 2
localhost:6379> SISMEMBER myset "likf"      # 判断一个值是否存在于集合中
(integer) 1
localhost:6379> SISMEMBER myset "lkf"
(integer) 0
localhost:6379> SMEMBERS myset              # 查看set的所有值
1) "likf"
2) "likf1"
localhost:6379> SCARD myset                 # 获取set元素个数
(integer) 2
localhost:6379> SREM myset likf             # 移除set中的指定元素
(integer) 1
localhost:6379> SMEMBERS myset
1) "likf1"
localhost:6379> 
 
localhost:6379> SMEMBERS myset
1) "likf3"
2) "likf2"
3) "likf1"
localhost:6379> SRANDMEMBER myset           # 随机抽选一个元素
"likf3"
localhost:6379> SRANDMEMBER myset
"likf1"
localhost:6379> SRANDMEMBER myset 2         # 随机抽选两个元素
1) "likf1"
2) "likf2"
localhost:6379> SRANDMEMBER myset 2
1) "likf3"
2) "likf2"
localhost:6379> 

localhost:6379> SMEMBERS myset          
1) "likf3"
2) "likf2"
3) "likf1"
localhost:6379> SPOP myset                 # 随机移除一个set中的元素
"likf2"
localhost:6379> SPOP myset
"likf1"
localhost:6379> SMEMBERS myset
1) "likf3"
localhost:6379> 

localhost:6379> SMEMBERS myset2           
1) "world"
2) "hello"
localhost:6379> SMOVE myset myset2 likf1  # 将一个指定的值,移动到另一个set中
(integer) 1
localhost:6379> SMEMBERS myset2
1) "world"
2) "hello"
3) "likf1"
localhost:6379> SMEMBERS myset
1) "likf3"
2) "likf2"

# 两个集合求差集
localhost:6379> sadd k1 a b c
(integer) 3
localhost:6379> sadd k2 b c d e
(integer) 4
localhost:6379> SDIFF k1 k2
1) "a"
localhost:6379> SDIFF k2 k1
1) "e"
2) "d"
# 两个集合求交集(场景:微博、B站共同好友。。。)
localhost:6379> SINTER k1 k2
1) "b"
2) "c"
# 两个集合求并集(场景:微博、B站所有好友)
localhost:6379> SUNION k1 k2
1) "e"
2) "a"
3) "c"
4) "d"
5) "b"

微博,A用户将所有关注的人放在一个set集合中,将它的粉丝也放在一个集合中

共同关注,共同爱好,二度好友,推荐关注!(六度分割理论)

Hash(哈希)

Map集合,key-map(key-),这时候这个值是一个map集合

localhost:6379> HSET myhash f1 likf         # set 一个具体的key-value
(integer) 1
localhost:6379> HGET myhash f1
"likf"

localhost:6379> HMSET myhash f1 likf f2 likf2 f3 likf3 # set 多个key-value
OK
localhost:6379> HMGET myhash f1 f2 f3       # 同时获取多个field的值
1) "likf"
2) "likf2"
3) "likf3"
localhost:6379> HGETALL myhash              # 获取所有field-value
1) "f1"
2) "likf"
3) "f2"
4) "likf2"
5) "f3"
6) "likf3"

localhost:6379> HDEL myhash f1             # 删除一个hash指定的field,对应的value值也没了
(integer) 1
localhost:6379> HGETALL myhash
1) "f2"
2) "likf2"
3) "f3"
4) "likf3"
localhost:6379> HLEN myhash                # 获取hash的长度
(integer) 2

localhost:6379> HGETALL myhash
1) "f2"
2) "likf2"
3) "f3"
4) "likf3"
localhost:6379> HEXISTS myhash f1         # 判断hash中的指定字段是否存在
(integer) 0
localhost:6379> HEXISTS myhash f2
(integer) 1

localhost:6379> HKEYS myhash              # 获取hash中所有的field
1) "f2"
2) "f3"
localhost:6379> HVALS myhash              # 获取hash中所有的value
1) "likf2"
2) "likf3"

localhost:6379> HGETALL myhash
1) "f2"
2) "likf2"
3) "f3"
4) "likf3"
5) "f1"
6) "1"
localhost:6379> HINCRBY myhash f1 1       # hash中的value自增
(integer) 2
localhost:6379> HGETALL myhash
1) "f2"
2) "likf2"
3) "f3"
4) "likf3"
5) "f1"
6) "2"
localhost:6379> HINCRBY myhash f1 -1     #  hash中的value自减
(integer) 1
localhost:6379> HGETALL myhash
1) "f2"
2) "likf2"
3) "f3"
4) "likf3"
5) "f1"
6) "1"
localhost:6379> HSETNX myhash f4 lkf    # 如果存在则可以设置      
(integer) 1
localhost:6379> HSETNX myhash f4 lkf4   # 如果不存在则不能设置
(integer) 0

hash应用:

hash更适合对象的存储,尤其是用户信息之类的,经常变动的信息,例如:hset user:1 nage lkf(1代表用户的ID)

localhost:6379> hset user:1 name lkf
(integer) 1
localhost:6379> hget user:1 name
"lkf"
localhost:6379>

Zset(有序集合)

在set的基础上增加了一个值

127.0.0.1:6379> zadd myzset 1 one          # 添加一个值
(integer) 1
127.0.0.1:6379> zadd myzset 2 two 3 three  #添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myzset 0 -1         # 查看所有值
1) "one"
2) "two"
3) "three"

# 根据score排序
127.0.0.1:6379> zadd salary 1000 xiaohong 1500 xiaobai 400 xiaoli
(integer) 3
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf          #根据score排序,负无穷到正无穷
1) "xiaoli"
2) "xiaohong"
3) "xiaobai"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores   # 负无穷到正无穷,带上score查询
1) "xiaoli"
2) "400"
3) "xiaohong"
4) "1000"
5) "xiaobai"
6) "1500"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 1000 withscores   # 负无穷到1000,带上score查询
1) "xiaoli"
2) "400"
3) "xiaohong"
4) "1000"

127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores  从大到小排序
1) "xiaohong"
2) "1000"
3) "xiaoli"
4) "400"


# 移除一个元素
127.0.0.1:6379> ZREM salary xiaobai
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "xiaoli"
2) "xiaohong"
127.0.0.1:6379> ZCARD salary        # 获取集合中的个数
(integer) 2

# 获取指定区间的数量
127.0.0.1:6379> zadd myset 1 likf1 2 likf2 3 likf3 4 likf4
(integer) 4
127.0.0.1:6379> zcount myset 1 4
(integer) 4
127.0.0.1:6379> zcount myset 1 2
(integer) 2
127.0.0.1:6379> zcount myset 2 4
(integer) 3

set排序,可以用于存储班级成绩表,工资表

普通消息、重要消息,带权重进行判断

排行榜的应用

3.2、三种特殊数据类型

geospatial (地理位置)

朋友的定位,附近的人,打车距离计算等

geoadd - 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中

  • 有效的经度从-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 114.279 30.573 wuhan 106.549 29.581 chongqing
(integer) 2

geopos - 从key里返回所有给定位置元素的位置(经度和纬度)

127.0.0.1:6379> GEOPOS china:city beijing
1) 1) "116.40800267457962036"
   2) "39.90399988166036138"
127.0.0.1:6379> GEOPOS china:city beijing wuhan
1) 1) "116.40800267457962036"
   2) "39.90399988166036138"
2) 1) "114.27899926900863647"
   2) "30.57299931525717795"

geodist - 返回两个给定位置之间的距离

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
    如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
127.0.0.1:6379> GEODIST china:city beijing wuhan km   # 北京到武汉的直线距离
"1055.6216"

georadius - 以给定的经纬度为中心, 找出某一半径内的元素

我附近的人?(获取附近的人的地址,定位!)通过半径来查询

127.0.0.1:6379> GEORADIUS china:city 116 30 1000 km
1) "wuhan"
2) "shanghai"
3) "chongqing"
127.0.0.1:6379> GEORADIUS china:city 120 30 1000 km
1) "wuhan"
2) "shanghai"
127.0.0.1:6379> GEORADIUS china:city 120 30 1000 km withdist withcoord  # 带上直线距离和坐标查询
1) 1) "wuhan"
   2) "553.0979"
   3) 1) "114.27899926900863647"
      2) "30.57299931525717795"
2) 1) "shanghai"
   2) "193.2264"
   3) 1) "121.44499808549880981"
      2) "31.213001199663303"
      
127.0.0.1:6379> GEORADIUS china:city 120 30 1000 km withdist withcoord count 1   # 限定显示数量
1) 1) "shanghai"
   2) "193.2264"
   3) 1) "121.44499808549880981"
      2) "31.213001199663303"

GEORADIUSBYMEMBER - 找出位于指定范围内的元素,中心点是由给定的位置元素决定

127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1200 km      # 查找北京1200km范围内的城市
1) "beijing"
2) "wuhan"
3) "shanghai"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1200 km withcoord withdist  # 带上直线距离和坐标查询
1) 1) "beijing"
   2) "0.0000"
   3) 1) "116.40800267457962036"
      2) "39.90399988166036138"
2) 1) "wuhan"
   2) "1055.6216"
   3) 1) "114.27899926900863647"
      2) "30.57299931525717795"
3) 1) "shanghai"
   2) "1068.2320"
   3) 1) "121.44499808549880981"
      2) "31.213001199663303"

GEOHASH - 返回一个或多个位置元素的 Geohash 表示

该命令将返回11个字符的Geohash字符串

127.0.0.1:6379> GEOHASH china:city beijing wuhan
1) "wx4g0bm9xh0"
2) "wt3mbmxkts0"

GEO 底层的实现原理其实就是zset,我们可以通过zset命令来操作geo。

127.0.0.1:6379> ZRANGE china:city 0 -1       # 查看地图中全部元素
1) "chongqing"
2) "wuhan"
3) "shanghai"
4) "beijing"
127.0.0.1:6379> ZREM china:city chongqing    # 删除其中一个元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "wuhan"
2) "shanghai"
3) "beijing"
127.0.0.1:6379> 

Hyperloglog

什么是基数?

A {1,3,5,7,9}

B {1,3,5,7,9,3}

基数(不重复的元素) = 5,可以接受误差

Hyperloglog简介

Redis hyperloglog 基数统计的算法!

优点:占用的内存是固定的,2^64不同的元素的基数,只需要12KB的内存!如果从内存角度比较的话,Hyperloglog首选!

应用场景:

网页的UV(一个人访问一个网站多次,但是还是算作一个人!)

传统方式: set保存用户的id,因为set集合数据是不重复的,那么久可以统计set中的元素数量。但是这个方式如果保存大量的用户ID,就会比较麻烦,占用内存。我们的目的是计数,而不是保存用户ID。

0.81%错误率,统计UV任务,可以忽略不计。

127.0.0.1:6379> PFADD mykey a b c d e      # 创建第一组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey              # 统计第一组元素的基数数量
(integer) 5
127.0.0.1:6379> PFADD mykey2 c d e f g     # 创建第二组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 5
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #合并mykey和mykey2=mykey3
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 7
127.0.0.1:6379> 

如果不允许容错,就是用set或者自己的数据类型即可

bitmap

位存储

统计用户信息,活跃/不活跃,登录/未登录,打卡/未打卡。。。。两个状态的,都可以是用bitmap

# 是用bitmap来记录周一到周日的打卡!
# 如下第一个数字代表星期,第二个数字代表是否打卡(0代表未打卡,1代表打卡)
127.0.0.1:6379> SETBIT sign 0 1
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 0
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 1
(integer) 0
127.0.0.1:6379> SETBIT sign 6 0
(integer) 0

# 查看某天是否有打卡
127.0.0.1:6379> GETBIT sign 0
(integer) 1
127.0.0.1:6379> GETBIT sign 3
(integer) 0

# 统计打卡天数
127.0.0.1:6379> BITCOUNT sign
(integer) 3

四、事务

Redis事务的本质:一切命令的集合

一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。

一次性、顺序性、排他性地执行一系列的命令。

Redis事务没有隔离级别的概念

所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行!

Redis的单条命令是保存原子性的,但是事务不保证原子性!

Redis的事务:

  • 开启事务(multi)
  • 命令入队(......)
  • 执行事务
127.0.0.1:6379> MULTI                 # 开启事务
OK
127.0.0.1:6379> set k1 v1             # 命令入队
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec                  # 执行事务
1) OK
2) "v1"
3) OK
4) "v2"

取消事务

127.0.0.1:6379> MULTI                # 开启事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD              # 取消事务
OK
127.0.0.1:6379> get 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> getset k2 v2
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> 

运行时错误(例行1/0)。如果事务队列中存在语法性错误,那么执行命令时,其它命令可以正常执行,错误命令抛出异常!

127.0.0.1:6379> set k1 v1                 # 设置一个字符串的值
OK
127.0.0.1:6379> MULTI                     # 开启事务
OK
127.0.0.1:6379> INCR k1                   # 将字符串+1,很明显不对
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec                     # 执行事务
1) (error) ERR value is not an integer or out of range     # 错误地方直接报错
2) OK                                                      # 后面语句正常执行
3) "v2"

监控 WATCH

  • 悲观锁

    • 很悲观,什么时候都会出现问题,无论做什么都会加锁!
  • 乐观锁

    • 很乐观,认为什么时候都不会出问题,所以不会加锁!更新数据的时候去判断一下,在此期间是否有人修改数据。
    • 获取version
    • 更新的时候比较version

Redis的监视测试

127.0.0.1:6379> set money 100                 
OK
127.0.0.1:6379> set out 20
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 out 20
QUEUED
127.0.0.1:6379> exec                        # 正确的情况下执行成功
1) (integer) 80
2) (integer) 40
127.0.0.1:6379> 

测试多线程修改值,使用watch,可以当做Redis的乐观锁操作

# 1、首先执行线程1,此时事务还未提交
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 10         # money减少10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> 

# 2、线程1未提交前,提交线程2
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000          # 修改money的值
OK

# 3、提交线程1
127.0.0.1:6379> EXEC                    # 事务提交失败
(nil)

# 如果执行失败,重新监视即可
127.0.0.1:6379> UNWATCH                 # 当发现事务执行失败后,需要先解锁
OK
127.0.0.1:6379> watch money             # 再添加锁,执行事务
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY money 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 990
2) (integer) 1010
127.0.0.1:6379> 

五、详解redis.conf

1、内存大小单位

当需要内存大小时,可以使用如下的单位,并且单位对大小写不敏感

image-20210830223303452

2、本配置文件可以包含其它的配置文件

image-20210830223340516

3、网络

bind 127.0.0.1         # 绑定ip

protected-mode yes     # 是否是受保护模式

port 6379              # 端口号

4、通用配置 GENERAL

daemonize no  # 以守护进程的方式运行

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是内存数据库,如果没有持久化,数据断电即失

save 900 1         # 900秒内,至少有一个key进行了修改,则进行持久化操作
save 300 10        # 300秒内,至少有十个key进行了修改,则进行持久化操作
save 60 10000      # 60秒内,至少有一万个key进行了修改,则进行持久化操作

stop-writes-on-bgsave-error yes   # 持久化出错了,是否继续进行持久化操作
rdbcompression yes                # 是否压缩rdb文件。需要消耗一些CPU资源

rdbchecksum yes    # 保存rdb文件的时候,进行错误的检查校验
dir ./             # rdb 保存到目录,默认是当前目录

6、REPLICATION (主从复制)

7、SECURITY

默认是没有密码的

# 通过命令查看密码
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) ""

# 通过命令行设置密码
127.0.0.1:6379> CONFIG SET requirepass 123456
OK
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) "123456"

# 重新登录显示需要密码
127.0.0.1:6379> exit
[root@master bin]# redis-cli -p 6379
127.0.0.1:6379> config get requirepass   # 发现命令不能使用了
(error) NOAUTH Authentication required.   
127.0.0.1:6379> auth 123456         # 使用密码登录
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"

8、CLIENTS

限制客户端最大连接数

image-20210830230530875

9、MEMORY MANAGEMENT

maxmemory <bytes>         # redis 最大内存设置

maxmemory-policy noeviction     # 达到最大内存后的策略
	# noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。(默认值)
    # allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
    # volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
    # allkeys-random: 所有key通用; 随机删除一部分 key。
    # volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
    # volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。

10、APPEND ONLY 模式——aof配置

appendonly no    # 默认是不开启aof模式的,默认使用rdb模式持久化,大部分情况下rdb够用的
appendfilename "appendonly.aof"   # 持久化文件的名字

# appendfsync always     # 每次修改都会同步,消耗性能
appendfsync everysec     # 每秒执行一次同步,如果突然宕机,可能会丢失这一秒的数据
# appendfsync no         # 不同步,这个时候操作系统自己同步数据,速度最快

具体的配置在持久化中介绍。

Redis持久化

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

RDB

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的snapshot快照,它恢复时是将快照文件直接读到内存中。

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加有效。RDB的缺点是最后一次持久化后的数据可能丢失。

我们默认的就是RDB,一般情况下不需要修改配置。

RDB 保存的文件就是dump.rdb 在redis.conf 的SNAPSHOTTING 中配置的

image-20210831212324644

触发机制

  1. redis.conf 文件中,SNAPSHOTTING 配置的save规则满足的情况下,会生产rdb文件;
  2. 执行flushall命令,也会触发rdb规则;
  3. 退出Redis,也会产生rdb文件

image-20210831213252655

恢复rdb文件

1、只需要将rdb文件放到我们的Redis启动目录就可以,Redis启动的时候会自动检查dump.rdb 并恢复其中的数据;

2、查看需要存在的位置

127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/bin"           # 如果目录下存在dump.rdb文件,启动的时候会自动恢复其中的数据

几乎默认的配置已经够用了,但是我们还是需要去学习

优点:

  • 适合大规模的数据恢复!
  • 对数据要求不高。

缺点:

  • 需要一定的时间间隔进行操作!如果Redis意外宕机了,,最后一次修改的数据就没有了。

  • fork进程时,需要占用一定的内存空间。

AOF(Append Only Fille)

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

以日志的形式来记录每隔写操作,将Redis执行过得所有指令记录下来(不记录读操作),只许追加文件。Redis启动之初会读取该文件重构数据,,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次完成数据的恢复。

aof保存的是appendonly.aof 文件

默认APPEND ONLY是不开启的,我们需要手动将配置文件中的appendonlly no设置为appendonlly yes 来开启。

# 重写参数介绍(aof默认的是文件的无限追加,文件会越来越大)
no-appendfsync-on-rewrite no         # 不开启重写 
auto-aof-rewrite-percentage 100      # 超过基准的百分比,基准是下面64m的参数
auto-aof-rewrite-min-size 64mb       # 当sof文件大于64m时将开启一个新的进程进行重写操作

image-20210831215040300

当aof文件被破坏

[root@master bin]# redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused         # 启动报错

可以使用Redis自带的修复工具进行修复

[root@master bin]# redis-check-aof --fix appendonly.aof                  # 修复文件
0x              64: Expected \r\n, got: 7366
AOF analyzed: size=118, ok_up_to=75, diff=43
This will shrink the AOF from 118 bytes, with 43 bytes, to 75 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@master bin]# redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> exit
[root@master bin]# redis-server redis.conf 
33259:C 31 Aug 2021 22:07:59.472 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
33259:C 31 Aug 2021 22:07:59.472 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=33259, just started
33259:C 31 Aug 2021 22:07:59.472 # Configuration loaded
[root@master bin]# redis-cli -p 6379                                     # 正常启动了
127.0.0.1:6379> 

优点:

  • 每一次修改都会同步,文件的完整性更好;
  • 每秒同步一次,可能会丢失一秒的数据;
  • 从不同步,效率最高。

缺点:

  • 相对于数据文件来说,aof远远大于rdb,修复速度也比rdb慢;

  • aof运行效率也要比rdb慢,所以我们Redis默认的配置就是rdb持久化。

发布订阅

订阅着

127.0.0.1:6379> SUBSCRIBE myword           # 订阅一个频道myword
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "myword"
3) (integer) 1
# 等待读取推送的消息
1) "message"
2) "myword"
3) "nihao"

1) "message"
2) "myword"
3) "nihaoma"

发布者

127.0.0.1:6379> PUBLISH myword nihao          # 发布者向频道myword发布消息
(integer) 1
127.0.0.1:6379> PUBLISH myword nihaoma
(integer) 1
127.0.0.1:6379> 

image-20210831223934884

生动形象地理解:

应用:

1、实时消息系统

2、实时聊天(频道当做一个聊天室,将信息回显给所有人即可)

3、订阅、关注系统

稍微复杂的会使用消息中间件来做

主从复制

概念

主从复制,是指将一台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

    电商网站上的商品,一般都是一次上传,无数次浏览,说专业点就是“多读少写”。

主从复制

模式一:一主二从

示意图

image-20210909141558353

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

基本信息
role ip port
master 192.168.0.7 6379
slave 192.168.0.8 6379
slave 192.168.0.9 6379
查看主从信息
# 查看主从信息,默认role是master
127.0.0.1:6379> info replication
# Replication
role:master               # 角色
connected_slaves:0        # 没有从机
master_failover_state:no-failover
master_replid:5842cc8e872228ca8a205bfe260d1de8f6d93f19
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> 
通过命令方式配置从机

1、先修改主机和从机的配置文件绑定的ip地址为0.0.0.0,以便于主从机可以相互访问

image-20210909105607536

2、在从机上执行命令

# 在从机上执行
127.0.0.1:6379> SLAVEOF 192.168.0.7 6379
OK
127.0.0.1:6379> 

# 在主机上再次查看信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2                                # 增加了连个从机
slave0:ip=192.168.0.8,port=6379,state=online,offset=56,lag=0     # 显示了从机的ip和port等信息
slave1:ip=192.168.0.9,port=6379,state=online,offset=56,lag=0
master_failover_state:no-failover
master_replid:cbfa3da61dc3aaa2e97dd38ff8cf753559b0df07
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56
127.0.0.1:6379> 


# 在从机上查看信息
127.0.0.1:6379> info replication
# Replication
role:slave                                    # 角色变为了slave
master_host:192.168.0.7                       # 显示了主机的信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:cbfa3da61dc3aaa2e97dd38ff8cf753559b0df07
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> 
通过配置文件配置从机

修改从机的redis.conf中的以下内容即可

image-20210909134356074

细节

  1. 主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存。
  2. 主机断开连接,从机依旧连接到主机,但是没有写操作,这个时候,主机回来了,从机依旧可以直接获取主机写进去的信息。
  3. 如果使用命令行配置主从,这个时候如果从机重启了,就会变回主机。只要变为从机,数据会立马恢复。

复制原理

slave启动成功连接到master后会发送一个sync同步命令

master街道命令,启动后台的存盘进程,同事收集所有接收到的用于修改数据集命令,在后台进程执行完毕后,master将传送整个数据文件到slave,并完成一次完全同步

全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。

增量复制:master继续将新的所有收集到的命令以此传给slave,完成同步。

但是只要是重连master,一次完全同步将被自动执行。

模式二:层层链路

示意图

image-20210909141915359

配置方式:通过命令或者修改配置文件皆可,将slave1的主机设置为master,slave2的主机设置为slave1 即可

测试

# 配置完后检查master 的信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1                     # 只有一个从机
slave0:ip=192.168.0.8,port=6379,state=online,offset=2828,lag=0
master_failover_state:no-failover
master_replid:50942b96fe38376c080fd35a01d87f8c5240994c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2828
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2828
127.0.0.1:6379> 

# 检查slave1 的信息
127.0.0.1:6379> info replication
# Replication
role:slave                                 # 角色依旧为slave
master_host:192.168.0.7                    # 主机为master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:2912
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:1
slave0:ip=192.168.0.9,port=6379,state=online,offset=2912,lag=1
master_failover_state:no-failover
master_replid:50942b96fe38376c080fd35a01d87f8c5240994c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2912
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2912
127.0.0.1:6379> 

# 检查slave2 的信息
127.0.0.1:6379> info replication
# Replication
role:slave                                  # 角色为slave
master_host:192.168.0.8                     # 主机为slave1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:3010
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:50942b96fe38376c080fd35a01d87f8c5240994c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3010
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2563
repl_backlog_histlen:448
127.0.0.1:6379> 

从机切换到主机模式

以上两种方式,当master宕掉后,两个slave还是为不能写数据,想要写数据,手动切换其中一个为master,其它节点手动切换连接到我。如果master又重新连接了,那么全部只能重新配置。

# 手动将slave改为master
127.0.0.1:6379> info replication   
# Replication
role:slave                                    # 1、确定当前角色为slave
master_host:192.168.0.8
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:3928
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:50942b96fe38376c080fd35a01d87f8c5240994c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3928
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2563
repl_backlog_histlen:1366
127.0.0.1:6379> SLAVEOF no one              # 切换为master
OK
127.0.0.1:6379> info replication
# Replication
role:master                                 # 确定切换成功
connected_slaves:0
master_failover_state:no-failover
master_replid:33186b4ba4a7041ce823541a8cc9020abde6b888
master_replid2:50942b96fe38376c080fd35a01d87f8c5240994c
master_repl_offset:3942
second_repl_offset:3943
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2563
repl_backlog_histlen:1380
127.0.0.1:6379> 

哨兵模式(自动选举master)

概述

主从切换技术的方法是:当主服务器宕机后,需要手动把一台服务器切换到主服务器,这就需要人工干预,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了sentinel(哨兵)架构来解决这个问题。

谋朝篡位的自动版,能够后台监控主机故障。如果故障了,根据投票数自动将从库转换为主库

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

单机模式示意图

1

哨兵的两个作用:

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

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

多哨兵模式示意图

2

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

哨兵模式

优点:

  • 哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
  • 主从可以切换,故障可以转移,系统的可用性就会更好
  • 哨兵模式就是主从模式的升级,手动到自动,更加健壮

缺点:

  • Redis不好在线扩容,集群容量一旦到达上线,在线扩容就十分麻烦
  • 实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

标签:127.0,0.1,redis,笔记,学习,6379,master,integer,localhost
From: https://www.cnblogs.com/likaifei/p/16707150.html

相关文章

  • 第七章 Redis数据持久化之RDB
    一、总体介绍1.Redis数据安全问题官网介绍:http://www.redis.io前面我们提到,Redis是一个缓存中间件,它的最大特点是使用内存从而使其性能强悍。但是使用内存的方式有一个......
  • 什么是合奏技术?让我们用柠檬语言学习吧。✔
    什么是合奏技术?让我们用柠檬语言学习吧。✔什么是合奏技巧?每当我们有大量数据时,或者我们可以说,每当我们在数据集中有大量行和列时,我们使用技术集。例如:-假设我们有......
  • 机器学习模型
    机器学习模型在MySkill继续学习,最后教授的材料是关于机器学习的。在这篇文章中,我将分享机器学习的一个概念或一个粗略的想法。我想分享一下如何一步一步地在python中构......
  • 第六章 Redis新数据类型
    一、Bitmaps1.简介现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位,例如“abc”字符串是由3个字节组成,但实际在计算机存储时将其用二进制表示,“abc”分别对应......
  • 《js 设计模式与开发实践》读书笔记 13
     职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 ......
  • 《js 设计模式与开发实践》读书笔记 14(完)
    在传统面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活,还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之改变;另......
  • 【RocketMQ 课程笔记】7.RocketMQ高可用方案
    RocketMQ高可用消息生产消费流程​ Broker即MQ服务器;​ NameServer可理解为注册中心。Broker主挂了的情况Broker主从都挂了的情况Broker双主挂了的情......
  • 第四章 Redis-6.0版本配置文件详解
    一、Units单位#如果要配置跟内存大小相关的参数是可以这样配置,只支持bytes,不支持bit,这些单位都是大小写不敏感的:#1k=>1000bytes#1kb=>1024bytes#1m=>10......
  • 【RocketMQ 课程笔记】11.RocketMQ消息发送之普通消息
    RocketMQ消息发送之普通消息架构拓扑NameServer:192.168.31.103Master:192.168.31.105Slave:192.168.31.111执行流程Master与Slave启动向NameServer注册生产者Prod......
  • 第二章 Redis-6.0概述安装
    一.缓存数据库的概念传统的数据库管理系统把所有数据都放在磁盘上进行管理,所以称做磁盘数据库(DRDB:Disk-ResidentDatabase)。磁盘数据库需要频繁地访问磁盘来进行数据的操......