Redis学习总结

Redis总结

这是我学习Redis网址,大家可以借鉴一下,仅供参考:https://www.bilibili.com/video/BV1S54y1R7SB?p=1

Nosql简介

NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。

在现代的计算系统上每天网络上都会产生庞大的数据量。

NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,特别是大数据应用难题。

回顾关系型数据库遵循ACID规则

事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性:

1、A (Atomicity) 原子性

原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。

2、C (Consistency) 一致性

一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。

3、I (Isolation) 独立性

所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。

4、D (Durability) 持久性

持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。

为什么使用NoSQL ?

今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL 数据库的发展却能很好的处理这些大的数据。

RDBMS vs NoSQL

RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务

NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
-键 - 值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性

关于CAP定理的介绍

在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer’s theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  • 一致性(Consistency) (所有节点在同一时间具有相同的数据)
  • 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
  • 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

Redis

Redis概述

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。

Redis 与其他 key - value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

redis的安装

安装gcc,yum install gcc-c++
img

解压redis: tar -zxvf redis-5.0.7.tar.gz
img

安装redis: make
img

如果是安装的6.x版本的redis,可能会报错,因为gcc的版本过低,所以需要更新版本,命令如下:
yum -y install centos-release-scl yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils scl enable devtoolset-9 bash

修改环境变量
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
gcc -v
make
make install

修改config的启动方式为后台启动
img

启动redis服务,指定配置文件启动:./redis-server ../myredis/redis.conf
img
查看redis进程是否开启 ps -ef|grep redis:
img

关闭redis: shutdown
img

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

类型简介特性场景
String(字符串)二进制安全可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M
Hash(字典)键值对集合,即编程语言中的Map类型适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)存储、读取、修改用户属性
List(列表)链表(双向链表)增删快,提供了操作某一段元素的API1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合)哈希表实现,元素不重复1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合)将Set中的元素增加一个权重参数score,元素按score有序排列数据插入集合时,已经进行天然排序1、排行榜 2、带权重的消息队列

Redis 键(key)

Redis 键命令用于管理 redis 的键。

select  [num]	    # 切换第num+1个数据库
dbsize				# 当前数据库大小
set name Antarcticc	# 意思是新增一字段name,值为Antarcticc
get name			# 取得字段name的值
keys *				# 查看所有键名
flushdb				# 清空当前库
flushall			# 清空所有库
exists name			# 判断内存中键值对是否存在,存在返回1,不存在返回0
MOVE name 1			# 移动键为name的键值对到1号数据库中
EXPIRE name 10		# 设置键为name的键值对10秒后自动过期
ttl name			# 查看键为name的键值对过期剩余时间
type name			# 查看键name对应值的类型

Redis 字符串(String)

string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

set key value    # 设置指定 key 的值
get key value    # 获取指定 key 的值
append key value # 在key后面追加字符串value,如果key是不存在的,相当于set key value

INCR key       # 使key对应的值自增1
INCRBY key 10  # 使key对应的值自增10
DECR key       # 使key对应的值自减1
DECRBY key 10  # 使key对应的值自减10

getrange key 0 3    # 取得key值的字符串中下标为0到3的字符
getrange key 0 -1   # 取得key值的整个字符串
SETRANGE key 2 q    # 替换key值的下标为2位置的字符

SETEX key 30 "hello"  # 如果key存在,则设置其值为hello,并在30秒还过期,否则创建失败
SETNX mykey "redis"   # 如果mykey不存在,则创建mykey并设置值为redis,否则创建失败。在分布式锁中经常使用

mset k1 v1 k2 v2 k3 v3 # 同时设置多个键值对,kl对v1,k2对v2,k3对v3
mget k1 k2 k3 		   # 使用多个键同时取得多个值,输出结果如下
1) "v1"
2) "v2"
3) "v3" 

msetnx k4 v4 		# 如果k4不存在,则创建k4并赋值为v4
msetnx k1 v1 k4 v4  # 如果k1和k4都不存在,则创建k1和k4并赋值为v1和v4,如果有一个已经存在,则失败。原子性操作

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

