Redis学习笔记

引自狂神说:https://www.bilibili.com/video/BV1S54y1R7SB

1.为什么要使用Nosql

1.单机MySQL的年代,90年代,一个基本的网站访问量不大,单个数据库完全足够。当时更多的使用静态网页,服务器根本没有太大的压力。

网站瓶颈:

  1. 数据量如果很大,一个数据库放不下;
  2. 数据的索引(B+ tree) ,一个机器内存也放不下;
  3. 访问量(读写混合),一个服务器承受不了。

APP->DAL->MySQL

2.Memcached(缓存)+MySQL+垂直拆分(读写分离),网站80%的情况都是在读,每次查询数据库的话非常麻烦,因此希望减轻数据库的压力,可以用缓存来保证效率。

发展过程:优化数据结构和索引-->文件缓存(IO)-->Memcached(当时最热门的技术)。

3.分库分表+水平拆分+MySQL集群

本质:数据库的读写

早些MySQL的引擎使用MyISAM:表锁(比如数据库中有100万条数据,查询某一条数据时,会把数据库锁起来,如果多线程查询的话非常影响效率)

后来使用InnoDB:行锁(每次查询数据只锁一行,效率较高)。

现在使用分库分表来解决写的压力

4.当今

2010-2020这十年时间,世界已经发生了翻天覆地的变化,大数据时代,数据量很多,变化很快。很多的数据类型,比如用户个人信息、社交网络、地理位置、日志等数据类型的存储不需要一个固定的格式。

MySQL等关系型数据库不够用了,出现了NoSQL,用来解决大数据时代出现的各种问题。

什么是NoSQL?

NoSQL=not only SQL(不仅仅是SQL,泛指非关系型数据库,随着web2.0互联网的诞生。)

NoSQL特点:

  • 高可扩展性;
  • 分布式计算;
  • 低成本;
  • 架构的灵活性,半结构化数据;
  • 没有复杂的关系;

NoSQL缺点:

  • 没有标准化;
  • 有限的查询功能;
  • 最终一致性是不直观的程序;

大数据时代的3V+3高

  • 3V(主要是描述问题的): 海量Velume、多样Variety、实时Velocity
  • 3高(主要是对程序的要求):高可拓(机器不够了,加机器)、高性能(保证用户体验和性能)、高并发

NoSQL分类:

NoSQL分类 代表性数据库 应用场景 数据模型 优点 确定
KV键值对 Redis、Memcached、Berkeley 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等 key-value键值对,使用Hash Table实现 查找速度快 数据无结构化,通常只能被当作字符创或二进制数据
文档型数据库 MongDB、CouchDB web应用 key-value键值对,value是结构化数据 数据结构要求不严谨,表结构可变,不用像关系型数据库那样预先定义表结构 查询性能不高,而且缺乏统一的查询语法
列存数数据库 Hbase、Cassandra、Hypertable 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
图存储 Neo4j、FlockDB 社交网络,推荐系统等,专注于构建关系图谱。 图结构 利用图结构相关算法,比如最短路径寻址、N度关系查找等。 很多时候需要对整个图做计算才能得到需要的信息,并且这种结构不太好做分布式集群方案

2.Redis简介

Remote Dictionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统,远程字典服务。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

优势:

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。

  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

  • 内存是断点即失的,所以持久化很重要。(RDB、AOF)

3.linux上安装Redis

  1. 下载redis的linux安装包,使用xftp上传redis-5.0.8.tar.gz文件到虚拟机。
  2. 解压文件,把程序放在/opt文件夹中。
tar  -zxvf  redis-5.0.8.tar.gz	#解压
  1. 进入文件夹后,可以看到redis.conf配置文件,后面需要自己修改。
  2. 安装gcc环境。
yum install gcc-c++ #安转环境
make	#从Makefile中读取指令,然后编译。
make test	#确保make是没有错误的
make install #make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置。
  1. 将redis的配置文件redis.conf复制到/usr/bin文件夹下的的yqdconfig中,如果没有可以先自己创建文件夹yqdconfig。以后就使用这个配置文件启动。
