【建议收藏】Redis学习笔记

Redis缓存型数据库基础

概述

Redis,远程字典服务。

能干什么?

  • 数据持久化:RDB、OF
  • 效率高,高速缓存
  • 发布订阅系统
  • 地图信息分析
  • 计时器、浏览量

特性

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

学习中需要用的东西

Redis官网:http://redis.io/

Redis中文网:http://www.redis.cn/

Github

Linux

安装

Redis版本5.0.7

gcc版本4.8

redis解压目录:/redis-5.0.7

redis配置文件目录 /usr/local/bin/wzconfig/redis.conf

基本常识

redis有13个数据库

一秒可以写81000次或者读110000次

Redis口令

Redis中存储方式是键值对(key-value),所以set key 值为录入数据,get key为取得数据

连接redis操作:cd /usr/local/bin进入redis安装目录,选择redis配置文件redis-server wzconfig/redis.conf,连接 redis-cli -p 6379

select 3,切换第4个数据库

dbsize,当前数据库大小

set name wz:意思是新增一字段name,值为wz

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服务

[root@iZwz923i6d0otioytcumi9Z bin]# redis-server wzconfig/redis.conf # 选择启动redis的配置文件
22096:C 07 Dec 2020 16:33:36.518 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
22096:C 07 Dec 2020 16:33:36.518 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=22096, just started
22096:C 07 Dec 2020 16:33:36.518 # Configuration loaded
[root@iZwz923i6d0otioytcumi9Z bin]# redis-cli -p 6379 # 启动

为什么Redis是6379?

6379在是手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字。MERZ长期以来被antirez及其朋友当作愚蠢的代名词。Redis作者antirez同学在twitter上说将在下一篇博文中向大家解释为什么他选择6379作为默认端口号。而现在这篇博文出炉,在解释了Redis的LRU机制之后,向大家解释了采用6379作为默认端口的原因。

Redis读写方案

Redis是单线程的

Redis是很快的,Redis是基于内存操作的,所以Redis是根据机器的内存和网络带宽,而不是CPU。

Redis是C语言写的,为什么单线程还这么快?

1、误区1:高性能的服务器一定是多线程的。

2、误区2:多线程(和CPU调度有关,CPU上下文会切换)一定比单线程效率高。

速度比:CPU>内存>硬盘

核心:Redis是将所有的数据全部放在内存中的,所以使用单线程操作效率就是最高的,多线程产生的CPU上下文切换导致速度变慢。对于内存系统来说,没有上下文切换,效率就是最高的,多次读写都在一个CPU上那么单线程就是最佳方案。

Redis数据类型

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理(消息中间件MQ)。它支持字符串哈希表列表集合有序集合位图hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区

Redis-Key

exists name# 判断内存中键值对是否存在,存在返回1,不存在返回0

MOVE name 1# 移动键为name的键值对到1号数据库中

EXPIRE name 10# 设置键为name的键值对10秒后自动过期

ttl name# 查看键为name的键值对过期剩余时间

type name# 查看键name对应值的类型

String

90%的Java程序员使用redis只会使用一个String类型

append key1 wz # 在key1后面追加字符串wz,如果key1是不存在的,相当于set key1 wz

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

getrange key1 0 3 #取得key1值的字符串中下标为0到3的字符

getrange key1 0 -1# 取得key1值的整个字符串

SETRANGE key2 2 q# 替换key2值的下标为2位置的字符

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

mset k1 vi k2 v2 k3 v3 #同时设置多个键值对,kl对v1,k2对v2,k3对v3
mget k1 k2 k3 # 使用多个键同时取得多个值,输出结果如下
1) "vi"
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加属性的方式实现工整有序不重复的存储。可以使用key:{id}:属性1、key:{id}:属性2,key:{id}:属性3等的方式拼出一个完整对象

# 
getset db "redis"# 相当于get db得到原值并输出后,set db "redis"。如果不存在则返回null并设置新值,如果存在先返回原值再设置新的值

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

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

List

列表,redis中List可以实现:栈、队列、阻塞队列。

所有的list命令都是L开头的