# 这里的key是一个巧妙的设计:user:{id}:{filed}
mset user:1:name zhangsan user:1:age 20
# 即通过key加id加属性的方式实现工整有序不重复的存储。
getset db "redis"# 相当于get db得到原值并输出后,set db "redis"。如果不存在则返回null并设置新值,如果存在先返回原值再设置新的值

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

  • 计数器
  • 浏览量
  • 统计多单位的数量,例:uid:999999:follow 0,设置uid为999999的用户粉丝数为0
  • 对象缓存存储

Redis 哈希(Hash)

Redis hash 是一个键值(key=>value)对集合。

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

127.0.0.1:6379> hset myhash field1 Antarcticc# 为myhash新增一个键值对
(integer) 1
127.0.0.1:6379> HGET myhash field1	# 查看myhash中键值对中key为field1的value值
"Antarcticc"
127.0.0.1:6379> HMSET myhash field1 hello field2 world# 批量对myhash新增键值对,如果包含已存在键值对,则覆盖原有的
OK
127.0.0.1:6379> HMGET myhash field1 field2# 批量查询myhash中的field1和field2对应的value值
1) "hello"
2) "world"
127.0.0.1:6379> HGETALL myhash# 列出myhash中所有的键值对,序号奇数为key,偶数为value
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> HDEL myhash field1# 删除myhash中键名为field1的键值对
(integer) 1
127.0.0.1:6379> HLEN myhash# 查看myhash表长度
(integer) 3
127.0.0.1:6379> HEXISTS myhash field1# 判断myhash表中是否存在field1字段
(integer) 1
127.0.0.1:6379> HKEYS myhash# 获取所有的key
1) "field2"
2) "field1"
3) "field3"
127.0.0.1:6379> HINCRBY myhash field4 1# 自增1
(integer) 2
127.0.0.1:6379> HSETNX myhash field hello# 如果myhash中不存在field字段则创建并赋值为hello,否则执行失败。
(integer) 0

Redis 列表(List)

Redis 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。

LPUSH key value # 从左侧插入,将一个值或者多个值插入到列表的头部,写入顺是1,2,3存储顺序是3,2,1
LRANGE key 0 -1 # 取得key列表中所有内容
LRANGE key 0 1  # 取得key列表中下标为0到下标为1的内容(后进先出,最后进来的下标是0)
RPUSH key value # 从右侧插入,将一个值或者多个值插入到列表的尾部,写入顺是1,2,3存储顺序就是1,2,3
LPOP key 		 # 移除名为key列表的第一个元素
RPOP key		 # 移除名为key列表的最后一个元素
LINDEX key index # 取得key列表中下标为index的值
LLEN key 		 # 取得key列表的长度
LREM key 4 3	 # 从头部开始移除4个值为3的value
LTRIM key 1 2	 # 删除列表中除下标为1、2的其他所有元素
RPOPLPUSH mylist myotherlist #移除mylist列表的最后一个元素,将其添加到myotherlist列表中
LSET mylist 0 value # 修改mylist中下标为0的值修改为value,如果列表或者下标不存在,则报错。
LINSERT mylist before "1" "Antarcticc" # 将Antarcticc插入到列表中1的前面
LINSERT mylist after "2" "Antarcticc"  # 将Antarcticc插入到列表中2的后面

Redis 集合(Set)

Redis 的 Set 是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

127.0.0.1:6379> sadd myset hello	# 向set集合myset中添加元素hello
(integer) 1
127.0.0.1:6379> SADD myset world	# 向set集合myset中添加元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset		# 查看myset中所有的值
1) "world"
2) "hello"
127.0.0.1:6379> SISMEMBER myset hello	# 查看hello是否在set中
(integer) 1
127.0.0.1:6379> SISMEMBER myset qq		# 查看hello是否在set中
(integer) 0
127.0.0.1:6379> scard myset				# 获取myset集合中有多少个值
(integer) 2
127.0.0.1:6379> SREM myset hello		# 移除set中的值为hello的元素
(integer) 1
127.0.0.1:6379> SRANDMEMBER myset 1		# 随机抽取myset集合中的1个元素
"hello"
127.0.0.1:6379> SRANDMEMBER myset 2		# 随机抽取myset集合中的2个元素
"hello"
”world"
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "world"
3) "hello3"
4) "hello2"
127.0.0.1:6379> spop myset 1# 随机移除myset集合中的1个元素
"hello2"
127.0.0.1:6379> spop myset 2# 随机移除myset集合中的2个元素
"hello3"
"hello"
127.0.0.1:6379> SMEMBERS myset
1) "world"
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> SMOVE myset myset2 3# 移动myset中的为3的值到myset2中,如果myset2不存在则先创建后再移动
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "3"
############################################################
# 交并差
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key2 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key1 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2# 取得key1和key2的差集
1) "e"
2) "a"
127.0.0.1:6379> SINTER key1 key2# 取得key1和key2的交集
1) "c"
127.0.0.1:6379> SUNION key1 key2# 取得key1和key2的并集
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"

