微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

中间件学习笔记-Redis入门

以下学习内容笔记全部来源于B站教学视频:【狂神说Java】Redis最新超详细版教程通俗易懂,非常感谢来自狂神老师的教学系列,附B站学习视频链接:https://www.bilibili.com/video/av840034966

1. Redis(缓存)

1.1 Nosql

1.1.1 概述

单机MysqL时代:

90年代,一个基本的网站访问量不会太大,单个数据库足够!

更多的使用静态网页html

网站瓶颈:

  • 数据量太大,一个机器放不下
  • 数据的索引,一个机器的内存也放不下
  • 访问量太大,读写混合,性能问题

缓存时代:

memcached(缓存)+MysqL+读写分离(垂直拆分)

网站百分之80情况都是在读,每次都要去数据库查询太麻烦,减轻数据库的压力,可以使用缓存来保证效率(Cache)-------解决读的问题

发展过程:优化MysqL底层结构和索引–>文件缓存(IO操作)—>Memcached 当时最热门的技术!

分库分表+水平拆分+MysqL集群:

技术和业务发展同时,对技术要求也越来越高

本质: 数据库(读、写)

早些年 MyISAM:表锁,影响效率,高并发下出现严重的锁问题

早些年Innodb:行锁

慢慢地使用分库分表来解决写的压力!并没有多少公司使用

MysqL集群,很好满足了个别需求

最近的年代:

2010-2020 技术发展迅速(定位、音乐、热榜)

MysqL等关系型数据库就不够用了,数据量很多,变化很快

目前一个基本的互联网项目:

在这里插入图片描述

为什么要用Nosql

  • 用户的个人信息、地理位置、社交网络、用户自己产生的数据、用户日志等等爆发式增长!

这时候可以使用Nosql数据库处理以上的情况!

1.1.2 什么是Nosql

Nosql

Nosql=not only sql

sql:表格,行,列

泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!

Nosql在当今大数据时代发展迅速,REdis发展是最快的,、

Nosql特点

  • 方便扩展(数据之间没有关系,很好扩展,没有耦合性)

  • 大数据量下的高性能(redis一秒可以写8W次,可以读取11w次,Nosql的缓存级别,是一种细粒度的缓存,性能会比较高!)

  • 多类型(不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人无法设计)

  • 传统RDBM和Nosql:

    结构化组织

    sql

    数据和关系都存在单独的表中

    数据操作语言,数据定义语言,

    严格的一致性

    基础的事务

​ 不仅仅是数据

​ 没有固定的查询语言

​ 键值对存储,列存储,文档存储,图形数据库(社交关系)

​ 最终一致性

​ CAP定理、 BASE (异地多活)初级架构师!

​ 基本的高性能、高可用、高可扩展

​ …

扩展:大数据的3v和3高

描述问题:海量、多样、实时

程序要求:高并发、高可用、高性能

1.1.3 Nosql分类

KV键值对

文档型数据库(bson格式json)

列存储数据库

图形关系数据库

在这里插入图片描述

  • 不是放图片的,是放关系的:朋友圈社交网络、广告推荐!

  • Ne04j, infoGrid

Nosql的对比

在这里插入图片描述

1.1.4 Redis

redis–Remote Dictionary Server

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。免费且开源,当前最热门的Nosql之一

能干嘛?

  • 内存存储、持久化,内存中是断电即失去,所以说持久化很重要(rdb/aof)

  • 效率高、可以用于高速缓存

  • 发布订阅系统

  • 地图信息分析

  • 计时器、计数器(浏览量)

特性

  • 多样的数据类型
  • 持久化
  • 集群
  • 事务

学习中需要用到的东西

  • 官网
  • 中文
  • 下载地址:通过官网(官方不建议用windows开发使用)

1.2 Redis的安装

1.2.1 windows安装

  • 下载安装包 https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100

  • 解压到特定文件

在这里插入图片描述

  • 开启Redis:双击运行运行服务(秒级启动)

在这里插入图片描述

认端口:6379

  • 使用Redis客户端连接

在这里插入图片描述

测试连接:

在这里插入图片描述

windows下使用确实简单,但是推荐使用linux去开发使用

1.2.2 Linux安装

1.2.2.1 下载安装包

redis-6.0.6.tar.gz

在这里插入图片描述

在这里插入图片描述

1.2.2.2 解压安装包

并进入

tar -zxvf redis-6.0.6.tar.gz

在这里插入图片描述

配置文件:redis.conf

1.2.2.3 基础环境安装

  1. yum install gcc-c++

  2. gcc -v

在这里插入图片描述

  1. make(需要一点时间)

在这里插入图片描述

报错,原因分析:gcc版本过低,升级一下

@H_95_404@# 升级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 # 升级后查看下版本 $ gcc -v

在这里插入图片描述

重新执行make

在这里插入图片描述

  1. make install (也可以不用执行,确认一下安装)

在这里插入图片描述

所有东西已安装好了

1.2.2.4 确认安装路径

Redis安装路径:/usr/local/bin

在这里插入图片描述

1.2.2.5 复制配置文件

复制Redis配置文件到当前目录下

@H_95_404@# 创建配置文件文件夹并复制配置文件 $ mkdir hconfig $ cp /opt/redis-6.0.6/redis.conf hconfig

在这里插入图片描述

之后就使用这个配置文件进行启动

1.2.2.6 配置后台启动

Redis认不是后台启动的,需要修改配置文件

@H_95_404@# 修改daemonize为yes $ vim redis.conf

在这里插入图片描述

1.2.2.7 启动Redis服务

启动Redis服务

通过指定的配置文件启动服务

redis-server hconfig/redis.conf

在这里插入图片描述

1.2.2.8 测试连接

测试连接

使用Redis客户端进行连接

@H_95_404@# 连接 $ redis-cli -p 6379 127.0.0.1:6379> ping PONG 127.0.0.1:6379> set name yunmx # 设置一个key OK 127.0.0.1:6379> get name # 读取一个key "yunmx" 127.0.0.1:6379> keys * # 查询所有的key 1) "name" 127.0.0.1:6379>

在这里插入图片描述

1.2.2.9 查看进程

查看Redis的进程是否开启

ps -ef |grep redis

在这里插入图片描述

1.2.2.10 关闭Redis服务

关闭redis服务

shutdown

@H_95_404@127.0.0.1:6379> ping PONG 127.0.0.1:6379> shutdown not connected> exit

在这里插入图片描述

1.3 Redis性能测试工具

1.3.1 redis-benchmark

官方自带性能测试工具—>

redis-benchmark

命令参数:

在这里插入图片描述

简单测试:

@H_95_404@# 测试100个并发连接,每个并发20个请求 $ redis-benchmark -h localhost -p 6379 -c 100 -n 20

在这里插入图片描述

1.3.2 如何分析

查看分析:

@H_95_404@====== PING_INLINE ====== 100000 requests completed in 1.46 seconds # 100000次ping请求在1.46s内完成 100 parallel clients # 100的并发量 3 bytes payload # 3字节数据写入 keep alive: 1 # 保证一台服务器测试(单机性能) host configuration "save": 900 1 300 10 60 10000 host configuration "appendonly": no multi-thread: no 0.00% <= 0.5 milliseconds # 0.5毫秒内完成百分之0的请求 1.37% <= 0.6 milliseconds 11.42% <= 0.7 milliseconds 23.22% <= 0.8 milliseconds 35.29% <= 0.9 milliseconds 47.60% <= 1.0 milliseconds 60.01% <= 1.1 milliseconds 72.40% <= 1.2 milliseconds 84.82% <= 1.3 milliseconds 94.78% <= 1.4 milliseconds 97.65% <= 1.5 milliseconds 98.50% <= 1.6 milliseconds 98.95% <= 1.7 milliseconds 99.28% <= 1.8 milliseconds 99.51% <= 1.9 milliseconds 99.67% <= 2 milliseconds 99.99% <= 3 milliseconds 100.00% <= 3 milliseconds # 3毫秒内完成100%的请求(100000) 68587.11 requests per second # 平均每秒完成的请求数

