狂神说Redis学习笔记

1、Nosql

1.1、为什么要用Nosql

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

1.2、什么是NoSql

Not Only Sql 不仅仅是sql

NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。NoSQL是一项全新的数据库革命性运动,其拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入
解耦

  1. 方便扩展(数据之间没有关系,很好扩展)
  2. 大数据量高性能(Redis一秒写8万次,读11万,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高)
  3. 数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人就无法设计了)
  4. 传统的RDBMS和NoSQL

传统的RDBMS

  • 机构化组织
  • SQL
  • 数据和关系都存在单独的表中 row col
  • 数据定义语言
  • 严格的一致性
  • 基础的事务

NoSQL

  • 不仅仅是数据
  • 没有固定的查询语言
  • 键值对存储,列存储,文档存储,图形数据库(社交关系)
  • 最终一致性
  • CAP定理和BASE(异地多活)
  • 高性能,高可用,高可拓

了解:3V+3高
大数据时代的3V:主要是描述问题的

  • 海量Volume
  • 多样Variety
  • 实时Velocity

大数据时代的3高:主要是对程序的要求

  • 高并发
  • 高可拓
  • 高性能

1.3、NoSQL的四大分类

KV键值对:

  • 新浪:Redis
  • 美团:Redis + Tair
  • 阿里、百度:Redis + memecache
    文档型数据库(bson格式和json一样):
  • MongoDB
    • MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!
    • MongoDB是一个介于关系型数据库和非关系型数据中间的产品,MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的
    • ConthDB
      列存储数据库
  • HBase
  • 分布式文件系统
    图关系数据库
  • 他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐
  • Neo4j,InfoGrid
    在这里插入图片描述
    图片来自于网络

2、Redis入门

2.1、概述

Redis是什么?

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 能干嘛?

1、内存存储、持久化,内存断电即失,持久化很重要(rdb,aof)
2、效率高,可以用于告诉缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量)
6…

特性

1、多样的数据类型
2、持久化
3、集群
4、事务

官网:https://redis.io/
中文网:http://www.redis.cn/

2.2、基本环境安装

  1. 下载linux安装包,本文下载的为5.0.12(6.0以上的需要升级gcc,太费劲)
  2. 将redis-5.0.12.tar.gz,传输到opt目录(当然了,不喜欢opt目录其它目录也可以)
  3. 解压 tar -zxvf redis-5.0.12.tar.gz
tar -zxvf redis-5.0.12.tar.gz 
  1. 安装gcc
yum -y install gcc gcc-c++
yum install gcc-c++ tcl
  1. 进入到redis解压后的目录,执行make
cd /opt/redis-5.0.12
make
  1. 进入到redis默认安装路径:/usr/local/bin下,查看是否安装完成
    在这里插入图片描述
  2. 创建文件夹,用来保存redis的配置文件,并将解压包内的redis.conf,拷贝一份,后续按照拷贝后的文件进行启动
mkdir /usr/local/bin/redisconfig
cp cp /opt/redis-5.0.12/redis.conf /usr/local/bin/redisconfig/
  1. 更改配置文件,配置后台启动
    在这里插入图片描述
  2. 用拷贝过来的配置文件启动redis
redis-server redisconfig/redis.conf

在这里插入图片描述

  1. 客户端连接
redis-cli -p 6379

在这里插入图片描述

  1. 查看redis进程
ps -ef|grep redis

在这里插入图片描述

  1. 关闭redis
shutdowm
exit

在这里插入图片描述

2.3、测试性能

Redis性能压测工具 redis-benchmark
在这里插入图片描述
在这里插入图片描述

2.4、redis基础知识

redis默认有16个数据库,默认使用第0个
在这里插入图片描述
redis端口号为什么是6379
6379在是手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字

Redis是单线程的!
Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以用单线程来实现,就使用单线程了
Redis是C语言写的,官方提供的数据为100000+的QPS,完全不必同样是使用key-value的Memecache差!

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

  1. 误区1:高性能的服务器一定是多线程的?
  2. 误区2:多线程(CPU上下文会切换!)一定比单线程效率高??

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