实际应用:

用户将所有关注的人放在一个set集合中。粉丝也放在一个set中。取得共同关注、共同好友。

Redis 有序集合(sorted set)

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

127.0.0.1:6379> ZADD myset 1 one			# 添加一个值
(integer) 1
127.0.0.1:6379> ZADD myset 2 two 3 three	# 添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1			# 查询所有
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ZADD salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> ZADD salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 199999 wangzheng
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 根据score从小到大排列,范围是负无穷到正无穷(从小到大不可改变)。
1) "xiaohong"
2) "zhangsan"
3) "wangzheng"
127.0.0.1:6379> ZREVRANGE salary 0 -1# 根据score从大到小排列,范围是所有元素。
1) "wangzheng"
2) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores# 根据score从小到大排列,范围是负无穷到2500(从小到大不可改变),并显示具体值。
1) "xiaohong"
2) "2500"
127.0.0.1:6379> ZREM salary xiaohong	# 移除小红
(integer) 1
127.0.0.1:6379> ZCARD salary			# 获取集合中元素个数
(integer) 2
127.0.0.1:6379> ZCOUNT myset 0 1		# 获取区间元素个数
(integer) 1

redis.conf配置文件

Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf

CONFIG GET * #获取所有配置项

redis.conf 配置项说明如下:

序号配置项说明
1daemonize noRedis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no )
2pidfile /var/run/redis.pid当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
3port 6379指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字
4bind 127.0.0.1绑定的主机地址
5timeout 300当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能
6loglevel notice指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice
7logfile stdout日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null
8databases 16设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
9save <seconds> <changes>Redis 默认配置文件中提供了三个条件:save 900 1save 300 10save 60 10000分别表示 900 秒(15 分钟)内有 1 个更改,300 秒(5 分钟)内有 10 个更改以及 60 秒内有 10000 个更改。指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
10rdbcompression yes指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大
11dbfilename dump.rdb指定本地数据库文件名,默认值为 dump.rdb
12dir ./指定本地数据库存放目录
13slaveof <masterip> <masterport>设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步
14masterauth <master-password>当 master 服务设置了密码保护时,slav 服务连接 master 的密码
15requirepass foobared设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭
16maxclients 128设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
17maxmemory <bytes>指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区
18appendonly no指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no
19appendfilename appendonly.aof指定更新日志文件名,默认为 appendonly.aof
20appendfsync everysec指定更新日志条件,共有 3 个可选值:no:表示等操作系统进行数据缓存同步到磁盘(快)always:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)everysec:表示每秒同步一次(折中,默认值)
21vm-enabled no指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM 机制将数据分页存放,由 Redis 将访问量较少的页即冷数据 swap 到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析 Redis 的 VM 机制)
22vm-swap-file /tmp/redis.swap虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享
23vm-max-memory 0将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis 的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为 0 的时候,其实是所有 value 都存在于磁盘。默认值为 0
24vm-page-size 32Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值
25vm-pages 134217728设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。
26vm-max-threads 4设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
27glueoutputbuf yes设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
28hash-max-zipmap-entries 64 hash-max-zipmap-value 512指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
29activerehashing yes指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍)
30include /path/to/local.conf指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件

三种特殊数据类型

Geospatial

地理位置