1.4 Redis基础知识

1.4.1 数据库

Redis数据库数量有16个,redis.conf配置文件可查看,认使用的数据库为0

在这里插入图片描述

在这里插入图片描述

1.4.2 切换数据库

@H_95_404@# 使用select 命令进行切换 [root@yunmx bin]# redis-cli -p 6379 127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> select 2 OK 127.0.0.1:6379[2]> select 16 (error) ERR DB index is out of range 127.0.0.1:6379[2]> select 15 OK 127.0.0.1:6379[15]>

1.4.3 查看数据库大小

@H_95_404@# DBSIZE 查看数据库大小 127.0.0.1:6379> set name hejie OK 127.0.0.1:6379> DBSIZE (integer) 1 127.0.0.1:6379> SET AGE NAN OK 127.0.0.1:6379> DBSIZE (integer) 2 127.0.0.1:6379>

1.4.4 查看所有的key

@H_95_404@# 使用keys * 127.0.0.1:6379> keys * 1) "AGE" 2) "name" 127.0.0.1:6379>

1.4.5 清空数据库

@H_95_404@# 清空当前数据库flushdb 127.0.0.1:6379> keys * 1) "AGE" 2) "name" 127.0.0.1:6379> flushdb OK 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> # 清空所有数据库flush 127.0.0.1:6379> select 0 OK 127.0.0.1:6379> set name hejie OK 127.0.0.1:6379> set key1 hejie1 OK 127.0.0.1:6379> set key2 hejie2 OK 127.0.0.1:6379> keys * 1) "key1" 2) "key2" 3) "name" 127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> set name yunmx OK 127.0.0.1:6379[1]> set key1 yunmx1 OK 127.0.0.1:6379[1]> set key2 yunmx2 OK 127.0.0.1:6379[1]> keys * 1) "key1" 2) "key2" 3) "name" 127.0.0.1:6379[1]> flushall OK 127.0.0.1:6379[1]> keys * (empty array) 127.0.0.1:6379[1]> select 0 OK 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379>

1.4.6 判断key是否存在

@H_95_404@# 使用exists 判断 127.0.0.1:6379> set name hejie OK 127.0.0.1:6379> set age 1 OK 127.0.0.1:6379> keys * 1) "age" 2) "name" 127.0.0.1:6379> exists age (integer) 1 # 存在 127.0.0.1:6379> exists name (integer) 1 127.0.0.1:6379> exists key1 (integer) 0 # 不存在 127.0.0.1:6379>

1.4.7 移动一个key

@H_95_404@# move移动一个key 127.0.0.1:6379> set key1 hejie OK 127.0.0.1:6379> set key2 hejie1 OK 127.0.0.1:6379> set key3 hejie2 OK 127.0.0.1:6379> move key1 1 # 将key1移动给数据库1 (integer) 1 127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> keys * 1) "key1" 127.0.0.1:6379[1]>

1.4.8 设置key的生效时间

@H_95_404@# EXPIRE 设置一个key的过期时间,单位时间:s # TTL 查看一个key的过期时间 127.0.0.1:6379> keys * 1) "key3" 2) "key2" 127.0.0.1:6379> get key3 "hejie2" 127.0.0.1:6379> EXPIRE key3 10 # 设置key3 10秒后过期 (integer) 1 127.0.0.1:6379> ttl key3 (integer) -2 # 表示已经过期 127.0.0.1:6379> ttl key3 (integer) -2 127.0.0.1:6379> keys * 1) "key2" 127.0.0.1:6379>

1.4.9 Redis单线程

单线程为什么这么快?

C语言写的,官方表示每秒的QPS是100000+,基于内存操作,Redis的瓶颈和服务器的内存和网络带宽有关,既然可以用单线程实现,就用单线程喽。

误区:

性能服务器一定是多线程的?

多线程一定比单线程效率高?

核心:Redis是将所有数据放在内存中的,所以说使用单线程去操作效率就是最高的,因为多线程会产生cpu上下文切换,消耗时间的操作,对于内存系统来说,没有上下文切换,效率就是最高的,多次读写都是在一个cpu上的

1.5 Redis数据类型

五大数据类型

Redis一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

在这里插入图片描述

使用场景:value除了是字符串还可以是我们的数字

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

1.5.1 String(字符串)

@H_95_404@### 常见用法 127.0.0.1:6379> set kye1 yunmx # 设置一个key OK 127.0.0.1:6379> get key1 # 获取一个key的值 (nil) 127.0.0.1:6379> keys * # 查看当前库的所有key,输出key名 1) "kye1" 127.0.0.1:6379> EXISTS kye1 # 判断key是否存在 (integer) 1 127.0.0.1:6379> EXISTS key1 (integer) 0 127.0.0.1:6379> APPEND kye1 "helloworld" # 向key追加内容,如果key不存在,就设置一个key (integer) 15 127.0.0.1:6379> get kye1 "yunmxhelloworld" 127.0.0.1:6379> STRLEN kye1 # 获取一个key的长度 (integer) 15 127.0.0.1:6379> ### 自增长用法 127.0.0.1:6379> set count 0 OK 127.0.0.1:6379> get count "0" 127.0.0.1:6379> incr count # 自增1 (integer) 1 127.0.0.1:6379> get count "1" 127.0.0.1:6379> incr count (integer) 2 127.0.0.1:6379> get count "2" 127.0.0.1:6379> decr count # 自减1 (integer) 1 127.0.0.1:6379> get count "1" 127.0.0.1:6379> decr count (integer) 0 127.0.0.1:6379> get count "0" 127.0.0.1:6379> incrby count 5 # 自增;步长5 (integer) 5 127.0.0.1:6379> get count "5" 127.0.0.1:6379> decrby count 5 # 自减;步长5 (integer) 0 127.0.0.1:6379> get count "0" ### 获取字符串范围 127.0.0.1:6379> set key1 yunmxhejie # 设置一个key OK 127.0.0.1:6379> getrange key1 0 4 # 获取从标0到4范围的字符串 "yunmx" 127.0.0.1:6379> getrange key1 0 -1 # 获取全部的字符串 "yunmxhejie" 127.0.0.1:6379> ### 替换 127.0.0.1:6379> set key1 yunmx123456 # 设置一个key OK 127.0.0.1:6379> SETRANGE key1 0 hejie # 从下标为0的位置开始替换 (integer) 11 127.0.0.1:6379> get key1 "hejie123456" 127.0.0.1:6379> ### 设置过期时间 127.0.0.1:6379> setex key1 10 yunmx # 设置一个key,过期时间为10秒 OK 127.0.0.1:6379> ttl key1 # 查看key的过期时间 (integer) 7 127.0.0.1:6379> ttl key1 # 返回-2表示已经过期 (integer) -2 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> ### 不存在设置(分布式锁中会经常用到) 127.0.0.1:6379> setnx key2 redis # setnx:不存在key值则创建 (integer) 1 127.0.0.1:6379> setnx key2 MysqL # setnx:存在key值则创建失败 (integer) 0 127.0.0.1:6379> keys * 1) "key2" 127.0.0.1:6379> ### 批量设置和获取 127.0.0.1:6379> mset key1 1 key2 2 key3 3 key4 4 # 同时设置多个值 OK 127.0.0.1:6379> keys * 1) "key1" 2) "key4" 3) "key3" 4) "key2" 127.0.0.1:6379> mget key1 key2 key3 key4 # 同时获取多个值 1) "1" 2) "2" 3) "3" 4) "4" 127.0.0.1:6379> msetnx key5 5 key4 4 # 原子性操作:要么一起成功,要么一起失败 (integer) 0 127.0.0.1:6379> get key5 (nil) 127.0.0.1:6379> ### 对象 127.0.0.1:6379> set user:1 {name:yunmx,age:25} # 普通设置对象:key加json字符串 OK 127.0.0.1:6379> get user:1 "{name:yunmx,age:25}" 127.0.0.1:6379> mset user:1:name yunmx user:1:age 25 # 巧妙设计对象;如此设计,是完全可以的 OK 127.0.0.1:6379> mget user:1:name user:1:age 1) "yunmx" 2) "25" ### 组合命令 127.0.0.1:6379> getset db MysqL # 如果不存在,则设置 (nil) 127.0.0.1:6379> get db "MysqL" 127.0.0.1:6379> getset db MongoDB # 如果存在,返回当前值,再设置新的值 "MysqL" 127.0.0.1:6379> get db "MongoDB" 127.0.0.1:6379>