cp /opt/redis-5.0.8/redis.conf  /usr/bin/yqdconfig/	#复制配置文件
vim redis.conf #打开配置文件
修改配置文件:
daemonize yes #:redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
  1. 使用指定的配置文件,启动redis服务,关闭redis服务。
redis-server yqdconfig/redis.conf #启动服务端
redis-cli -p 6379	#启动客户端
ping	#测试连接
set name yqd	#设置一个用户名
get name	#查看用户名
keys *	#查看数据库中所有的key

redis -ef|grep "redis"	#查看redis端口是否开启。
shutdown	#关闭服务
exit	#退出

4.Redis性能测试

redis-benchmark是一个官方自带的测试工具。

redis-benchmark -h localhost -p 6379 -c 100 -n 100000	#测试:100个并发客户端,100000个请求

5.Redis数据类型

redis默认有16个数据库,编号0-15.

dbsize	#查看当前数据库的容量
select 2	#切换到2号数据库
keys *	#查看数据库中所有的key
flushdb		#清空当前数据库
flushall	#清空全部数据库

redis使用的是多线程还是单线程?

redis是基于内存操作,CPU不是redis的性能瓶颈,redis的性能瓶颈是根据机器的带宽和网络带宽,既然可以使用单线程,就使用单线程就好了。单线程可以避免了不必要的上下文切换和竞争条件。

redis使用了单线程,为什么还这么快?

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

误区2:多线程(CPU上下文会切换)一定比单线程效率高。

执行速度:CPU>内存>硬盘

核心:redis是把所有的数据全部放在内存中的,所以使用单线程去操作效率是最高的。因为多线程中CPU会上下文切换,这是很耗时的操作。对于内存系统,没有上下文切换效率就是最高的,多次读写都是在一个CPU上。

五种基本的数据类型

string(字符串),list(列表),set(集合),hash(哈希)及zset(sorted set:有序集合)。

常用指令:

set name yqd	#设置key
get name	#获取key
exists name	#判断当前的key是否存在
move name 1	#移除key到1号数据库中
expire name 10	#设置key的过期时间,单位是秒
ttl name	#查看key的剩余生存时间,time to live
type name	#查看key的类型

5.1 String(字符串)

string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB
string类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

用途:计数器、统计多单位数据、粉丝数、对象缓存存储。

set key value#设置指定 key 的值
get key#获取指定 key 的值。
getrange key start_index end_indet#返回 key 中字符串值的子字符
setrange key offset value#用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。
getset key vaue#将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
getbit key offset#对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
mset k1 v1 k2 v2 ...#设置多个key-value。 
mget key1 key2 ...#获取所有(一个或多个)给定 key 的值。 
setbit key offset value#对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
setex key second value#将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
setnx key value#只有在 key 不存在时设置 key 的值。 (分布式锁中常常使用)
strlen key#返回 key 所储存的字符串值的长度。
incr key#将 key 中储存的数字值增一。
decr key#将 key 中储存的数字值减一。
incrby key number#将 key 中储存的数字值增加number。
decrby key number#将 key 中储存的数字值减少number。
append key value#如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。 

5.2 List(列表)

Redis中的list可以作为栈、队列、阻塞队列。
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

lpush list one#把one放到list中
lrange list 0 -1#查看list中的所有元素
rpush list four#插队,four放在one前面

lpop list#移除list第一个元素
rpop list#移除list最后一个元素

lindex list 0#通过下标获取list中的元素
llen list#获取list的长度
lrem list count value#移除列表元素

ltrim list start end#对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

rpoplpush source destination#移除列表的最后一个元素,并将该元素添加到另一个列表并返回

lset key index value#通过索引设置列表元素的值

linsert list before|after value#在列表的元素前或者后插入元素

5.3 Set(集合)

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