127.0.0.1:6379>  geoadd china:city 116.4 39.9 beijing# 添加地理位置
(integer) 1
127.0.0.1:6379> GEOPOS china:city beijing# 获取指定城市的经纬度
1) 1) "116.39999896287918091" #经度
   2) "39.90000009167092543" #纬度
127.0.0.1:6379> GEOPOS china:city beijing shanghai aomen# 获取多个城市经纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "121.40000134706497192"
   2) "31.20000061483705878"
3) 1) "113.49999994039535522"
   2) "22.19999914574732003"
127.0.0.1:6379> GEODIST china:city beijing shanghai# beijing和shanghai之间的直线距离
"1067742.3622"# 默认单位米
127.0.0.1:6379> GEODIST china:city beijing shanghai km # beijing和shanghai之间的直线距离,设置单位为千米
"1067.7424"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km# 以经纬度110 30为原心,1000km为半径,寻找在china:city中满足这个范围内的城市。
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord# 加一个withcoord是显示详细经纬度
1) 1) "aomen"
   2) 1) "113.49999994039535522"
      2) "22.19999914574732003"
2) 1) "xianggang"
   2) 1) "114.19999748468399048"
      2) "22.29999896492555678"
3) 1) "shenzhen"
   2) 1) "114.09999936819076538"
      2) "22.50000113800319212"
4) 1) "guangzhou"
   2) 1) "113.29999834299087524"
      2) "23.10000005307264104"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist# 加一个withdist显示直线距离
1) 1) "aomen"
   2) "935.1758"
2) 1) "xianggang"
   2) "953.3433"
3) 1) "shenzhen"
   2) "928.8366"
4) 1) "guangzhou"
   2) "834.6077"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 10000 km# china:city中beijing的经纬度为中心,10000千米为半径,寻找在china:city中满足这个范围内的城市
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
5) "xiangshan"
6) "shanghai"
7) "beijing"
127.0.0.1:6379> GEOHASH china:city beijing shanghai# 将china:city集合中的beijing、shanghai二维的经纬度,转换成一维的字符串
1) "wx4fbxxfke0"
2) "wtw36xbc1j0"
127.0.0.1:6379> ZRANGE china:city 0 -1# 使用Zset来查询所有地理信息
1) "aomen"
2) "xianggang"
3) "shenzhen"
4) "guangzhou"
5) "xiangshan"
6) "shanghai"
7) "beijing"
127.0.0.1:6379> ZREM china:city xianggang# 删除指定地理位置
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "aomen"
2) "shenzhen"
3) "guangzhou"
4) "xiangshan"
5) "shanghai"
6) "beijing"

Hyperloglog

HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

127.0.0.1:6379> PFADD mykey a b c d e f g h i j k l m n	# 创建第一组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey	# 显示数据量
(integer) 14
127.0.0.1:6379> PFADD mykey2 o p q r s t u v w x y z	# 创建第二组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2	# 显示数据量
(integer) 12
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2	# 将mykey3赋值为mykey和mykey2的不重复并集,如果出现重复,则只保留一个其余的均忽略
OK
127.0.0.1:6379> PFCOUNT mykey32	# 显示数据量
(integer) 25

Bitmaps位图

都是操作二进制位来进行记录,就只有1和0两个状态。

# 情景再现:员工打卡系统,sign代表打卡者,0代表星期一,1代表星期二,以此类推,记录为1代表打卡成功,记录为0代表打卡失败
# 录入
127.0.0.1:6379> setbit sign 0 1# 下标为0,记录为1,打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 0# 下标为1,记录为0,未打卡
(integer) 0
# 查询
127.0.0.1:6379> getbit sign 3# 查看星期四是否打卡
(integer) 0 # 否
127.0.0.1:6379> getbit sign 0# 查看星期一是否打卡
(integer) 1 # 是
# 统计
127.0.0.1:6379> BITCOUNT sign# 统计sign所有数据中为1的数据的总数,即统计sign本周打卡成功的情况
(integer) 3

Redis事务

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

一次性:Redis中命令集合按照顺序排列在一个队列中一次执行完毕。

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

排他性:事务执行中不允许其他干扰。