1.5.2 List(列表)

基本的数据类型

在这里插入图片描述

基本的数据类型,列表

在redis里面,list可以完成栈、队列、阻塞队列!

用法

  • 他实际是一个链表,before node after ,left right都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,代表不存在
  • 两边插入或者改动值,效率最高;修改中间元素,相对来说,效率偏低一点

消息排队、消息队列(lpush Rpop) 栈(lpush lpop)

@H_95_404@### 基础命令 127.0.0.1:6379> LPUSH key1 1 # 将一个值或者多个值添加到头部 (integer) 1 127.0.0.1:6379> LPUSH key1 2 (integer) 2 127.0.0.1:6379> LPUSH key1 3 (integer) 3 127.0.0.1:6379> LRANGE key1 0 -1 1) "3" 2) "2" 3) "1" 127.0.0.1:6379> LRANGE key1 0 1 1) "3" 2) "2" 127.0.0.1:6379> rpush key1 5 # 将一个添加到尾部 (integer) 4 127.0.0.1:6379> LRANGE key1 0 -1 1) "3" 2) "2" 3) "1" 4) "5" ### 移除元素 127.0.0.1:6379> LRANGE key1 0 -1 1) "5" 2) "4" 3) "3" 4) "2" 5) "1" 127.0.0.1:6379> LPOP key1 # 移除列表的第一个元素 "5" 127.0.0.1:6379> rPOP key1 # 移除列表的最后一个元素 "1" 127.0.0.1:6379> LRANGE key1 0 -1 1) "4" 2) "3" 3) "2" ### 获取元素、列表长度 127.0.0.1:6379> LRANGE key1 0 -1 1) "4" 2) "3" 3) "2" 127.0.0.1:6379> LINDEX key1 0 # 获取列表中的第一个元素 "4" 127.0.0.1:6379> LINDEX key1 1 "3" 127.0.0.1:6379> LINDEX key1 2 "2" 127.0.0.1:6379> LINDEX key1 3 (nil) 127.0.0.1:6379> LLEN key1 # 获取列表长度 (integer) 3 ### 移除指定的值 127.0.0.1:6379> LRANGE key1 0 -1 1) "2" 2) "4" 3) "4" 4) "3" 5) "2" 127.0.0.1:6379> LREM key1 1 2 # 移除一个2 (integer) 1 127.0.0.1:6379> LRANGE key1 0 -1 1) "4" 2) "4" 3) "3" 4) "2" 127.0.0.1:6379> LREM key1 2 4 # 移除2个4 (integer) 2 127.0.0.1:6379> LRANGE key1 0 -1 1) "3" 2) "2" ### 截取 127.0.0.1:6379> LRANGE key1 0 -1 1) "15" 2) "14" 3) "13" 4) "12" 5) "11" 6) "10" 127.0.0.1:6379> LTRIM key1 0 3 # 通过下标截取指定的长度,list已经被改变了 OK 127.0.0.1:6379> LRANGE key1 0 -1 1) "15" 2) "14" 3) "13" 4) "12" ### 组合命令:rpoppush 127.0.0.1:6379> rpush list1 1 2 3 4 5 (integer) 5 127.0.0.1:6379> LRANGE list1 0 -1 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 127.0.0.1:6379> rpoplpush list1 list2 # 移除列表中的最后一个元素,并加入到新的列表中 "5" 127.0.0.1:6379> LRANGE list1 0 -1 1) "1" 2) "2" 3) "3" 4) "4" 127.0.0.1:6379> LRANGE list2 0 -1 1) "5" 127.0.0.1:6379> ### 元素更新 127.0.0.1:6379> EXISTS list1 # 判断列表是否存在 (integer) 0 127.0.0.1:6379> lset list1 0 yunmx # 不存在报错 (error) ERR no such key 127.0.0.1:6379> lpush lista1 1 2 3 4 (integer) 4 127.0.0.1:6379> LRANGE list1 0 0 (empty array) 127.0.0.1:6379> LRANGE list1 0 -1 (empty array) 127.0.0.1:6379> LRANGE lista1 0 0 1) "4" 127.0.0.1:6379> lset lista1 0 yunmx # 存在修改当前下标的值 OK 127.0.0.1:6379> LRANGE lista1 0 0 1) "yunmx" ### 元素插入 127.0.0.1:6379> LRANGE lista1 0 -1 1) "yunmx" 2) "3" 3) "2" 4) "1" 127.0.0.1:6379> LINSERT lista1 before yunmx hejie # 插入当前元素的前面 (integer) 5 127.0.0.1:6379> LINSERT lista1 after yunmx hejie1 # 插入当前元素的后面 (integer) 6 127.0.0.1:6379> LRANGE lista1 0 -1 1) "hejie" 2) "yunmx" 3) "hejie1" 4) "3" 5) "2" 6) "1"

1.5.3 Set(集合)

set中的值不能重复d ,是无序的

@H_95_404@### 基础用法 127.0.0.1:6379> sadd set1 yunmx # 往集合里面存值 (integer) 1 127.0.0.1:6379> sadd set1 hejie1 (integer) 1 127.0.0.1:6379> sadd set1 hejie01 (integer) 1 127.0.0.1:6379> sadd set1 hejie02 (integer) 1 127.0.0.1:6379> sadd set1 hejie02 (integer) 0 127.0.0.1:6379> SMEMBERS set1 # 查询集合里面所有的元素 1) "yunmx" 2) "hejie02" 3) "hejie01" 4) "hejie1" 127.0.0.1:6379> SISMEMBER set1 hejie # 判断元素是否存在 (integer) 0 127.0.0.1:6379> SISMEMBER set1 hejie5 (integer) 0 127.0.0.1:6379> SISMEMBER set1 hejie1 # 存在返回1 (integer) 1 ### 获取集合中的元素的个数 127.0.0.1:6379> SCARD set1 (integer) 4 127.0.0.1:6379> sadd set1 hejie09 (integer) 1 127.0.0.1:6379> SCARD set1 (integer) 5 ### 移除set集合中的指定元素 127.0.0.1:6379> SREM set1 yunmx # 移除set集合中的:yunmx (integer) 1 127.0.0.1:6379> SCARD set1 (integer) 4 127.0.0.1:6379> SMEMBERS set1 1) "hejie01" 2) "hejie09" 3) "hejie1" 4) "hejie02" ### 抽随机用法(无序不重复) 127.0.0.1:6379> SRANDMEMBER set1 # 随机抽选出一个元素 "hejie02" 127.0.0.1:6379> SRANDMEMBER set1 "hejie09" 127.0.0.1:6379> SRANDMEMBER set1 "hejie02" 127.0.0.1:6379> SRANDMEMBER set1 "hejie09" 127.0.0.1:6379> SRANDMEMBER set1 "hejie01" 127.0.0.1:6379> SRANDMEMBER set1 2 # 随机抽选出指定的数量元素 1) "hejie1" 2) "hejie01" 127.0.0.1:6379> SRANDMEMBER set1 2 1) "hejie09" 2) "hejie02" 127.0.0.1:6379> SRANDMEMBER set1 2 1) "hejie1" 2) "hejie01" ### 随机移除元素 127.0.0.1:6379> SMEMBERS set1 1) "hejie01" 2) "hejie09" 3) "hejie1" 4) "hejie02" 127.0.0.1:6379> spop set1 # 随机移除一个元素 "hejie02" 127.0.0.1:6379> spop set1 "hejie1" 127.0.0.1:6379> SMEMBERS set1 1) "hejie01" 2) "hejie09" ### 指定的值移动另一个集合中 127.0.0.1:6379> SMEMBERS set1 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> SMEMBERS set2 1) "er" 2) "san" 3) "yi" 127.0.0.1:6379> smove set1 set2 1 # 将set1中的元素移动到set2,需要移动的值为set1中的1 (integer) 1 127.0.0.1:6379> SMEMBERS set1 1) "2" 2) "3" 127.0.0.1:6379> SMEMBERS set2 1) "1" 2) "er" 3) "san" 4) "yi" ### 共同关注:B站,微博(并集) # 数字集合:差集、交集、并集 127.0.0.1:6379> sadd set1 1 2 3 4 5 6 (integer) 6 127.0.0.1:6379> sadd set2 4 5 6 7 8 9 (integer) 6 127.0.0.1:6379> 127.0.0.1:6379> SDIFF set1 set2 # 差集:set1中的元素不存在于set2 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> SINTER set1 set2 # 交集 1) "4" 2) "5" 3) "6" 127.0.0.1:6379> SDIFF set2 set1 1) "7" 2) "8" 3) "9" 127.0.0.1:6379> SUNION set1 set2 # 并集 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6" 7) "7" 8) "8" 9) "9"