LPUSH wzlist one #从左侧插入,将一个值或者多个值插入到列表的头部,写入顺是1,2,3存储顺序是3,2,1
LRANGE wzlist 0 -1#取得wzlist列表中所有内容
LRANGE wzlist 0 1#取得wzlist列表中下标为0到下标为1的内容(后进先出,最后进来的下标是0)
RPUSH wzlist right#从右侧插入,将一个值或者多个值插入到列表的尾部,写入顺是1,2,3存储顺序就是1,2,3
LPOP wzlist #移除名为wzlist列表的第一个元素
RPOP wzlist#移除名为wzlist列表的最后一个元素
LINDEX wzlist 0#取得wzlist列表中下标为0的值

LLEN wzlist #取得wzlist列表的长度

LREM wzlist 4 3# 从头部开始移除4个值为3的value

LTRIM wzlist 1 2#删除列表中除下标为1、2的其他所有元素

RPOPLPUSH mylist myotherlist #移除mylist列表的最后一个元素,将其添加到myotherlist列表中

LSET mylist 0 value0# 修改mylist中下标为0的值修改为value0,如果列表或者下标不存在,则报错。

LINSERT mylist before "1" "haha" #将haha插入到列表中1的前面
LINSERT mylist after "2" "haha"#将haha插入到列表中2的后面

小结:实际上是一个链表,before/Node/after/left/right都可以插入值

如果key不存在,创建新的链表

如果key存在,新增内容

如果移除了所以值,只剩key,此链表为空链表,也不存在

在两边插入或者改动值,效率最高,中间元素相对来说效率低。

Set:无序不重复集合

Set中的值是不能重复的,Set指令开头都是S

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"

实际应用:

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

六度分割理论。

Hash哈希表

Map集合,key-value集合

Hash命令以H开头

127.0.0.1:6379> hset myhash field1 wangzheng# 为myhash新增一个键值对,key:field1,value:wangzheng
(integer) 1
127.0.0.1:6379> HGET myhash field1# 查看myhash中键值对中key为field1的value值
"wangzheng"
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"

27.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

实际应用:

和String类似,hash更适合存储对象。

用于用户信息、经常变动的信息的存储。

Zset有序集合

在set的基础上,增加了一个值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

实际应用:

实现优先级。

排行榜

三种特殊数据类型

geospatial

地理位置

规则:两极地区无法直接添加,我实际开发中下载城市位置,使用Java一次性导入。

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"

GEO中命名的实现原理是Zset,所以我们可以使用Zset操作GEO,如下

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数据结构

用于基数统计的算法

基数,多个数据集中不重复的元素,可以接受误差。

一个人访问同一网站多次还是算作一个人。

**传统方式:**使用set保存用户id,set不允许重复,用set的元素数量来作为判断标准。

**题:**如果保存大量用户id,导致内存占用严重。

Hyperloglog的优点

占用内存是固定的,即使存储2^64个不同元素的基数,只需要12KB内存,如果要从内存角度来比较的话,Hyperloglog是首选。

但是有0.81%的错误率。统计用户访问量(UV)数据可以忽略不计。

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两个状态。

如打卡每天打一次卡,365天就是365bit,1字节等于8bit,存储365天约46字节即可。

# 情景再现:员工打卡系统,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

事务回顾

事务的四个基本特征:
Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成 功,要么全部失败。

Consistency(一致性):事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏,事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没 有执行过一样。

Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性 和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。

Durability(持久性):事务结束后,事务处理的结果必须能够得到固化。


Redis事务

Redis事务和普通事务的区别:

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

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

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

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"

面试常问:Redis监控(Watch)和实现乐观锁

悲观锁:

  • 认为所有时候都会出问题,无论做什么都加锁。

乐观锁:

  • 认为所有时候都不会出现问题,所以不会上锁。
  • 在更新数据的时候去判断一下,在此期间是否有人修改这个数据,使用version来判断。
  • 更新时候比较和上次的version是否一致。

Redis监视测试

# 情景再现:持有100元money,使用20元out
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money# 监视money对象,一旦事务执行成功,监控就会自己取消掉,如果事务没有执行成功,则需要使用unwatch语句手动解锁。
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) 20

如果出现第一个线程还未进行修改money时,第二个线程抢先一步修改了money.

使用watch可以当做redis的乐观锁操作。

测试:

# 线程1打开监视,并启动事务,命令入队。
127.0.0.1:6379> watch money # 监视当前money的值
OK
127.0.0.1:6379> multi# 启动事务
OK
127.0.0.1:6379> DECRBY money 10# 命令入队
QUEUED
127.0.0.1:6379> INCRBY out 10# 命令入队
QUEUED

# 线程2突然出现,修改了money的值,此时线程1的事务还没有执行,但是指令已经入队
127.0.0.1:6379> set money 1000# 修改money值
OK

 # 线程1,执行事务失败,返回空
127.0.0.1:6379> exec# 执行事务
(nil)

# 此时想要继续完成操作,需要解开监视,加监视,启动事务,命令入队,完成执行。
127.0.0.1:6379> unwatch# 解开监视
OK
127.0.0.1:6379> watch money# 监视最新money的值
OK
127.0.0.1:6379> multi# 启动事务
OK
127.0.0.1:6379> DECRBY money 10# 命令入队
QUEUED
127.0.0.1:6379> INCRBY out 10# 命令入队
QUEUED
127.0.0.1:6379> EXEC# 比对监视的值是否发生变化,如果没有变化,执行成功。否则执行失败
1) (integer) 990
2) (integer) 30

Jedis

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

导入Jedis包:

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>

编码测试:

  • 连接数据库

  • public class TestPing {
        public static void main(String[] args) {
            // 1、 new一个Jedis对象
            Jedis jedis = new Jedis("127.0.0.1",6379);
            // Jedis所有的命令就是Redis命令
            System.out.println(jedis.ping());
            //连接成功输出PONG
        }
    }
    
  • 操作命令

  • 结束测试

常用API

在这里插入图片描述

在这里插入图片描述

String

在这里插入图片描述

在这里插入图片描述

List

在这里插入图片描述

在这里插入图片描述

Set

在这里插入图片描述

在这里插入图片描述

Hash

在这里插入图片描述

Zset

三种特殊类型

Jedis事务

public class TestTX {
    public static void main(String[] args) {
        //连接
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","wz");

        //开启事务
        Transaction multi = jedis.multi();
        String res = jsonObject.toJSONString();
//        jedis.watch(res);
        try {
            multi.set("user1",res);
            multi.set("user2",res);

            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.flushDB();
            jedis.close(); //关闭连接
        }
    }
}

Spring集成Redis

SpringBoot相关的数据操作都被封装到SpringData中,如JDBC、JPA、MongoDB、Redis等等。

MyBatis是主动迎合的Spring,发布了MyBatis-Spring,所以MyBatis不在SpringData中。

SpringBoot2.x之后底层由Jedis替换为了lettuce

Jedis:采用的直连,多个线程操作是不安全的,如果想要避免不安全,就需要使用Jedis pool连接池,导致性能较低。BIO模式

lettuce:底层采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。可以减少线程数据。NIO模式。

SpringRedis源码分析

public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"}) // 如果不存在此方法,就实现此方法,意思就是可以自己手动实现来替代此方法。
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        //默认的RedisTemplate没有过多的设置,Redis对象都是需要序列化(即按照顺序存储)
        //两个泛型是<Object, Object>类型,我们后使用需要强制类型转换<String, Object>
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean // 由于String是redis中最常使用的类型,所以说单独提出来了一个bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

整合测试

导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置连接

spring.redis.host=127.0.0.1
spring.redis.port=6379

测试

值的序列化配置,默认的序列化方式是jdk序列化

private boolean initialized = false;
private boolean enableDefaultSerializer = true;
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
@Nullable
private ScriptExecutor<K> scriptExecutor;

编写自己的RedisTemplate配置,固定模板,企业中可以直接使用。

作用是替代JDK序列化,解决控制台中输出英文或者中文是乱码的问题。

在这里插入图片描述

// 在企业开发中,我们80%不会使用原生的序列化操作,即原生的redis。
// 在真实的开发中,都会看到一个公司自己封装的RedisUtils或者RedisTools,自己实现的Redis操作。企业级开发大部分不使用原生的。

基础总结

在实际开发中:

读请求:

不要求强一致性的读请求,走Redis,要求强一致性的直接从MySQL读取。

写请求:

数据首先都写到MySQL数据库,之后更新Redis(先写redis再写MySQL的话,如果写入失败MySQL事务回滚,又因为Redis事务的性质特殊,会造成Redis中存在上一次操作的脏数据)。