Redis事务步骤:

  • 开启事务
  • 命令入队
  • 执行事务
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> exec# 执行事务// 注意在执行完exec语句后,再需要使用事务需要再次multi开启
1) OK
2) OK
3) OK
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# 取消事务// 注意在执行完DISCARD语句后,再需要使用事务需要再次multi开启
OK
127.0.0.1:6379> get k4# k4存入失败,事务取消成功
(nil)
# 如果语句出现问题,那么整个事务不执行。比如下面入队一个不存在的语句getset
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v3
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 k3
(nil)
# 如果是语句没问题,是运行时异常,除了有问题的那一句报错且不执行以外,其他语句照常执行。比如下面让字符串类型自增
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> 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"

Jedis

使用Java来操作Redis,Jedis是Redis推荐的Java连接开发工具,是Java操作Redis的中间件

导入Jedis包:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

数据库的连接:

public class TestPing {
    public static void main(String[] args) {
        // 1、 new一个Jedis对象
        Jedis jedis = new Jedis("127.0.0.1",6379);
        // 如果 Redis 服务设置来密码,需要下面这行,没有就不需要
        // jedis.auth("123456"); 
        // Jedis所有的命令就是Redis命令
        System.out.println(jedis.ping());
        //连接成功输出PONG
    }
}

Jedis事务

public static void main(String[] args) {
    Jedis jedis = new Jedis("47.99.179.145", 6379);
	// 清空数据库
    jedis.flushAll();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hello","world");
    jsonObject.put("name","zhaohang");
    // 开启事务
    Transaction multi = jedis.multi();
    String result = jsonObject.toJSONString();
    try {
        multi.set("user1",result);
        multi.set("user2",result);
        int  i=1/0;
        multi.exec();
    } catch (Exception e) {
        multi.discard();
        e.printStackTrace();
    } finally {
        System.out.println(jedis.get("user1"));
        System.out.println(jedis.get("user2"));
        jedis.close();
    }

}

Redis持久化

RDB

数据存入,保存的文件是dump.rdb,再次启动时读出。

只要dump.rdb文件生成,即使关机失去内存,也能在下一次启动时成功读取到持久化的信息。

image-20210331200447777

rdb触发机制

1、save的规则满足的情况下会自动触发.rdb文件的创建

2、执行了flushdb或者flushall也会生成 .rdb文件

3、退出redis,也会产生rdb文件

备份就会自动生成dump.rdb

只要dump.rdb文件生成,即使关机失去内存,也能在下一次启动时成功读取到持久化的信息。

如何恢复rdb文件

只需要将rdb文件放在redis启动目录下即可,redis启动时会自动检查dump.rdb文件恢复其中的数据到内存中。

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin" # 如果在此目录下存在dump.rdb文件,redis启动就会自动恢复dump.rdb中的内容到内存中。

RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能。适合打大规模的数据恢复。应用情景:服务器宕机后,redis启动目录中的dump.rdb文件将数据恢复到宕机前的模样。对数据完整性要求不高。

缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

AOF

将所有命令都记录下来,有一个历史文件,恢复时候将此文件全部再执行一遍。<几乎不使用>

AOF保存的是appendonly.aof,也是在配置文件redis.conf中配置。

默认是不开启aof的,我们需要主动进行配置

当aof文件有错误,此时redis是启动不起来的,我们需要修复aof这个文件。redis-check-aof进行修复

重写规则说明

redis会记录上一次aof文件的大小,如果aof文件大于64MB,将fork一个新的进程来将为aof文件进行重写。

aof默认的是文件的无限追加,所以这个文件只会越来越大。

优点和缺点

优点:

1、每一次修改都同步,文件的完整性比较好。

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

2、每秒同步一次,可能会丢失一秒的数据。

3、从不同步,效率最高。

缺点:

1、相对于数据文件来说,即aof文件远远大于rdb,修复速度也比rdb慢。

2、aop文件的读写效率慢,所以redis默认是rdb而不是aof

RDB和AOF的扩展

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 这条规则。
如果启用 AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
如果不启用 AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。

Redis发布订阅

Redis发布订阅是一种消息模式:发送者发送消息,订阅者接受消息。举例:微信、微博、关注系统。

Redis客户端可以订阅任意数量的频道。作为了解。

三个角色:消息发送者、频道、消息订阅者