1.5.4 Hash(哈希)

Map集合,key-Map集合,本质和string没有太大区别,还是一个简单的key-vlaue

应用

  • 用户信息之类的
  • 经常变动的信息
  • 更适合存储对象,string更适合字符串存储
@H_95_404@### 基础用法 127.0.0.1:6379> hset hash1 name yunmx # 设置一个key-vlaue (integer) 1 127.0.0.1:6379> hget hash1 name # 获取一个字段值 "yunmx" 127.0.0.1:6379> hmset hash1 name hejie age 28 # 设置多个 OK 127.0.0.1:6379> hgetall hash1 # 获取所有元素 1) "name" 2) "hejie" 3) "age" 4) "28" 127.0.0.1:6379> hget hash1 age # 获取一个字段值 "28" ### 删除一个值 127.0.0.1:6379> HDEL hash1 name # 删除指定的key字段 (integer) 1 127.0.0.1:6379> hgetall hash1 1) "age" 2) "28" ### 获取hash所有的长度(键值对) 127.0.0.1:6379> HLEN hash1 # 获取hash中的键值对数量 (integer) 1 127.0.0.1:6379> hset hash1 age 18 (integer) 0 127.0.0.1:6379> hset hash1 age1 19 (integer) 1 127.0.0.1:6379> hset hash1 name hejie (integer) 1 127.0.0.1:6379> HLEN hash1 (integer) 3 ### 判断hash中字段是否存在 127.0.0.1:6379> HGETALL hash1 1) "age" 2) "18" 3) "age1" 4) "19" 5) "name" 6) "hejie" 127.0.0.1:6379> HEXISTS hash1 age # 判断哈希中是否存在age字段 (integer) 1 127.0.0.1:6379> HEXISTS hash1 yunmx (integer) 0 ### 只获取所有的字段或值 127.0.0.1:6379> hkeys hash1 1) "age" 2) "age1" 3) "name" 127.0.0.1:6379> HVALS hash1 1) "18" 2) "19" 3) "hejie" ### 指定自增 127.0.0.1:6379> HINCRBY hash1 age 1 # 自增1 (integer) 19 127.0.0.1:6379> HINCRBY hash1 age -1 (integer) 18 127.0.0.1:6379> hsetnx hash1 age 1 # 如果存在,不设置 (integer) 0 127.0.0.1:6379> hsetnx hash1 age2 1 # 不存在,则添加新的键值对 (integer) 1

1.5.5 Zset(有序集合)

在set的基础上增加一个值,多了一个计数位

场景思路:

  • set能做的东西它都能做,存储班级成绩表、工资表排序
  • 普通消息1,重要消息2,带权重进行判断
  • 排行榜应用实现:TOP N
@H_95_404@### 基础用法 127.0.0.1:6379> zadd myset 1 one # 添加一个值 (integer) 1 127.0.0.1:6379> zadd myset 2 two (integer) 1 127.0.0.1:6379> zadd myset 3 three (integer) 1 127.0.0.1:6379> zadd myset 4 four 5 five # 添加多个值 (integer) 2 127.0.0.1:6379> ZRANGE myset 0 -1 # 查看所有的值 1) "one" 2) "two" 3) "three" 4) "four" 5) "five" ### 排序如何实现 # 用法:从最小到最大 # ZRANGEBYscore key min max [WITHscoreS] [LIMIT offset count] 127.0.0.1:6379> zadd salary 12000 yunmx # 添加4个用户 (integer) 1 127.0.0.1:6379> zadd salary 11000 hejie (integer) 1 127.0.0.1:6379> zadd salary 10000 hejie01 (integer) 1 127.0.0.1:6379> zadd salary 9000 yunmx01 (integer) 1 127.0.0.1:6379> ZRANGEBYscore salary -inf +inf # -inf +inf 负无穷到正无穷 从小到大 1) "yunmx01" 2) "hejie01" 3) "hejie" 4) "yunmx" 127.0.0.1:6379> ZRANGEBYscore salary -inf +inf withscores # withscores表示带一些其他参数说明 1) "yunmx01" 2) "9000" 3) "hejie01" 4) "10000" 5) "hejie" 6) "11000" 7) "yunmx" 8) "12000" ### 移除元素 127.0.0.1:6379> ZRANGE salary 0 -1 # 查看所有的元素 1) "yunmx01" 2) "hejie01" 3) "hejie" 4) "yunmx" 127.0.0.1:6379> zrem salary yunmx01 # 移除特定的元素 (integer) 1 127.0.0.1:6379> ZRANGE salary 0 -1 1) "hejie01" 2) "hejie" 3) "yunmx" ### 获取有序集合中的个数 127.0.0.1:6379> ZRANGE salary 0 -1 1) "hejie01" 2) "hejie" 3) "yunmx" 127.0.0.1:6379> ZCARD salary (integer) 3 ### 按区间计算 127.0.0.1:6379> ZRANGE salary 0 -1 1) "hejie01" 2) "hejie" 3) "yunmx" 127.0.0.1:6379> zcount salary 1000 9000 # 查看1000到9000之间的数量 (integer) 0 127.0.0.1:6379> zcount salary 1000 10000 # 查看1000到1-000之间的数量值 (integer) 1

1.5.6 geospatial(地理空间)

城市经纬度查询:http://www.jsons.cn/lngcode/

  • 朋友圈的定位
  • 附近的人
  • 打车距离计算

GEO实现的底层原理其实就是Zset!可以使用Zset来操作GEO!

只有6个命令

  • GEOADD
  • GEOdisT
  • GEOHASH
  • GEOPOS
  • GEORADIUS
  • GEORADIUSBYMEMBER