2.4.1、redis常用命令

#1.切换数据库  select index
select 3
#2.数据库大小
dbsize
#3. 查看所有的key
keys *
#4. 清空全部
flushall
#5. 清空当前库
flushdb
#6.判断某个key是否存在  
EXISTS KEY
#7.移某个key
move keyName 数据库index  (move name 1)
#8.设置key过期时间
EXPIRE keyName seconds
#9.查询有效期还剩多久
ttl keyName
#10.查看key类型
type keyName

3、五大数据类型

在这里插入图片描述
全段翻译:
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)。

3.1、String

####################################################################
127.0.0.1:6379> APPEND name aa #追加,如果追加的key不存在,则新建
(integer) 6
127.0.0.1:6379> GET name
"yangaa"
127.0.0.1:6379> STRLEN name #获取key对应值的长度
(integer) 6
####################################################################
127.0.0.1:6379> set views 0 #初始浏览量为0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCR views #自+1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> DECR views
(integer) 0
127.0.0.1:6379> get views #自-1
"0"
127.0.0.1:6379> INCRBY views 10 #自+10
(integer) 10
127.0.0.1:6379> get views
"10"
127.0.0.1:6379> DECRBY views 5 #自-5
(integer) 5
127.0.0.1:6379> get views
"5"
####################################################################
127.0.0.1:6379> set key1 hello,yang
OK
127.0.0.1:6379> get key1
"hello,yang"
127.0.0.1:6379> GETRANGE key1 0 3 #截取字符串
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1 #截取全部
"hello,yang"
127.0.0.1:6379> set key2 abcd
OK
127.0.0.1:6379> SETRANGE key2 1 xx  #替换   SETRANGE key offset value
(integer) 4
127.0.0.1:6379> get key2
"axxd"

####################################################################
# setex (set with expire) 设置过期时间
# setnx (set if not exist) 不存在才设置(在分布式锁中会常常使用)
27.0.0.1:6379> setex key3 30 "hello"  #设置过期时间
OK
127.0.0.1:6379> ttl key3
(integer) 24
127.0.0.1:6379> setnx mykey "redis" #此时mykey不存在才能设置成功
(integer) 1
127.0.0.1:6379> keys *
1) "key1"
2) "mykey"
3) "key2"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey "MongoDB" #此时mykey已存在设置不成功
(integer) 0
127.0.0.1:6379> get mykey
"redis"
####################################################################
#批量set,批量获取,批量不存在set
127.0.0.1:6379> mset k1 v1 k2 v2
OK
127.0.0.1:6379> mget k1 k2
1) "v1"
2) "v2"
127.0.0.1:6379> MSETNX k1 v2 k3 v3
(integer) 0
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
####################################################################
set user:1 {name:zhangsan,age:20} #设置一个user:1 对象
mset user:1:name zhangsan user:1:age 20
mget user:1:name user:1:age
####################################################################
getset #先get然后再set
127.0.0.1:6379> getset key1 redis #如果不存在则返回nil
(nil)
127.0.0.1:6379> get key1
"redis"
127.0.0.1:6379> getset key1 mongodb
"redis"
127.0.0.1:6379> get key1
"mongodb"
####################################################################

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

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

3.2、List

127.0.0.1:6379> LPUSH list one #将一个值或多个值,插入到列表头部(左)
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list four #将一个值或多个值,插入到列表尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"

####################################################
LPOP key 从左侧移除一个值
RPOP key 从右侧移除一个值
LREM key count value 从key中移除count个value
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> LPOP list
"three"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
3) "four"
127.0.0.1:6379> RPOP list
"four"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
####################################################
LINDEX 通过下角标求值
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> LINDEX list 0
"two"

127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "one"
3) "two"
4) "one"
127.0.0.1:6379> LREM list 1 one #从list中移除1个one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "two"
3) "one"

####################################################
LLEN 求list长度
LTRIM key start end  #截取