订阅并不是Redis特有的,只是Redis也可以实现。

image-20210331202132268

测试

订阅端:

# 订阅者对频道Antarcticc发起订阅
127.0.0.1:6379> SUBSCRIBE Antarcticc
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Antarcticc"
3) (integer) 1

发布端:

# 发布者向频道Antarcticc发起消息
127.0.0.1:6379> PUBLISH Antarcticc "hello"
(integer) 1

订阅端得到发布端推送的信息展示

# 订阅者收到消息后实时更新
127.0.0.1:6379> SUBSCRIBE Antarcticc
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Antarcticc"
3) (integer) 1
1) "message" 	# 消息
2) "Antarcticc" # 频道
3) "hello" 		# 消息内容

实际应用:

1、实时消息系统。在注册完网页后,自动订阅网站官方这个频道,从而实现官方向用户的推送。

2、实时聊天。将信息回显即可。

Redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

image-20210331202752515

  1. 一个master可以有多个slave(最少是一主二从)
  2. 一个slave只能有一个master
  3. 数据流向是单向的,master到slave(主节点到从节点,主节点写,从节点写。)
  4. 主从复制,读写分离。80%情况下都是在读。所以将写的请求放在主节点,读的请求放在从节点,减缓服务器压力。

主从复制的作用

数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

环境配置

只配置从机,不配置主机,因为每台redis服务器本身就默认是主节点。

127.0.0.1:6379> info replication	 # 查看当前库的角色
# Replication
role:master
connected_slaves:0
master_replid:fb1c53140ad7f8cacaf39d7e39fd64b8014deeea
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

一主二从伪集群模式搭建:

复制三个配置文件,修改对应的配置信息。

1、修改端口号

2、pid名字

3、日志名

4、备份文件dump.rdb文件名

修改完毕后启动三个redis服务

[root@antarctic ~]# cd /usr/local/bin
[root@antarctic bin]# ls
6379.log    cloud-init      easy_install-3.6  jsonpatch         mcrypt           redis-cli
6380.log    cloud-init-per  easy_install-3.8  jsonpointer       mdecrypt         redis-sentinel
6381.log    dump6380.rdb    jemalloc-config   jsonschema        myredisconf      redis-server
apt-get     dump6381.rdb    jemalloc.sh       libmcrypt-config  redis-benchmark  temp-1617193959.30034.rdb
chardetect  dump.rdb        jeprof            luajit            redis-check-aof
cloud-id    easy_install    jsondiff          luajit-2.0.4      redis-check-rdb
[root@antarctic bin]# cd myredisconf/
[root@antarctic myredisconf]# ls
redis79.conf  redis80.conf  redis81.conf  redis.conf  sentinel.conf

配置从机:

从机1:

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
127.0.0.1:6380> info replication
# Replication
role:slave# 身份是从机
master_host:127.0.0.1
master_port:6379 # 主机端口号
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
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

从机2:

127.0.0.1:6381> SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6381> info replication
# Replication
role:slave # 身份是从机
master_host:127.0.0.1
master_port:6379# 主机端口号
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:42
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43
repl_backlog_histlen:0

查看主机信息:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2 # 从机数
slave0:ip=127.0.0.1,port=6380,state=online,offset=84,lag=1# 从机1
slave1:ip=127.0.0.1,port=6381,state=online,offset=84,lag=1# 从机2
master_replid:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84

真实的主从配置应该是在配置文件中配置,我们这里使用的是命令,是暂时的。关闭服务将不会成为从机

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

测试

主机断开连接:

主机断开连接,从机依旧是连接到主机的,只是没有写操作了,从机依旧可以获得主机写入过的信息,如果此时主机重新连接了,此时可以重新获取到主机写的信息。

从机断开连接:

前提是使用命令方式设置主从复制,而不是使用配置文件实现主从复制的情况下,从机断开连接后,重新连接后此从机自动变回主机,不再能获得原来是从机身份时主机中的内容,如果此时重新变回从机,就会重新获得主机的内容。

Redis主从复制哨兵模式

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。

哨兵模式概述

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

image-20210331204159889

这里的哨兵有两个作用:

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

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

image-20210331204436041

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

测试