sadd set "hello"#向集合添加一个或多个成员
smembers set#查看set所有元素
sismember set value#判断set中是否存在value
scard set#获取set中元素个数
srem set value#移除set中的value
srandmember set count#随机获取set中count个元素
spop set count#随机删除set中count个元素
smove source distination value#将value元素从 source 集合移动到 destination 集合
sfiff set1 set2#查看集合set1和set2的差集
sinter set1 set2#查看集合set1和set2的交集
sunion set1 set2#查看集合set1和set2的并集

5.4 Hash(哈希)

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Map集合,key-map。Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。

hset myhash k1 v1#将哈希表myhash中的字段k1的值设为v1
hget myhash k1#获取哈希表myhash中的字段k1的值
hmset myhash k1 v1 k2 v2 ...#将哈希表myhash中的字段:k1的值设为v1,k2的值设为v2...
hmget myhash k1 k2 ...#获取哈希表myhash中的字段k1、k2的值
hgetall myhash#获取哈希表myhash中所有的字段和对应的值
hkeys myhash#获取哈希表myhash中所有的字段
hvals myhash#获取哈希表myhash中所有的值

hdel myhash k1#删除哈希表myhash中字段k1
hlen myhash#获取哈希表myhash的字段数量
hexists myhash k1#判断哈希表myhash中是否存在字段k1
hsetnx myhash k1 v1#如果哈希表myhash中不存在k1-v1就创建,如果有了就返回0

5.5 Zset(sorted set,有序集合)

有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

zadd myset 1 one#向集合myset中添加一个值
zadd myset 1 one 2 two#向集合myset中添加多个值
zrange myset 0 -1#查询所有
zrangebyscore myset -inf +inf with scores#通过分数返回有序集合指定区间内的成员
zrem set one ...#移除有序集合中的一个或多个成员
zcard set#返回set中元素的个数
zcount set -inf +inf#返回区间中元素的个数

三种特殊的数据类型

5.6 Geospatial(地理空间)

把某个具体的位置信息(经度,纬度,名称)添加到指定的key中,数据将会用一个Zset存储,以便稍后能使用georadius和georadiusbymember命令来根据半径来查询位置信息。

  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度。

用途:附件的人(获得所有人的地址,通过半径来查找)、获得指定数量的人

#添加一个或多个地理位置元素到一个key中 
geoadd city 116 39 beijing 121 31 shanghai 113 23 guangzhou 114 22 shenzhen
#返回一个key中指定两个位置之间的距离,unit可以指定长度单位:m,km,ft等 默认为m
geodist city beijing shanghai [unit]
#返回一个或多个位置元素的 Geohash (一种经纬度散列算法)表示
geohash city beijing ...
#返回一个或多个位置的经纬度信息,由于采用了geohash算法,返回的经纬度和添加时的数据可能会有细小误差
geopos city beijing
#以给定位置为中心,半径不超过给定半径的附近所有位置
georadius city 经度 纬度 半径 单位(m/km/ft/mi)
#和GEORADIUS相似,只是中心点不是指定经纬度,而是指定已添加的某个位置作为中心
georadiusbymember city beijing 半径 单位 
zrange city 0 -1#查看所有数据
zrem city beijing#删除字段beijing 

5.7 HyperLogLog(基数统计)

什么是基数?

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。

优点:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

应用:网站UV(传统的记录方式使用set,但是如果用户很多占用的存储空间会很大)

如果允许容错,就可以使用HyperLogLog;如果不允许容错,就使用set或者自己的数据类型。

pfadd mykey1 a b c d e #创建mykey
pfcount mykey1#返回给定 HyperLogLog 的基数估算值。
pfmerge mykey mykey1 mykey2#将多个HyperLogLog合并为一个HyperLogLog 

5.8 Bitmaps(位图)

用途:统计用户打卡信息、统计用户活跃度、是否登录。两个状态的,都可以使用Bitmaps。

setbit sign 0 1#五天的打卡,0代表第一天,1代表打卡
setbit sign 1 1#五天的打卡,1代表第二天,1代表打卡
setbit sign 2 1#五天的打卡,2代表第三天,1代表打卡
setbit sign 3 0#五天的打卡,3代表第四天,0代表未打卡
setbit sign 4 0#五天的打卡,4代表第五天,0代表未打卡
getbit sign 0#查看某一天是否打卡
bitcount sign#统计打卡的天数