MySQL和Redis处理不同的数据类型

  • MySQL处理实时性数据,例如金融数据、交易数据。
  • Redis处理实时性要求不高的数据,例如网站最热贴排行榜,好友列表等。

在并发不高的情况下,读操作优先读取redis,不存在的话就去访问MySQL,并把读到的数据写回Redis中;写操作的话,直接写MySQL,成功后再写入Redis(可以在MySQL端定义CRUD触发器,在触发CRUD操作后写数据到Redis,也可以在Redis端解析binlog,再做相应的操作)

在并发高的情况下,读操作和上面一样,写操作是异步写,写入Redis后直接返回,然后定期写入MySQL

需要频繁调用且不经常变动的数据使用redis操作get到转换为JSON返回给前端,就不再回到底层查询MySQL数据库,效率提升。






Redis进阶导论

Redis.conf详解

启动时通过配置文件启动的。

单位

# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
大小写不敏感

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

包含

# Include one or more other config files here.  This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings.  Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf

好比学习Spring中可以把多个配置文件配置过来。

网络

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

通用

daemonize yes #以守护进程的方式运行,默认是no需要手动开启为yes

pidfile /var/run/redis_6379.pid # 如果以守护进程方式运行,就需要指定一个pid进程文件

# 日志四个级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)生产环境适用
# warning (only very important / critical messages are logged)
loglevel notice

logfile "" # 日志的文件名

databases 16# 默认数据库的数量是16个

always-show-logo yes # 是否总是显示logo

快照

持久化,在规定时间内执行了多少次操作则会持久化到文件.rdb .aof

redis是内存数据库,没有持久化数据断电即失去。

save 900 1 # 如果900秒(15分钟)内至少有1个key进行了修改,则进行持久化操作
save 300 10# 如果300秒(5分钟)内至少有10个key进行了修改,则进行持久化操作
save 60 10000# 如果60秒(1分钟)内至少有10000个key进行了修改,则进行持久化操作
# 我们之后会自己定义

stop-writes-on-bgsave-error yes# 持久化如果出错是否还需要继续工作,默认开启,一般情况下开启
rdbcompression yes #是否压缩rdb文件,需要消耗一些CPU资源
rdbchecksum yes# 保存rdb文件时是否去错误校验rdb文件
dir ./ # rdb文件的保存目录

安全

设置redis密码,默认是没有密码的

127.0.0.1:6379> config get requirepass #查看密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass root123# 设置密码
OK
127.0.0.1:6379> config get requirepass# 没有登录无法使用功能
(error) NOAUTH Authentication required.
127.0.0.1:6379> pings# 没有登录无法使用功能
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth root123 # 登录
OK
127.0.0.1:6379> config get requirepass# 登录后可以使用
1) "requirepass"
2) "root123"

限制

maxclients 10000 #设置能连接redis能连接客户端的最大数量
maxmemory <bytes># redis配置最大的内存容量 
maxmemory-policy noeviction# 内存到达上限的处理策略
maxmemory-policy 六种策略

**1、volatile-lru:**只对设置了过期时间的key进行LRU(默认值)

2、allkeys-lru : 删除lru算法的key

**3、volatile-random:**随机删除即将过期key

**4、allkeys-random:**随机删除

5、volatile-ttl : 删除即将过期的

6、noeviction : 永不过期,返回错误

APPEND ONLY 模式 aof配置

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

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

具体配置在Redis持久化中进行学习。

Redis持久化

面试和工作持久化都是重点

RDB

数据存入,再次启动时读出。

什么时候使用?在主从复制中,rdb就是备用的,从机上面使用。

什么是RDB

在这里插入图片描述

在这里插入图片描述

rdb保存的文件是dump.rdb,关于如何触发创建dump.rdb都是在redis.conf配置的快照中进行配置的。

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

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中的内容到内存中。

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

优点:

1、适合打大规模的数据恢复。应用情景:服务器宕机后,redis启动目录中的dump.rdb文件将数据恢复到宕机前的模样。

2、对数据完整性要求不高。如果设置60秒内10000次访问才触发生成dump.rdb,那么在

AOF

几乎不使用

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

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

no改为yes

重启redis就可以生效了

在这里插入图片描述

appendonly.aof创建成功

在这里插入图片描述