####################################################
RPOPLPUSH source destaintion #移除列表最后一个元素,将它移至目标key中
127.0.0.1:6379> RPUSH list "hello1"
(integer) 1
127.0.0.1:6379> RPUSH list "hello2"
(integer) 2
127.0.0.1:6379> RPUSH list "hello3"
(integer) 3
127.0.0.1:6379> RPOPLPUSH list otherlist
"hello3"
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> LRANGE otherlist 0 -1
1) "hello3"
####################################################
LSET key index value 将列表中指定下标的值替换
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> LSET list 0 "aa"
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "aa"
2) "hello2"
####################################################
LINSERT key before|after pivot value  #指定字符前后插入值
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "word"
127.0.0.1:6379> LINSERT list before word find
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "find"
3) "word"
127.0.0.1:6379> LINSERT list after word yeah
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "find"
3) "word"
4) "yeah"

小结:list为双向链表,在两边插入或改动值,效率最高!中间元素,相对来说效率会低一点

3.3、set

SADD key [member....]  像set中插入值
SMEMBERS key 查看key中有哪些值
SISMENBER key value  查看key中是否存在这个值

127.0.0.1:6379> SADD myset hello fine word
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "fine"
2) "word"
3) "hello"
127.0.0.1:6379> SISMEMBER myset hello
(integer) 1
127.0.0.1:6379> SISMEMBER myset yang
(integer) 0
#################################################
SCARD key 查看set中值的数量
SREM key value 移除key中某一个值

127.0.0.1:6379> SCARD myset
(integer) 3
127.0.0.1:6379> SREM myset word
(integer) 1
#################################################
SMOVE key otherkey member  移除指定元素至另一个key中
127.0.0.1:6379> SMOVE myset myotnerset hello
(integer) 1
#################################################
SDIFF key1 key2 差集
SINTER key1 key2 交集
SUNION key1 key2 并集

127.0.0.1:6379> SADD key1 a
(integer) 1
127.0.0.1:6379> SADD key1 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 key2 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2
1) "a"
2) "b"
127.0.0.1:6379> SINTER key1 key2
1) "c"
127.0.0.1:6379> SUNION key1 key2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"

3.4、Hash

HSET key filed value #hash存值
HGET key filed #hash取值
HMSET key [filed value ...] #批量存
HGET key [filed...] #批量取
HGETALL key #取全部
HDEL key [filed...] #删除某一个或某几个
HLEN key #获取长度
HEXISTS key value #获取某个值是否存在
HKEYS key #获取所有的filed
HVALS key #获取所有的value


127.0.0.1:6379> HSET myhash name yang 
(integer) 1
127.0.0.1:6379> HGET myhash name
"yang"
127.0.0.1:6379> HMSET myhash name yang1 age 18
OK
127.0.0.1:6379> HMGET myhash name age
1) "yang1"
2) "18"
127.0.0.1:6379> HGETALL myhash
1) "name"
2) "yang1"
3) "age"
4) "18"
127.0.0.1:6379> HDEL myhash name
(integer) 1
127.0.0.1:6379> HLEN myhash
(integer) 1
127.0.0.1:6379> HEXISTS myhash age
(integer) 1


3.5、zset(有序集合)

127.0.0.1:6379> ZADD myset 100 xiaoming
(integer) 1
127.0.0.1:6379> ZADD myset 200 xiaohong
(integer) 1
127.0.0.1:6379> ZADD myset 300 xiaowang
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE myset -inf +inf  #显示全部的用户,从小到大
1) "xiaoming"
2) "xiaohong"
3) "xiaowang"
127.0.0.1:6379> ZRANGEBYSCORE myset -inf +inf withscores #显示全部的用户,从小到大并附带值
1) "xiaoming"
2) "100"
3) "xiaohong"
4) "200"
5) "xiaowang"
6) "300"
127.0.0.1:6379> ZREVRANGE myset 0 -1  #显示全部的用户,从大到小
1) "xiaowang"
2) "xiaohong"
3) "xiaoming"
127.0.0.1:6379> ZREVRANGE myset 0 -1 withscores
1) "xiaowang"
2) "300"
3) "xiaohong"
4) "200"
5) "xiaoming"
6) "100"