6.事务

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

redis事务的本质:一组命令的集合。特点:一次性、顺序性、排他性

redis事务执行的三个阶段:

  1. 开启事务(multi)
  2. 命令入队
  3. 执行事务(exec)
multi#开启事务
set k1 v1#第一条命令
set k2 v2#第二条命令
get k2#第三条命令
exec#执行事务
#一个事务执行完成后会持久化,如果想要在此使用事务,需要重新开启事务
discard#放弃事务

监控功能

类似悲观锁和乐观锁。

悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁。

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

在redis中使用watch和unwatch。在多线程中

  1. 如果发现执行事务失败,先用unwatch解锁;
  2. 使用watch加锁,获取最新的值,再次监视;
  3. 命令入队,继续执行事务。

7.Jedis

Jedis是使用Java来操作Redis的中间件,实际就是一个jar包。

使用方式:

  1. 导入Jedis依赖;
  2. 创建Jedis对象,使用对象进行各种操作。
  3. 关闭连接。

8.SpringBoot整合

Spring2.x之后,原来使用的Jedis替换成了lettuce。

Jedis:采用的直连,多个线程操作的话是不安全的,如果想要避免这种安全问题,使用Jedis Pool连接池。BIO

lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的问题。NIO

使用:

  1. 导入相关依赖;
  2. 在config下重写redisTemplate配置;
  3. 编写RedisUtils工具类去使用。

9.Redis.conf详解

####################内存单位设置####################
# 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.
####################包含####################
# include .path	olocal.conf
# include c:path	oother.conf
####################通用设置####################
port 6379#端口
#绑定ip
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1
databases 16#默认16个数据库
#日志级别
# 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

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

stop-writes-on-bgsave-error yes#持久化如果出错,是否继续工作
rdbcompression yes#是否压缩rdb文件,需要消耗一些CPU资源
rdbchecksum yes#保存rdb文件的时候,进行错误的检查校验
dbfilename dump.rdb#rdb文件保存的文件名
dir ./#rdb文件保存的目录
####################复制设置####################
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
slave-priority 100
####################安全设置####################
#设置登录密码等权限
config set requirepass ""#不设置密码
config set requirepass "123456"#设置密码为123456
####################限制####################
# maxclients 10000#能连接的最大的客户端数量
# maxmemory <bytes>#配置最大内存容量
#内存达到上限之后的处理策略
# MAXMEMORY POLICY: 
# volatile-lru ->对设置了过期时间的key进行LRU(默认)
# allkeys-lru -> 删除LRU算法的key
# volatile-random -> 随机删除即将过期的key
# allkeys-random -> 随机删除kei
# volatile-ttl -> 删除即将过期的
# noeviction -> 永不过期,返回错误
####################AOF设置####################
appendonly no#默认使用RDB方式持久化,不开启AOF。因为在大多数情况下,RDB模式已经够用了
appendfilename "appendonly.aof"#持久化文件的名字

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

10.Redis持久化

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

Redis中有两种持久化方式:RDB(Redis DataBase)、AOF(Append Only File)

10.1 RDB:

定义:把当前内存中的数据库快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。保存成dump.rdb。

触发机制:自动触发、手动触发

  1. save规则满足的情况下,会自动触发rdb规则;
  2. 执行flushall命令,会触发rdb规则;
  3. 退出redis时,会出发rdb规则。

恢复rdb文件:将dump.rdb文件放在redis的启动目录下就可以,启动的时候会自动检查。

config get dir#查看启动路径

优点:

  1. 适合大规模的数据备份和灾难恢复;
  2. 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作;
  3. 对数据的完整性要求不高;
  4. 速度比AOF快。

缺点:

  1. 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了;
  2. fork进程的时候,会占用一定的内存空间;
  3. RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)。

10.2 AOF