@H_95_404@### 添加位置 # geoadd key longitude(经度) latitude(维度) member [longitude latitude member ...] 可添加多个 # 参数:key 值(经度 维度 名称) 127.0.0.1:6379> geoadd china:city 104.065735 30.659462 chengdu (integer) 1 127.0.0.1:6379> geoadd china:city 106.504962 29.533155 chongqing (integer) 1 ### 获取当前定位经纬度 127.0.0.1:6379> GEOPOS china:city chengdu # 获取成都的经纬度 1) 1) "104.06573742628097534" 2) "30.65946118872339099" 127.0.0.1:6379> GEOPOS china:city chongqing # 获取重庆的经纬度 1) 1) "106.50495976209640503" 2) "29.53315530684997015" ### 计算两个地方的距离 #单位:m/km/mi/ft 127.0.0.1:6379> geodist china:city chengdu chongqing m # 计算出成都到重庆的直线距离 单位m "266056.2971" 127.0.0.1:6379> geodist china:city chengdu chongqing km # 计算出成都到重庆的直线距离 单位km "266.0563" ### 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。 # GEORADIUS: 127.0.0.1:6379> GEORADIUS china:city 103 30 1 km # 以103 30这个点为中心,以1km为半径 (empty array) 127.0.0.1:6379> GEORADIUS china:city 103 30 2 km (empty array) 127.0.0.1:6379> GEORADIUS china:city 103 30 20 km (empty array) 127.0.0.1:6379> GEORADIUS china:city 103 30 200 km 1) "chengdu-qingyangqu" 2) "chengdu" 3) "chengdu-jinjiangqu" 127.0.0.1:6379> GEORADIUS china:city 103 30 20000 km 1) "chengdu-qingyangqu" 2) "chongqing" 3) "chengdu" 4) "chengdu-jinjiangqu" 127.0.0.1:6379> GEORADIUS china:city 103 30 20000 km withcoord # 附带显示经纬度 1) 1) "chengdu-qingyangqu" 2) 1) "104.06151026487350464" 2) "30.67387107851423167" 2) 1) "chongqing" 2) 1) "106.50495976209640503" 2) "29.53315530684997015" 3) 1) "chengdu" 2) 1) "104.06573742628097534" 2) "30.65946118872339099" 4) 1) "chengdu-jinjiangqu" 2) 1) "104.06573742628097534" 2) "30.65946118872339099" ### 找出位于指定范围内的元素,中心点是由给定的位置元素决定 # GEORADIUSBYMEMBER 127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 10 km # chengdu城市方圆10km的坐标 1) "chengdu" 2) "chengdu-jinjiangqu" 3) "chengdu-qingyangqu" 127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 100 km 1) "chengdu" 2) "chengdu-jinjiangqu" 3) "chengdu-qingyangqu" 127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 10 km 1) "chengdu" 2) "chengdu-jinjiangqu" 3) "chengdu-qingyangqu" 127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 1000 km 1) "chengdu-qingyangqu" 2) "chongqing" 3) "chengdu" 4) "chengdu-jinjiangqu" 127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 1000 km withcoord # 附带坐标信息 1) 1) "chengdu-qingyangqu" 2) 1) "104.06151026487350464" 2) "30.67387107851423167" 2) 1) "chongqing" 2) 1) "106.50495976209640503" 2) "29.53315530684997015" 3) 1) "chengdu" 2) 1) "104.06573742628097534" 2) "30.65946118872339099" 4) 1) "chengdu-jinjiangqu" 2) 1) "104.06573742628097534" 2) "30.65946118872339099" ### Redis GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示 不常用