127.0.0.1:6379> ZRANGEBYSCORE myset -inf 200 withscores #显示小于等于200全部的用户,从小到大并附带值
1) "xiaoming"
2) "100"
3) "xiaohong"
4) "200"

4、三种特殊数据类型

4.1、geospatial 地理位置

GEOADD

#getadd 添加地理位置
#规则:两级无法直接添加
#有效的经度从-180度到180度
#有效的纬度从-85.05112878度到85.05112878

GEOADD china:city 116.40 39.90 beijing 121.47 31.23 shanghai

GEOPOS

127.0.0.1:6379> GEOPOS china:city beijing #获取城市的经度和纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"

GEODIST key member1 member2 [unit]

返回两个给定位置之间的距离。
如果两个位置之间的其中一个不存在, 那么命令返回空值。
指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。

127.0.0.1:6379> GEODIST china:city beijing shanghai km
"1067.3788"

GEORADIUS

key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在给定以下可选项时, 命令会返回额外的信息:
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD: 将位置元素的经度和维度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。
DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。
在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT 选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素, 命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的。

127.0.0.1:6379> GEORADIUS china:city 110 30 5000 km  #以经纬度为110 30为中心查询5000km的城市
1) "shanghai"
2) "beijing"

127.0.0.1:6379> GEORADIUS china:city 110 30 5000 km withdist #显示到中间距离的位置
1) 1) "shanghai"
   2) "1105.9098"
2) 1) "beijing"
   2) "1245.2858"

GEORADIUSBYMEMBER

key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
指定成员的位置被用作查询的中心。

GEOHASH key member [member …]

该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示。返回的geohashes具有以下特性
他们可以缩短从右边的字符。它将失去精度,但仍将指向同一地区。
它可以在 geohash.org 网站使用,网址 http://geohash.org/。查询例子:http://geohash.org/sqdtr74hyu0.
与类似的前缀字符串是附近,但相反的是不正确的,这是可能的,用不同的前缀字符串附近。

GEO底层的实现原理其实就是Zset!我们可以使用Zset命令操作geo

127.0.0.1:6379> ZRANGE china:city 0 -1
1) "shanghai"
2) "beijing"

4.2、Hyperloglog

简介

Redis2.8.9版本更新了Hyperloglog数据结构
Redis Hyperloglog基数统计的算法
优点:占用的内存是固定,2^6不同的元素的计数,只需要废12kb内存,如果从内存角度来比较的话Hyperloglog首选
网页的UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断
这个方式如果保存大量的用户id,就会比较麻烦!我们目的是为了计数,而不是保存用户id
0.81%错误率!统计UV任务,可以忽略不计。

127.0.0.1:6379> PFADD mykey a b c d e f  #创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey #统计mykey中元素的数量
(integer) 6
127.0.0.1:6379> PFADD mykey1 b c d r j d #创建第二组元素 mykey1
(integer) 1
127.0.0.1:6379> PFCOUNT mykey1
(integer) 5
127.0.0.1:6379> PFMERGE mykey2 mykey mykey1 #合并两组mykey mykey1 => mykey2 并集
OK
127.0.0.1:6379> PFCOUNT mykey2 #查看并集数量
(integer) 8

4.3、Bitmap

位存储
统计用户信息,活跃,不活跃,打卡,未打卡 两个状态的数据,都可以使用Bitmaps
Bitmap位图,数据结构!都是二进制位来进行记录,就只有0和1两个状态

127.0.0.1:6379> setbit sign 0 1  #第1天 打卡了
(integer) 0
127.0.0.1:6379> setbit sign 1 1 #第2天 打卡了
(integer) 0 
127.0.0.1:6379> setbit sign 2 0 #第3天 没打卡
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> getbit sign 0  #获取某一天是否打卡
(integer) 1
127.0.0.1:6379> BITCOUNT sign 0 4  #第一天到第五天共计打卡几次
(integer) 3

5、事务

Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性、顺序性、排他性!执行一系列的命令

-----------------队列 set set set 执行

Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有被直接执行!只有发起执行命令的时候才会执行!Exec
Redis单条命令是保持原子性的,但是事务不保证原子性!