定义:用日志的形式来记录每个写操作,将redis执行过的每个指令记录下来(读操作不记录),只许追加文件不许修改文件。redis启动之初会读取该文件重新构建数据库,完成数据的恢复工作。如果数据量很大,效率比较低。redis默认AOF是关闭的,如果要开启,只需要在配置文件中把appendonly的属性修改为yes,重启redis即可生效。保存成“appendonly.aof ”。

redis-check-aof --fix appendonly.aof#如果文件出问题了,可以使用该指令修复

####################AOF设置####################
appendonly no#默认使用RDB方式持久化,不开启AOF。因为在大多数情况下,RDB模式已经够用了
appendfilename "appendonly.aof"#持久化文件的名字

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

重写规则:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb#如果aof文件大小超过了64MB,就重新fork一个进程重写aof文件

优点:

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

缺点:

  1. 相对于数据文件大小来说,aof远远大于rdb,修复的速度也比rdb慢;
  2. aof运行效率也比rdb慢,所以redis默认的配置就是rdb持久化。

11.Redis发布订阅

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

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

三个角色:发布者、频道、订阅者

应用:实时消息系统、实时聊天、订阅、关注系统。更复杂的场景,使用消息队列做。

subscribe channel1#订阅channel1
unsubscribe channel1#退订channel1
publish channel1 "message"#发布者在channel1发布消息

12.Redis主从复制

概念:把一台redis服务器上的数据(主节点master),复制到其他的redis服务器上(从节点slave/follower)。数据的复制是单向的,只能由主节点向从节点复制master以写为主,slave以读为主。

默认情况下,每台redis服务器都是一个主节点。

且一个主节点可以有多个或者0个从节点,但是每个从节点只能有一个主节点。

主从复制的作用:

  1. 数据冗余。实现了数据的热备份,是持久化之外的一种数据冗余方式。

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

  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载。尤其是在读多写少的场景下,通过多个节点分担读负载,可以大大提高redis服务的并发量。

  4. 高可用(集群)的基石。

环境配置:

  1. 查看服务器信息
127.0.0.1:6379> info replication #查看
# Replication
role:master #角色
connected_slaves:0#从节点个数
master_replid:697f4b9f7eca1a4380f840ea4c05bb81fb1dbb1d
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. 复制三个配置文件,然后修改对应的信息:端口号、pid名字、日志文件名字、dump.rdb名字。修改完毕之后启动三个服务。
[root@localhost yqdconfig]# ps -ef|grep redis
root      41739      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6379
root      41744      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6380
root      41749      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6381
kuangsh+  42717  40925  0 01:47 pts/1    00:00:00 redis-cli -p 6379
kuangsh+  42719  41042  0 01:47 pts/2    00:00:00 redis-cli -p 6380
kuangsh+  42729  41163  0 01:48 pts/3    00:00:00 redis-cli -p 6381
root      42731  41333  0 01:48 pts/0    00:00:00 grep --color=auto redis
  1. 配置一主二从:默认情况下,每个redis服务器节点都是主节点,我们一般情况下只用配置丛机就好了。

真实的主从配置应该是在配置文件中配置,这样是永久的。使用命令配置是临时的。

#主机默认选择6379
#从机选择6380和6381
127.0.0.1:6380> slaveof 127.0.0.7 6379 #成为6379的从机
OK
127.0.0.1:6380> info replication #查看信息
# Replication
role:slave
master_host:127.0.0.7
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:0
master_link_down_since_seconds:1588324516
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:64cb8e2f0f260e94d3eddee56d34c287b555b65b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

127.0.0.1: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:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:0
master_link_down_since_seconds:1588324215
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:8c7cef888bcca8ac29dacffed5f4bd01cfcb1d42
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. 如果是使用命令行来配置的主从,如果从机重启了,就会变为主机,主要重新设置为从机,立马就可以从主机中取值。

复制原理

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

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

增量复制:master继续把新收集到的修改命令依次传给slave,完成同步。

只要重新连接master,全量复制将自动执行,主机中的数据一定可以在slave中看到。