1.5.7 HyperLogLog(基数统计

基数:不重复的元素个数,可以接受误差

A(1 2 3 45 5 78)

B(3 4 6 7 8 9 0 9 78 )

基数统计的算法

  • 网页的UV(一个人访问一个网站,但是还是算做一个

    传统的方式:set保存用户的ID,然后就可以统计set中的元素数量作为标准判断,如果保存大量的用户ID,就会比较麻烦,我们的目的是为了计数而不是保存ID

  • 占用内存是固定的,2^64不同的元素技术,只需要废12KB内存

0.81%的错误率!统计UV时候,可以忽略不计

@H_95_404@127.0.0.1:6379> PFADD key1 a 1 2 3 4 b c 6 7 9 # 设置一个HyperLOGLOG (integer) 1 127.0.0.1:6379> PFCOUNT key1 # 查看里面不重复的数量个数 (integer) 10 127.0.0.1:6379> PFADD key2 a a 1 2 3 4 b c 6 7 9 10 10 101 10 10 8 6 (integer) 1 127.0.0.1:6379> PFCOUNT key2 (integer) 13 127.0.0.1:6379> PFMERGE key3 key1 key2 # 合并两个key的元素数量 OK 127.0.0.1:6379> PFCOUNT key3 # 查看不重复的元素数量 (integer) 13

1.5.8 Bitmap(位图)

位存储0和1

2个状态的都可以使用此数据类型

  • 活跃、不活跃
  • 感染人数、未感染人数
@H_95_404@127.0.0.1:6379> SETBIT key 0 1 # 使用Bitmaps记录一周的打卡 (integer) 0 127.0.0.1:6379> SETBIT key 1 0 (integer) 0 127.0.0.1:6379> SETBIT key 2 0 (integer) 0 127.0.0.1:6379> SETBIT key 3 1 (integer) 0 ## 查看某一天是否有打卡 127.0.0.1:6379> getbit key 3 (integer) 1 127.0.0.1:6379> getbit key 2 (integer) 0 ### 统计操作 # 统计打开天数 127.0.0.1:6379> BITCOUNT key (integer) 2

1.6 Redis事务

事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行

1.6.1 基本的事务操作

Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,事务执行中,按照顺序执行

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

  • 一次性
  • 顺序性
  • 排他性
  • 没有隔离级别的概念,没有幻读、脏读…
@H_95_404@---入队--- command1 command2 command2 ---执行---

三个阶段:

  1. 开启事务(MULTI)
  2. 命令入队(commands)
  3. 执行事务(EXEC)
@H_95_404@### 正常执行 127.0.0.1:6379> MULTI # 开启事务 OK 127.0.0.1:6379> set key1 1 # 命令入队 QUEUED 127.0.0.1:6379> set key2 2 QUEUED 127.0.0.1:6379> set key3 3 QUEUED 127.0.0.1:6379> get key1 QUEUED 127.0.0.1:6379> get key2 QUEUED 127.0.0.1:6379> exec # 事务执行,按照顺序执行 1) OK 2) OK 3) OK 4) "1" 5) "2" ### 放弃事务 127.0.0.1:6379> MULTI # 开启事务 OK 127.0.0.1:6379> set key1 1 QUEUED 127.0.0.1:6379> set key2 2 QUEUED 127.0.0.1:6379> set key3 3 QUEUED 127.0.0.1:6379> disCARD # 取消事务 OK 127.0.0.1:6379> exec (error) ERR EXEC without MULTI 127.0.0.1:6379> get key1 # 放弃事务取不到相关的值 (nil) ### 事务错误一:命令有错,事务中所有的命令都不会被执行! 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key1 1 QUEUED 127.0.0.1:6379> set key2 2 QUEUED 127.0.0.1:6379> getset key3 # 错误的命令,直接报错 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379> set key3 3 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of prevIoUs errors. # 执行事务报错 127.0.0.1:6379> get key1 # 结果是所有的命令都不会执行 (nil) ### 事务错误二:运行异常,如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key1 "yunmx" QUEUED 127.0.0.1:6379> incr key1 QUEUED 127.0.0.1:6379> set key2 2 QUEUED 127.0.0.1:6379> set key3 3 QUEUED 127.0.0.1:6379> get key3 QUEUED 127.0.0.1:6379> exec 1) OK 2) (error) ERR value is not an integer or out of range # 代码结构没问题,抛出异常 3) OK 4) OK 5) "3" 127.0.0.1:6379> get key2 "2"

1.6.2 乐观锁

监控:watch

悲观锁:很悲观;认为任何时候都会出现问题,无论做什么都加锁,影响性能

乐观锁:很乐观,任务任何时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间,是否有人修改过这个数据,MysqL用的version字段!

@H_95_404@### Redis的测试监视 127.0.0.1:6379> set mymoney 1000 OK 127.0.0.1:6379> set myout 0 OK 127.0.0.1:6379> WATCH mymoney # 监视 OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRby mymoney 20 QUEUED 127.0.0.1:6379> INCRBY myout 20 QUEUED 127.0.0.1:6379> exec # 事务正常结束,数据期间没有发生变动,正常执行了 1) (integer) 980 2) (integer) 20 127.0.0.1:6379> ### 使用watch当乐观锁,测试多线程修改值后事务提交失败 # 第一个进程开启事务 127.0.0.1:6379> watch mymoney OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRBY mymoney 10 QUEUED 127.0.0.1:6379> INCRBY myout 10 QUEUED 127.0.0.1:6379> exec # 未提交前执行第二个事务进行数据的修改 (nil) # 事务提交后返回修改失败, 127.0.0.1:6379> # 第二个进程修改我们的值 127.0.0.1:6379> get mymoney "980" 127.0.0.1:6379> set mymoney 20 OK 127.0.0.1:6379> get mymoney "20" 127.0.0.1:6379> set mymoney 1000 OK 127.0.0.1:6379> get mymoney "1000" ### 可以使用unwatch解锁,再次使用watch获取最新的值,再去执行事务

1.7 Redis配置文件详解

1.7.1 redis.conf

启动时候就是通过配置文件来启动的

配置文件对大小写不敏感

在这里插入图片描述

包含其他配置文件

在这里插入图片描述

网络

在这里插入图片描述

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

GENERAL通用配置

在这里插入图片描述

@H_95_404@daemonize yes # 已守护进程方式运行,认是no,我们需要自己开启 pidfile /var/run/redis_6379.pid # 如果以后台的方式运行,我们就需要指定一个pid文件 # 日志(4个级别) # Specify the server verbosity level. # This can be one of: # debug (a lot of @R_125_4045@ion, 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 可以不显示显示

快照SNAPSHottING

在这里插入图片描述

@H_95_404@# 规则 save 900 1 # 如果900秒内,至少有一个key进行修改,我们就进行持久化操作 save 300 10 save 60 10000 # 我们可以设置自己的一个规则 stop-writes-on-bgsave-error yes # 持久化出现错误的时候,是否继续进行工作,认是yes rdbcompression yes # 是否压缩rdb文件,压缩就会消耗cpu资源 rdbchecksum yes # 是否校验rdb文件,出错会进行修复 dir ./ # rdb文件保存的目录

快照

持久化,在规定的时间内,执行了多少次操作,则会持久化到文件,.rdb .aof,如果没有持久化,那么数据就会断电及失

主从复制 REPLICATION

在这里插入图片描述

安全Security

在这里插入图片描述

@H_95_404@# requirepass foobared 认没有密码 # 可以通过修改配置文件设置密码 # 比如: requirepass 123456 # 验证 127.0.0.1:6379> ping (error) NOAUTH Authentication required. 127.0.0.1:6379> auth 123456 OK 127.0.0.1:6379> ping PONG 127.0.0.1:6379> # 也可以通过命令进行密码设置 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "123456" 127.0.0.1:6379> config set requirepass "110" OK 127.0.0.1:6379> ping (error) NOAUTH Authentication required. 127.0.0.1:6379> auth 110 OK 127.0.0.1:6379> ping PONG 127.0.0.1:6379>

客户端CLIENTS

在这里插入图片描述

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

APPEND ONLY MODE模式 aof配置

在这里插入图片描述

@H_95_404@appendonly no # 认不开启aof模式,认使用的是rdb方式持久化的 appendfilename "appendonly.aof" # 持久化的文件的名字 # appendfsync always # 每次修改值都会写入,速度比较慢 appendfsync everysec # 每秒执行一次sync,可能会丢失1s的数据! # appendfsync no # 不同步,这个时候操作系统自己同步数据,速度最快!

1.8 Redis持久化

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

1.8.1 RDB(Redis DataBase)

RDB

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

操作

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

rdb保存的文件是dump.rdb

在这里插入图片描述

在这里插入图片描述

@H_95_404@# 触发机制 1.save的规则满足的情况下 2.执行了FLUSHALL命令 3.退出redis也会触发

如何恢复rdb文件

  1. 只需要将rdb文件放在redis启动目录下即可,启动的时候会自动检查dump.rdb恢复其中的数据
@H_95_404@# 查看存放的位置 127.0.0.1:6379> config get dir 1) "dir" 2) "/usr/local/bin"

优缺点

优点

  1. 适合大规模的数据恢复
  2. 如果你对数据的完整性要求不高

缺点

  1. 需要一定的时间间隔进行操作!如果redis意外宕机,最后一次修改的数据就没有了
  2. fork进程的时候,会占用一定的内存空间

1.8.2 AOF(Append Only File)

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

AOF

以日志的形式来记录每个操作,将redis执行过的所有指令记录下来(读操作不会记录),只许追加文件但不可改写文件,redis启动之后会读取文件重新构建数据,换言之,redis重启的话根据日志文件内容将写指令从前到 后执行一次以完成数据的恢复工作

aof保存的文件是appendonly.aof文件

在这里插入图片描述

@H_95_404@# 破坏aof文件后,尝试连接 [root@yunmx bin]# redis-cli Could not connect to Redis at 127.0.0.1:6379: Connection refused # 使用检测文件自动修复这个AOF文件:redis-check-aof [root@yunmx bin]# redis-check-aof --fix appendonly.aof 0x 62: Expected \r\n, got: 6173 AOF analyzed: size=122, ok_up_to=23, diff=99 This will shrink the AOF from 122 bytes, with 99 bytes, to 23 bytes Continue? [y/N]: y Successfully truncated AOF

优缺点

优点

  1. 每次修改都同步,文件完整性更好,
  2. 每秒同步一次,这样会丢失一秒的数据
  3. 从不同步,效率最高的!

缺点

  1. 相对于数据文件来说,aof远远大于rdb,修复的速度比rdb慢
  2. aof运行效率要比rdb慢,会进行IO操作

扩展:

  • RBD
    RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。

  • AOF
    AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。

  • 只做缓存
    如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.。

  • 同时开启两种持久化方式

    • 在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整
    • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
  • 性能建议

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

1.9 Redis发布订阅

1.9.1 Redis发布订阅

订阅、发布消息图:

在这里插入图片描述

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息

Redis客户端可以订阅任意数量的频道

一个是:消息发送者 第二个是:频道 第三个是:消息订阅

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

在这里插入图片描述

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

在这里插入图片描述

Redis 发布订阅命令
下表列出了 redis 发布订阅常用命令:

  • PSUBSCRIBE pattern [pattern …]
    订阅一个或多个符合给定模式的频道。
  • PUBSUB subcommand [argument [argument …]]
    查看订阅与发布系统状态。
  • PUBLISH channel message
    将信息发送到指定的频道。
  • PUNSUBSCRIBE [pattern [pattern …]]
    退订所有给定模式的频道。
  • SUBSCRIBE channel [channel …]
    订阅给定的一个或多个频道的信息。
  • UNSUBSCRIBE [channel [channel …]]
    指退订给定的频道。
@H_95_404@# 测试 # 订阅一个频道 127.0.0.1:6379> SUBSCRIBE yunmx # 订阅一个频道到yunmx Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "yunmx" 3) (integer) 1 # 新窗口发送一个消息:发布者 [root@yunmx ~]# redis-cli 127.0.0.1:6379> PUBLISH yunmx test # 发布者发布消息到指定名称的频道 (integer) 1 # 接收到的消息 127.0.0.1:6379> SUBSCRIBE yunmx Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "yunmx" 3) (integer) 1 1) "message" # 接收的消息 2) "yunmx" # 哪个频道 3) "test" # 消息内容

Redis发布订阅的原理

使用C实现的,通过分析Redis源码里面的pubsub.c文件,了解发布和订阅机制的底层实现逻辑,加深对redis的理解。(俺也不懂C,俺也不是开发,咋整)

通过PUBLISH/SUBSCRIBE/PSUBSCRIBE等命令实现发布订阅功能

  • SUBSCRIBE:通过此命令订阅频道以后,redis-server里维护一个字典,字典的键就是一个个channel(频道),而字典的值则是一个链表,链表中保存了所有订阅这个频道的客户端。-----就是将客户端添加到给定channel的订阅链表中
  • PUBLISH:向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查询了记录了订阅这个频道所有的客户端连接,遍历整个链表,将消息发送给所有订阅
  • PUB、SUB:就是发布、订阅,在redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值进行了消息发布以后,所有订阅它的客户端都会收到相应的消息,这一功能明显的用法就是用作实时消息系统,比如普通的即时聊天、群聊等功能

1.9.2 使用场景

  • 实时消息系统
  • 实时聊天------频道当作聊天室,将信息回显给所有人即可
  • 订阅,公众号订阅
  • 稍微复杂的场景我们会使用消息中间件(MQ)

2.0 Redis主从复制

2.0.1 概念

主从复制

是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。master以写为主,slave以读为主。

认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量
  4. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

一般来说,要将redis运用于生产项目中,只使用一台redis是万万不能的

  • 从结构上,单个redis服务器会发生单点故障,且单机处理所有的请求会导致负载过大
  • 从容量上,单机内存容量有限,一般来说,单台redis最大使用内存不应该超过20G

在这里插入图片描述

2.0.2 配置主从

主从复制,读写分离!80%的情况都是在进行读操作!减缓服务器的压力,架构中经常使用!一主二 从!

测试场景

环境配置

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

@H_95_404@# 启动一个redis,查看信息 127.0.0.1:6379> info replication # Replication role:master # 角色 connected_slaves:0 # 从机数量 master_replid:fa0e795a3e369ed7e46a40ee8818a51255ab6df3 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

复制一个配置文件修改一下端口号、日志保存的文件名、rdb文件名、pid文件 ,在启动2个redis

@H_95_404@[root@yunmx bin]# redis-server redis-conf/redis.conf1 # 6380 6038:C 12 Dec 2021 13:06:38.166 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 6038:C 12 Dec 2021 13:06:38.166 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=6038, just started 6038:C 12 Dec 2021 13:06:38.166 # Configuration loaded [root@yunmx bin]# redis-server redis-conf/redis.conf2 # 6381 6045:C 12 Dec 2021 13:06:39.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 6045:C 12 Dec 2021 13:06:39.641 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=6045, just started 6045:C 12 Dec 2021 13:06:39.641 # Configuration loaded

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

未配置主从之前,三个节点都是主节点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

认老大

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

@H_95_404@# 6380配置 127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # 认本机的6379端口服务的redis做老大 OK 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:8 master_sync_in_progress:0 slave_repl_offset:0 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 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=168,lag=0 # 从机的信息 slave1:ip=127.0.0.1,port=6381,state=online,offset=168,lag=0 master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:168 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:168

真实的主从配置应该在配置文件中配置,这样的话是永久的,上述使用的是命令,只是暂时的

在这里插入图片描述

@H_95_404@replicaof <masterip> <masterport> # 主机的IP地址和端口号 masterauth <master-password> # 如果设定有密码,配置密码即可

2.0.3 特性验证

主机可以设置值,从机不能写,主机中所有信息和数据,从机都会保存

@H_95_404@# 主机设置一个key 127.0.0.1:6379> set key1 yunmx OK 127.0.0.1:6379> # 从机中也会有,从机无法设置key 127.0.0.1:6380> keys * 1) "key1" 127.0.0.1:6380> get key1 "yunmx" 127.0.0.1:6380> set key2 yunmx2 (error) READONLY You can't write against a read only replica. 127.0.0.1:6380>

老大宕机后,从机还是从机,只是会显示主机状态不正常;主机恢复,从机依旧可以直接获取到主机写入的信息,保证了一定的高可用性

如果使用的是我们命令行配置的主从,如果从机宕机后,从机就会脱离主从了,需要再次命令行配置从机,变成从机以后,就能获取到key的值了

@H_95_404@# 停掉6379主机服务,查看从机集群状态 127.0.0.1:6380> info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1065 master_link_down_since_seconds:10 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1065 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:1065 # 恢复6379主机服务,验证是否还会同步数据 [root@yunmx ~]# redis-cli -p 6379 127.0.0.1:6379> set key2 yunmx2 # 恢复主节点,设置一个key OK 127.0.0.1:6379> # 从节点读取主机恢复后设置的key 127.0.0.1:6380> get key2 # 能够正常读取 "yunmx2" # 停止从机服务,主机设定一个key,在恢复从机,进行验证 127.0.0.1:6379> set key3 yunmx3 OK 127.0.0.1:6380> get key3 # 从机无法获得key3的数据 (nil) 127.0.0.1:6381> get key3 "yunmx3"

2.0.4 复制原理

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

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

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

增量复制:Mster继续将新的所有收集到的修改命令依次传给slave,完成同步

但是只要重新连接master,一次完全同步将被自动执行!我们的数据一定可以在从机中看到!

2.0.5 层层链路

一个M链接一个S!

可以完成主从复制!

在这里插入图片描述

如果没有79,这个时候能不能选择一个老大出来呢,这个时候需要手动去配置!

谋朝篡位:slaveof no one 使自己变成主机!如果老大回来了,也是需要手动配置

2.1 Redis哨兵模式

自动版选老大的模式

2.1.1 概述

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

  • 能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

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

2.1.2 基本架构

在这里插入图片描述

哨兵模式的作用:

在这里插入图片描述

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

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

在这里插入图片描述

使用哨兵模式,至少都会启动6个进程

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

2.1.3 场景测试

我们目前测试的架构是一主二从

配置哨兵模式的配置文件

@H_95_404@# 新建配置文件并编辑以下内容 sentinel monitor myredis 127.0.0.1 6379 1# 语法:sentinel monitor 被监控的名称 host port 1(1代表主机宕机后,从机投票让谁来接替成为主机,)

启动哨兵

@H_95_404@[root@yunmx bin]# redis-sentinel redis-conf/sentinel.conf # 启动一个哨兵 12680:X 12 Dec 2021 15:13:42.570 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 12680:X 12 Dec 2021 15:13:42.570 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=12680, just started 12680:X 12 Dec 2021 15:13:42.570 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.0.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 12680 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 12680:X 12 Dec 2021 15:13:42.571 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 12680:X 12 Dec 2021 15:13:42.575 # Sentinel ID is ea7ccf0119a4cf2873cf3bb108da5c7af86d36bd 12680:X 12 Dec 2021 15:13:42.575 # +monitor master myredis 127.0.0.1 6379 quorum 1 12680:X 12 Dec 2021 15:13:42.575 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:13:42.580 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379

手动宕机测试

@H_95_404@# 关掉主机 127.0.0.1:6379> SHUTDOWN not connected> # 哨兵监控的一些信息 12680:X 12 Dec 2021 15:16:49.174 # +failover-state-select-slave master myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:49.241 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:49.241 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:49.324 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:50.181 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:50.181 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:50.237 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:51.182 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:51.182 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:51.234 # +failover-end master myredis 127.0.0.1 6379 12680:X 12 Dec 2021 15:16:51.234 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6381 # 哨兵显示主机自动切换到了6381 12680:X 12 Dec 2021 15:16:51.234 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381 12680:X 12 Dec 2021 15:16:51.234 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381 12680:X 12 Dec 2021 15:17:21.244 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381 # 检查6381主机的信息 127.0.0.1:6381> info replication # Replication role:master # 变成了master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=21944,lag=0 master_replid:a4719b795f6c088ed1a11408a2bc52cc48ece215 master_replid2:367d9493cb151b433bd535ad9e49603d1fa35013 master_repl_offset:21944 second_repl_offset:11812 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:21944

如果master宕机以后,这个时候就会从从机中随机选择一个服务器(有一个自己的投票算法)作为主机

主机恢复

@H_95_404@# 重新开启之前宕机的主机。观察哨兵的反应 12680:X 12 Dec 2021 15:23:41.461 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381 # 查看先前主机的信息 127.0.0.1:6379> info replication # Replication role:slave # 变成从机 master_host:127.0.0.1 master_port:6381 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:41645 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:a4719b795f6c088ed1a11408a2bc52cc48ece215 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:41645 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:39205 repl_backlog_histlen:2441 # 只能归并到新的主机下,当作从机,这就是哨兵模式的规则!

优缺点

  • 哨兵集群,基于主从复制模式,所有的主从配置优点它多有

  • 主从可以切换,故障可以转移,系统的可用性会更好

  • 就是主从模式的升级,手动到自动,更加健壮!

  • 不好在线扩容,集群容量一旦达到上线,在线扩容就十分麻烦!

  • 实现哨兵模式的配置其实很麻烦的,里面有很多选择!

哨兵模式的全部配置

<后续进行详细学习>

@H_95_404@# Example sentinel.conf # port <sentinel-port> # The port that this sentinel instance will run on # sentinel实例运行的端口 port 26379 # 哨兵进程运行的端口号 # sentinel announce-ip <ip> # sentinel announce-port <port> # # The above two configuration directives are useful in environments where, # because of NAT, Sentinel is reachable from outside via a non-local address. # # When announce-ip is provided, the Sentinel will claim the specified IP address # in HELLO messages used to gossip its presence, instead of auto-detecting the # local address as it usually does. # # Similarly when announce-port is provided and is valid and non-zero, Sentinel # will announce the specified TCP port. # # The two options don't need to be used together, if only announce-ip is # provided, the Sentinel will announce the specified IP and the server port # as specified by the "port" option. If only announce-port is provided, the # Sentinel will announce the auto-detected local IP and the specified port. # # Example: # # sentinel announce-ip 1.2.3.4 # dir <working-directory> # Every long running process should have a well-defined working directory. # For Redis Sentinel to chdir to /tmp at startup is the simplest thing # for the process to don't interferer with administrative tasks such as # unmounting filesystems. dir /tmp # sentinel monitor <master-name> <ip> <redis-port> <quorum> # master-name : master Redis Server名称 # ip : master Redis Server的IP地址 # redis-port : master Redis Server的端口号 # quorum : 主实例判断为失效至少需要 quorum 个 Sentinel 进程的同意,只要同意 Sentinel 的数量不达标,自动failover就不会执行 # # Tells Sentinel to monitor this master, and to consider it in O_DOWN # (Objectively Down) state only if at least <quorum> sentinels agree. # # Note that whatever is the odoWN quorum, a Sentinel will require to # be elected by the majority of the kNown Sentinels in order to # start a failover, so no failover can be performed in minority. # # Slaves are auto-discovered, so you don't need to specify slaves in # any way. Sentinel itself will rewrite this configuration file adding # the slaves using additional configuration options. # Also note that the configuration file is rewritten when a # slave is promoted to master. # # Note: master name should not include special characters or spaces. # The valid charset is A-z 0-9 and the three characters ".-_". # sentinel monitor mymaster 127.0.0.1 6379 2 # sentinel auth-pass <master-name> <password> # # Set the password to use to authenticate with the master and slaves. # Useful if there is a password set in the Redis instances to monitor. # # Note that the master password is also used for slaves, so it is not # possible to set a different password in masters and slaves instances # if you want to be able to monitor these instances with Sentinel. # # However you can have Redis instances without the authentication enabled # mixed with Redis instances requiring the authentication (as long as the # password set is the same for all the instances requiring the password) as # the AUTH command will have no effect in Redis instances with authentication # switched off. # # Example: # # sentinel auth-pass mymaster MySUPER--secret-0123passw0rd # sentinel down-after-milliseconds <master-name> <milliseconds> # # Number of milliseconds the master (or any attached slave or sentinel) should # be unreachable (as in, not acceptable reply to PING, continuously, for the # specified period) in order to consider it in S_DOWN state (Subjectively # Down). # 选项指定了 Sentinel 认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING,或者直接返回错误, 那么 Sentinel 将这个实例标记为主观下线(subjectively down,简称 SDOWN ) # # Default is 30 seconds. sentinel down-after-milliseconds mymaster 30000 # sentinel parallel-syncs <master-name> <numslaves> # # How many slaves we can reconfigure to point to the new slave simultaneously # during the failover. Use a low number if you use the slaves to serve query # to avoid that all the slaves will be unreachable at about the same # time while performing the synchronization with the master. # 选项指定了在执行故障转移时, 最多可以有多少个从Redis实例在同步新的主实例, 在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长。 sentinel parallel-syncs mymaster 1 # sentinel failover-timeout <master-name> <milliseconds> # # Specifies the failover timeout in milliseconds. It is used in many ways: # # - The time needed to re-start a failover after a prevIoUs failover was # already tried against the same master by a given Sentinel, is two # times the failover timeout. # # - The time needed for a slave replicating to a wrong master according # to a Sentinel current configuration, to be forced to replicate # with the right master, is exactly the failover timeout (counting since # the moment a Sentinel detected the misconfiguration). # # - The time needed to cancel a failover that is already in progress but # did not produced any configuration change (SLAVEOF NO ONE yet not # ackNowledged by the promoted slave). # # - The maximum time a failover in progress waits for all the slaves to be # reconfigured as slaves of the new master. However even after this time # the slaves will be reconfigured by the Sentinels anyway, but not with # the exact parallel-syncs progression as specified. # 如果在该时间(ms)内未能完成failover操作,则认为该failover失败 # # Default is 3 minutes. sentinel failover-timeout mymaster 180000 # SCRIPTS EXECUTION # # sentinel notification-script and sentinel reconfig-script are used in order # to configure scripts that are called to notify the system administrator # or to reconfigure clients after a failover. The scripts are executed # with the following rules for error handling: # # If script exits with "1" the execution is retried later (up to a maximum # number of times currently set to 10). # # If script exits with "2" (or an higher value) the script execution is # not retried. # # If script terminates because it receives a signal the behavior is the same # as exit code 1. # # A script has a maximum running time of 60 seconds. After this limit is # reached the script is terminated with a SIGKILL and the execution retried. # NOTIFICATION SCRIPT # # sentinel notification-script <master-name> <script-path> # # Call the specified notification script for any sentinel event that is # generated in the WARNING level (for instance -sdown, -odown, and so forth). # This script should notify the system administrator via email, SMS, or any # other messaging system, that there is something wrong with the monitored # Redis systems. # # The script is called with just two arguments: the first is the event type # and the second the event description. # # The script must exist and be executable in order for sentinel to start if # this option is provided. # 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,但是很常用。 # # Example: # # sentinel notification-script mymaster /var/redis/notify.sh # CLIENTS RECONfigURATION SCRIPT # # sentinel client-reconfig-script <master-name> <script-path> # # When the master changed because of a failover a script can be called in # order to perform application-specific tasks to notify the clients that the # configuration has changed and the master is at a different address. # # The following arguments are passed to the script: # # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port> # # <state> is currently always "failover" # <role> is either "leader" or "observer" # # The arguments from-ip, from-port, to-ip, to-port are used to communicate # the old address of the master and the new address of the elected slave # (Now a master). # # This script should be resistant to multiple invocations. # # Example: # # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

2.2 Redis缓存穿透和雪崩

服务的高可用问题!

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时也会带来一些问题,其中,最要害的就是数据的一致性问题,从严格意义上来讲,这个问题无解,如果对数据一致性要求较高的,那么就不能使用缓存。还有一些典型的问题就是:缓存穿透、缓存雪崩、缓存击穿。目前,业界也都有比较流行的解决方案。

2.2.1 缓存穿透

查不到导致的

概念

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

在这里插入图片描述

解决方

  • 布隆过滤器

是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力

在这里插入图片描述

  • 缓存空对象

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

在这里插入图片描述

缓存空对象的2个问题:

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

2.2.2 缓存击穿

量太大,缓存过期!

概念

  • 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

  • 缓存击穿是指一个key非常热点,在不停的扛着大并发, 大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就会穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个

  • 当某个key在过期的瞬间,会大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库查询最新数据,并且回写缓存,会导致数据库瞬间压力过大

解决方

  • 设置热点数据不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题

  • 加互斥锁

分布式锁:保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的 权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因为对分布式锁的考验很大

2.2.3 缓存雪崩

概念

是指某一个时间段,缓存集中过期失效,redis宕机

在这里插入图片描述

解决方

redis集群,保证服务的高可用(异地多活)

  • 限流降级

在缓存失效后,通过加锁、队列来控制读数据库写缓存的线程数量,比如对某个key只允许一个线程查看数据和写缓存,其他线程等待

  • 数据预热

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