Redis的事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务()

正常执行事务

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 #执行事务
1) OK
2) OK
3) OK
127.0.0.1:6379>

放弃事务

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD  #放弃事务
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> getset k3  #错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1  #所有的命令都不会被执行
(nil)

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

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR k1 #会执行失败
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range  #后续的命令可以正常执行
2) OK

监控 Watch

悲观锁

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

乐观锁

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

Redis监视测试

正常执行成功

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> exec
1) (error) ERR value is not an integer or out of range
2) OK

测试多线程修改值,使用watch可以当作redis的乐观锁操作
在这里插入图片描述

在这里插入图片描述
解除监控,unwatch

6、jedis

什么是jedis

jedis是Redis官方推荐的java连接开发工具!使用java操作redis中间件!

测试

1.导入对应的依赖

<!--导入jedis包-->
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

2.编码测试

如果需要连接远程redis服务,不连接本地redis服务
一:修改配置文件 redis.conf
1.注掉bing 127.0.0.1
2.设置访问redis的密码 requirepass 密码
3.重启redis服务
4.使用密码登录 redis-cli redis配置文件 -h 127.0.0.1 -p 6379 -a 密码
二:关掉防火墙:systemctl stop firewalld
#注意防火墙每次重启服务器都会自动开放,为了安全不建议移除自启动,移除防火墙自启动systemctl disable firewalld
或者可以开放防火墙端口:
firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --reload
三:idea连接时增加
jedis.auth(“123”);//123为配置文件中设置的密码

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.1.101", 6379);
        jedis.auth("123");
        System.out.println(jedis.ping());
    }
}

在这里插入图片描述

7、SpringBoot整合

SpringBoot操作数据:spring-data jpa jdbc mongodb redis!
SpringData 也是和SpringBoot齐名的项目
说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce

为什么将jedis替换为lettuce?
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!更像BIO模式
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式

源码分析:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")//我们可以自己定义一个
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		//默认的RedisTemplate没有过多的设置,redis对象都是需要序列化的
		//两个泛型都是Object,Object的类型,我们使用后需要强制转换<String,Object>
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

整合测试一下

1、导入依赖

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

2、配置连接

spring:
  redis:
    host: 192.168.1.101
    port: 6379
    password: 123