如果主机宕机了,如何使当前从机成为主节点?(谋朝篡位

slaveof no one#不成为slave,就自动成为了master,其他主机可以重新选择主机。即便主机重新启动了,也还是master。

13.哨兵模式(自动选举老大)

主从切换的技术含义:当master宕机后,需要手动把一个slave设置为master,这就需要人工干预,费时费力,还会造成一段时间内服务不可用,因此手动设置的模式不使用。才有了哨兵模式(Sentinel)。

哨兵模式是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立的运行。

哨兵模式原理:哨兵通过发送命令,等待服务器响应,从而监控运行的多个redis实例。

哨兵的作用:

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

然而一个哨兵也有可能出现宕机问题,因此可以使用多个哨兵进行监控,各个哨兵之间也可以互相监控,这样就形成了多哨兵模式

测试哨兵模式

1.创建自己的哨兵配置文件sentinel.conf

#sentinel monitor 名称 host port 被投票的slave,票数最多的自动成为主机 
sentinel monitor myredis 127.0.0.1 6379 1 

2.启动哨兵

[root@localhost yqdconfig]# redis-sentinel sentinel.conf  #启动哨兵
44618:X 01 May 2020 02:54:20.762 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
44618:X 01 May 2020 02:54:20.762 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=44618, just started
44618:X 01 May 2020 02:54:20.762 # Configuration loaded
44618:X 01 May 2020 02:54:20.763 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.8 (00000000/0) 64 bit
  .-`` .-```.  ```/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 44618
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

44618:X 01 May 2020 02:54:20.766 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
44618:X 01 May 2020 02:54:20.768 # Sentinel ID is a5066fd82500f2d5d52d704dd963605465aa2b5b
44618:X 01 May 2020 02:54:20.768 # +monitor master myredis 127.0.0.1 6379 quorum 1
44618:X 01 May 2020 02:54:20.770 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
44618:X 01 May 2020 02:54:20.772 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
  • 如果主机宕机了,哨兵可以自动选举一个从节点作为master,这时候如果重新启动主机,主机会自动成为新的master的slave。
  • 如果从机宕机了,哨兵会自动检测到其宕机。这个时候如果重启它,它又会自动成为slave。

哨兵模式的优缺点

优点:

  1. 哨兵集群,基于主从复制模式,所有的主从复制优点它都有;
  2. 主从可以切换,故障可以转移,系统的可用性更好;
  3. 哨兵模式就是主从模式的升级,从手动到自动,更加健壮。

缺点:

redis不好在线扩容,集群容量一旦达到上限,在线扩容十分麻烦;

实现哨兵模式实际上很麻烦,里面有很多配置选择。

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

14.Redis缓存穿透、缓存击穿和缓存雪崩

14.1缓存穿透(没查到)

概念

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

缓存穿透解决方案

  1. 布隆过滤器:一种数据结构,对所有可能查询的参数用hash形式存储,在控制层先进行校验,不符合就丢弃,从而避免对数据库的查询压力;
  2. 缓存空对象:当存储层没有命中,即使返回一个空对象,也把这个空对象缓存起来,同时设置一个过期时间。之后再访问这个数据时就可以从缓存中取,保护了后端数据库。

缓存空对象存在的两个问题:

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

14.2缓存击穿(查询量太大,缓存过期)

概念

当某个热点key在过期的瞬间,有大量的请求并发访问这个热点key。由于缓存过期,会访问数据库来查询最新数据,并且写回缓存,会导致数据库瞬间压力过大,导致服务器崩溃。

缓存击穿解决方案

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

14.3缓存雪崩(宕机或者断网)

概念

在某一个时间段,缓存集体过期失效。比如缓存服务器某个节点宕机或者断网。

缓存雪崩解决方案

  1. 搭建集群:设置多台服务器,异地多活。
  2. 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询和写缓存,其他线程等待。
  3. 数据预热:在正式部署之前,把可能的数据都预先访问一遍,这样部分可能大量被访问的key就会被写入到缓存中。即在发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀一些。
原文地址:https://www.cnblogs.com/smalldong/p/14466602.html