1、创建哨兵配置文件sentinel.conf,固定名称,不能拼错

2、在哨兵配置文件中写入

sentinel monitor myredis79 127.0.0.1 6381 1
# 自定义监控名称myredis,监控目标127.0.0.1下的6379端口,1的意思是如果主机挂了就自动去已挂主机下的从机中票选一个票数最多的从机,成为新的主机。
sentinel auth-pass myredis79 123456

3、启动哨兵

[root@antarctic bin]# redis-sentinel sentinel.conf # 哨兵模式启动,配置文件是sentinel.conf
23516:X 08 Dec 2020 15:13:37.984 
23516:X 08 Dec 2020 15:13:37.984 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=23516, just started
23516:X 08 Dec 2020 15:13:37.984 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 23516
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               
23516:X 08 Dec 2020 15:13:37.985 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
23516:X 08 Dec 2020 15:13:37.987 # Sentinel ID is 334f503724eb834cfbbb2498f75fc2847e6b9496
23516:X 08 Dec 2020 15:13:37.987 # +monitor master myredis 127.0.0.1 6379 quorum 1

此时断开6379主机连接,哨兵模式替换开始运作

哨兵日志如下:

23531:X 08 Dec 2020 15:20:53.324 # +sdown master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.324 # +odown master myredis 127.0.0.1 6379 #quorum 1/1
23531:X 08 Dec 2020 15:20:53.324 # +new-epoch 1
23531:X 08 Dec 2020 15:20:53.324 # +try-failover master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.328 # +vote-for-leader 334f503724eb834cfbbb2498f75fc2847e6b9496 1
23531:X 08 Dec 2020 15:20:53.328 # +elected-leader master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.328 # +failover-state-select-slave master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.418 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.418 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.473 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.900 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.900 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:53.956 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:54.942 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:54.942 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:55.008 # +failover-end master myredis 127.0.0.1 6379
23531:X 08 Dec 2020 15:20:55.008 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6381
23531:X 08 Dec 2020 15:20:55.008 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381
23531:X 08 Dec 2020 15:20:55.008 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
23531:X 08 Dec 2020 15:21:25.053 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381 # 此时哨兵完成主机更换,新主机为127.0.0.1 6381

如果主机断开连接,就会从从机中随机票选出一个成为主机,具体是如何票选的是有具体算法的,这里不作赘述。(哨兵选主机是根据性能和数据最好的从机进行选择)

如果此时原主机重新连接,会默认成为新主机的从机。

哨兵模式优点:

1、哨兵集群,基于主从复制模式,所有的主从配置优点,他全有。

2、主从可以切换,故障可以转移,系统的可用性就会跟好。

3、哨兵模式就是主从模式的升级,手动到自动,更加健壮。

缺点:

1、Redis不好扩容,集群容量一旦到达上限,在线扩容十分麻烦(大量配置文件需要修改)。

2、实现哨兵模式的配置,其实是很麻烦的,里面有很多选择。

Sentinel.conf配置说明

# 哨兵sentinel监控的redis主节点的 ip port 
# 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  
 
 
# 哨兵sentinel实例运行的端口 默认26379  
port 26379  
 
 
# 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非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

为了避免缓存穿透其实有很多种解决方案。下面介绍几种。

解决方案

布隆过滤器

image-20210331205617119

布隆过滤器是一种数据结构,垃圾网站和正常网站加起来全世界据统计也有几十亿个。网警要过滤这些垃圾网站,总不能到数据库里面一个一个去比较吧,这就可以使用布隆过滤器。假设我们存储一亿个垃圾网站地址。

有一天网警查到了一个可疑的网站,想判断一下是否是XX网站,首先将可疑网站通过哈希映射到1亿个比特数组上的8个点。如果8个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。

那这个布隆过滤器是如何解决redis中的缓存穿透呢?很简单首先也是对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。

这个形式很简单。

缓存空对象

image-20210331205600766

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;

但是这种方法会存在两个问题:

如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存雪崩

概念

缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

image-20210331205631672

解决方案

(1)redis高可用

这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

(2)限流降级

这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

(3)数据预热

数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

(4) 给缓存的失效时间,加上一个随机值,避免集体失效。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值