3、测试
```java
@SpringBootTest
class RedisSpringStudyApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        //redisTemplate  操作不同的数据类型,api和命令是一样的
        //opsForValue 操作字符串 类似String
        //opsForList()
        //opsForSet()
        //opsForZSet()
        //opsForHash()
        //opsForGeo()
        //opsForHyperLogLog()

        //除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD

        //获取连接对象
        //RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        //connection.flushAll();
        //connection.flushDb();

        redisTemplate.opsForValue().set("myKey", "hello redis");
        System.out.println(redisTemplate.opsForValue().get("myKey"));
    }
}

在这里插入图片描述
在这里插入图片描述

不序列化会报异常

7.1、自定义RedisTemplate

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);

        //Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }

}

这样只要在实体中实现序列化,就不会有乱码问题了

import java.io.Serializable;
@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
}

7.1、自定义Redis工具类

网上好多:
https://blog.csdn.net/ye17186/article/details/87934399
https://blog.csdn.net/qq_40925189/article/details/109585667

8、Redis.conf详解

单位

在这里插入图片描述

包含

在这里插入图片描述

网络

bind 127.0.0.1 #绑定ip  *代表通配
protected-mode yes #保护模式
port 6379 #端口

通用 GENERAL

daemonize yes #以守护进程的方式运行,默认是no,我们需要自己开启为yes
pidfile /var/run/redis_6379.pid #如果以后台的方式运行,我们就需要指定一个pid文件

#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) 生产环境
# warning (only very important / critical messages are logged)
loglevel notice #日志级别
logfile "" #日志的文件位置
databases 16 #数据库的数量,默认是16个数据库
always-show-logo yes #是否显示logo

快照

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

#如果900s内,如果至少有1个key进行了修改,我们即进行持久化操作
save 900 1
#如果300s内,如果至少10个 key进行了修改,我们即进行持久化操作
save 300 10
# 如果60s内,如果至少1000个 key进行了修改,我们即进行持久化操作
save 60 10000

stop-writes-on-bgsave-error yes #持久化如果出错,是否还需要继续工作
rdbcompression yes #是否压缩rdb文件,需要消耗一些cpu资源
rdbchecksum yes #保存rdb文件的时候,进行错误的检查校验
dir ./ #rdb文件保存的目录

RePLICATION复制

SECURITY 安全

requirepass foobared #设置密码
#也可以用命令设置密码
config set requirepass "password"
auth "password" #使用密码登录

限制CLIENTS

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 MODE

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

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

9、Redis持久化

RDB于AOF详情请见:https://blog.csdn.net/weixin_45416687/article/details/115283376

10、Redis发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis客户端可以订阅任意数量的频道
订阅/发布消息图:
在这里插入图片描述
下图展示了频道channel1,以及订阅这个频道的三个客户端–client2、client5和client1之间的关系:
在这里插入图片描述

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

在这里插入图片描述

命令

这些命令被广泛用于构建即时通信应用,比如网络聊天室(chatroom)和实时广播、实时提醒等
在这里插入图片描述
实例
以下实例演示了发布订阅是如何工作的,需要开启两个 redis-cli 客户端。

在我们实例中我们创建了订阅频道名为 runoobChat:

第一个客户端
redis 127.0.0.1:6379> SUBSCRIBE runoobChat
Reading messages… (press Ctrl-C to quit)

  1. “subscribe”
  2. “redisChat”
  3. (integer) 1

现在,我们先重新开启个 redis 客户端,然后在同一个频道 runoobChat 发布两次消息,订阅者就能接收到消息。

第二个客户端:
redis 127.0.0.1:6379> PUBLISH runoobChat “Redis PUBLISH test”
(integer) 1
redis 127.0.0.1:6379> PUBLISH runoobChat “Learn redis by runoob.com”
(integer) 1
订阅者的客户端会显示如下消息

  1. “message”
  2. “runoobChat”
  3. “Redis PUBLISH test”
  4. “message”
  5. “runoobChat”
  6. “Learn redis by runoob.com”

原理

Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,了解发布和订阅机制的底层实现,借此加深对Redis的理解。
Redis通过PUBLISH、SUBSCRIBE等命令实现发布和订阅功能

通过SUBSCRIBE命令订阅某个频道后,redis-server里维护了一个字典,字典的键就是一个个频道!而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE命令的关键,就是将客户端添加到指定channel的订阅链表中
通过PUBLISH命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者

11、Redis主从复制

11.1、概念

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

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

主从复制的作用主要包括:

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

一般来说,要将Redis运用于工程项目中,只是用一台Redis是万万不能的,原因如下:
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2、从容量上,单个Redis服务器内存容量优先,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
在这里插入图片描述

环境配置,搭建伪集群

修改配置文件
port:进程占用的端口号
pid(port id):记录了进程的id,文件带有锁,可以防止程序的多次启动
logfile:明确日志文件位置
dbfilename:dumpxxx.file 持久化文件位置
如果设置了密码,需要在从机上添加masterauth 主机密码,否则从机连接不上主机

在这里插入图片描述

11.2、主从复制之复制原理

默认情况下,每台Redis服务器都是主节点
查看命令:INFO replication
在这里插入图片描述
一主二从
配置从机,认老大,一主(79)二从(80、81)

  1. 通过命令配置
192.168.137.140:6380> SLAVEOF 192.168.137.140 6379  #配置从机  SLAVEOF host port
OK
192.168.137.140:6380> info replication #查看
# Replication
role:slave #当前角色
master_host:192.168.137.140  #查看主机的配置
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1616933195
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:60515091a402e7416c97cf7fc9d95b212f3b38a2
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

###############################################
#配置完成后,也可以在主机上查看从机配置
192.168.137.140:6379> info replication
# Replication
role:master
connected_slaves:2 #已经连接两个从节点
slave0:ip=192.168.137.140,port=6380,state=online,offset=84,lag=0
slave1:ip=192.168.137.140,port=6381,state=online,offset=84,lag=0
master_replid:3d553be66e2a6e5c19dbeb104fb224a3e093517d
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


  1. 通过修改配置文件配置

在这里插入图片描述

细节

主机可以写,从机不能写只能读!主机中的所有信息和数据,都会在从机自动保存
主机写:
在这里插入图片描述

从机只能读取内容:
在这里插入图片描述

  • 主机断开连接,从机依旧连接主机,但是没有写的操作
  • 从机断开后,重新连接后,成为从机后,依旧会获取到断开时的主机数据

复制原理

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

层层链路

上一个M连接下一个S!
在这里插入图片描述

如果没有老大了,这个时候能不能选择一个老大出来?手动

如果主机79断开了链接,我们可以使用slaveof no one让80自己变成主节点,如果这个时候主机79恢复了,也不会改变谋朝篡位成功的80

12、哨兵模式 (自动选老大)

12.1、概述

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

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

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

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

然而一个哨兵进行对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式
在这里插入图片描述
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称为主观下线 当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover【故障转移】操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线

测试:

我们目前的状态是一主二从
1、配置哨兵配置文件 sentinel.conf

vim sentinel.conf
# sentinel monitor 被监控的主机名称 host port 1
sentinel monitor myredis 192.168.137.140 6379 1
#后面这个数字1,代表主机挂了,slave投票让谁接替成为主机,票数最多的,就会成为主机
#有密码的需要在配置文件中增加
sentinel auth-pass <master-name> <password>

在这里插入图片描述

2、启动哨兵


# redis-sentinel sentinel的配置文件
[root@localhost bin]# redis-sentinel redisconfig/sentinel.conf 
20858:X 28 Mar 2021 21:28:22.566 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
20858:X 28 Mar 2021 21:28:22.566 # Redis version=5.0.12, bits=64, commit=00000000, modified=0, pid=20858, just started
20858:X 28 Mar 2021 21:28:22.566 # Configuration loaded
20858:X 28 Mar 2021 21:28:22.567 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.12 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 20858
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

20858:X 28 Mar 2021 21:28:22.568 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
20858:X 28 Mar 2021 21:28:22.569 # Sentinel ID is dc245ae88de15c20a29fba857c5f046c27fa6f44
20858:X 28 Mar 2021 21:28:22.569 # +monitor master myredis 192.168.137.140 6379 quorum 1
20858:X 28 Mar 2021 21:28:22.574 * +slave slave 192.168.137.140:6380 192.168.137.140 6380 @ myredis 192.168.137.140 6379
20858:X 28 Mar 2021 21:28:22.575 * +slave slave 192.168.137.140:6381 192.168.137.140 6381 @ myredis 192.168.137.140 6379

如果Master节点断开了,这个时候就会从从机中选出一个服务器成为主节点
在这里插入图片描述

在这里插入图片描述

如果主机79恢复了,那也只能成为80的从机

12.2、哨兵模式的优缺点

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

哨兵模式的全部配置sentinel.conf

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# 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>

13、Redis缓存穿透和雪崩

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题就是数据一致性的问题,从严格意义上讲,这个问题无解,如果对数据的一致性要求很高,那么就不能使用缓存

13.1、Redis缓存穿透(查不到)

概念

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

解决方案

布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的压力
在这里插入图片描述
缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源
在这里插入图片描述

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

13.2、Redis缓存击穿(量太大,缓存过期)

概述

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

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

解决方案

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

13.3、Redis缓存雪崩

概念

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis宕机

产生雪崩的原因之一,比如在写文本的时候,马上就要到双十一零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时,那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造车给存储层也会挂掉的情况
在这里插入图片描述

解决方案

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

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

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

14、鸣谢

本篇文章均为观看狂神说整理,只作为学习使用

  1. 狂神说java,哔哩哔哩地址:https://space.bilibili.com/95256449?spm_id_from=333.788.b_765f7570696e666f.1
  2. 狂神的公司,学相伴也可以学习:https://www.kuangstudy.com/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值