如果aof文件有错误,此时redis是启动不起来的,我们需要修复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的扩展

在这里插入图片描述

在这里插入图片描述

Redis发布订阅

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

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

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

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

在这里插入图片描述

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

img

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

img

Redis 发布订阅命令

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

channel频道,message消息
在这里插入图片描述

测试

订阅端:

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

发布端:

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

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

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

Redis订阅发布原理

在这里插入图片描述

实际应用:

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

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

订阅、关注系统。

稍微复杂的场景我们就会使用专门的消息中间件来做,例如MQ、Kafka

Redis主从复制

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

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

主从复制的作用

在这里插入图片描述

ps:高可用一般指集群。

在这里插入图片描述

只要在公司中,主从复制就是必须使用的,因为在真实开发中不可能单机使用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服务

在这里插入图片描述

启动成功验证:

在这里插入图片描述

配置从机:

从机1

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
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: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

真实的主从配置应该是在配置文件中配置,我们这里使用的是命令,是暂时的。

细节

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试:

主机断开连接:

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

从机断开连接:

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

主从复制原理
  • 全量复制
  • 增量复制

在这里插入图片描述

崩星咆哮炮主从复制模型搭建(”我是火车王“模型)

主机A下挂载一个从机B,从机B下挂载从机C。

此时从机B的主机是A,从机C的主机是B,A中写入数据,B读取A成功后,C也能从B中读取到A的数据。

A——>B——>C
在这里插入图片描述

如果此时主机崩溃,如何才能从从机中选出一个当作新主机?

127.0.0.1:6381> SLAVEOF no one # 表明已经无主机可用,将自己作为主机。
OK
127.0.0.1:6381> INFO replication # 检查主从复制配置
# Replication
role:master # 已经变为主机
connected_slaves:0
master_replid:b5b46f59a09000b5a85bd610dc24ffa564cccc74
master_replid2:38d163d200d5c9b7c6e88773fe8564802f0e67a6
master_repl_offset:7473
second_repl_offset:7474
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43
repl_backlog_histlen:7431

如果此时主机恢复了,就只能重新配置。

Redis主从复制哨兵模式(工作中用)

自动选取主机的模式。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试

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

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

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

3、启动哨兵

[root@iZwz923i6d0otioytcumi9Z bin]# redis-sentinel wzconfig/sentinel.conf # 哨兵模式启动,配置文件是sentinel.conf
23516:X 08 Dec 2020 15:13:37.984 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
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

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

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

23531:X 08 Dec 2020 15:50:35.748 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381

优点:

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

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

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

缺点:

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

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

哨兵模式的全部配置如下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Redis缓存穿透和雪崩(面试高频、工作常用)

服务(集群)的高可用问题。

缓存穿透

在这里插入图片描述

数据不存在,数据库中查不到导致的缓存穿透。

解决方案:

方法1、

在这里插入图片描述

方法2、
在这里插入图片描述
)]

方法2的问题:

在这里插入图片描述

缓存击穿(注意不是缓存穿透)

在这里插入图片描述

缓存过瞬间隙查询量太大导致。

解决方案:

在这里插入图片描述

什么是分布式锁?

  • 当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。

  • 与单机模式下的锁不仅需要保证进程可见,还需要考虑进程与锁之间的网络问题。(我觉得分布式情况下之所以问题变得复杂,主要就是需要考虑到网络的延时和不可靠。。。一个大坑)

  • 分布式锁还是可以将标记存在内存,只是该内存不是某个进程分配的内存而是公共内存如 Redis、Memcache。至于利用数据库、文件等做锁与单机的实现是一样的,只要保证标记能互斥就行。

    在这里插入图片描述

缓存雪崩

概念:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

解决方案:
在这里插入图片描述

小结:

Redis即缓存型数据库,读取速度快,把经常访问又不经常变化的数据存在其中,在调用读取接口时,首先去Redis中查询,如果返回值为空,即Redis中不存在此数据,再去MySQL中查询。

在写入数据时候,先对MySQL进行操作,等MySQL成功操作完毕后再写入Redis,如果先写Redis,再写MySQL的话,一旦MySQL中操作失败,Redis中已经存在了数据,MySQL进行事务回滚后,再执行一次,再次向Redis中存储数据,由于Redis事务的性质,造成了Redis中出现脏数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君去何方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值