Redis学习

Redis

Redis学习

因为没有指定配置文件

需配置

redis-server redis.windows.conf

之后自动启动

测试性能

redis-benchmark -p 6379 -c 100 -n 10000

Redis学习

Redis学习

基础概念:

Redis学习

Redis学习

清空当前数据库:flushdb

清空全部数据库:flushall

redis是单线程的

redis的瓶颈是机器的内存和网络的带宽,用单线程既然可以实现,就用单线程了

为什么单线程还这么快呢

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

底层数据结构(参考

简单动态字符串

Redis学习

一般来说,SDS 除了保存数据库中的字符串值以外,SDS 还可以作为缓冲区(buffer):包括 AOF 模块中的AOF缓冲区以及客户端状态中的输入缓冲区。

链表

Redis链表特性:

①、双端:链表具有前置节点和后置节点的引用,获取这两个节点时间复杂度都为O(1)。

②、无环:表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL,对链表的访问都是以 NULL 结束。

③、带链表长度计数器:通过 len 属性获取链表长度的时间复杂度为 O(1)。

④、多态:链表节点使用 void* 指针来保存节点值,可以保存各种不同类型的值

字典

Redis 的字典使用哈希表作为底层实现、

key 用来保存键,val 属性用来保存值,值可以是一个指针,也可以是uint64_t整数,也可以是int64_t整数。

注意这里还有一个指向下一个哈希表节点的指针,我们知道哈希表最大的问题是存在哈希冲突,如何解决哈希冲突,有开放地址法和链地址法。这里采用的便是链地址法,通过next这个指针可以将多个哈希值相同的键值对连接在一起,用来解决 哈希冲突

跳表

跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其它节点的指针,从而达到快速访问节点的目的。具有如下性质:

1、由很多层结构组成;

2、每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节点;

3、最底层的链表包含了所有的元素;

4、如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);

5、链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;

​ ①、搜索:从最高层的链表节点开始,如果比当前节点要大和比当前层的下一个节点要小,那么则往下找,也就是和当前层的下一层的节点的下一个节点进行比较,以此类推,一直找到最底层的最后一个节点,如果找到则返回,反之则返回空。

②、插入:首先确定插入的层数,有一种方法是假设抛一枚硬币,如果是正面就累加,直到遇见反面为止,最后记录正面的次数作为插入的层数。当确定插入的层数k后,则需要将新元素插入到从底层到k层。

③、删除:在各个层中找到包含指定值的节点,然后将节点从链表中删除即可,如果删除以后只剩下头尾两个节点,则删除这一层。

五大数据类型的应用场景

对于string 数据类型,因为string 类型是二进制安全的,可以用来存放图片,视频等内容,另外由于Redis的高性能读写功能,而string类型的value也可以是数字,可以用作计数器(INCR,DECR),比如分布式环境中统计系统的在线人数,秒杀等。

对于 hash 数据类型,value 存放的是键值对,比如可以做单点登录存放用户信息。

对于 list 数据类型,可以实现简单的消息队列,另外可以利用lrange命令,做基于redis的分页功能

对于 set 数据类型,由于底层是字典实现的,查找元素特别快,另外set 数据类型不允许重复,利用这两个特性我们可以进行全局去重,比如在用户注册模块,判断用户名是否注册;另外就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

对于 zset 数据类型,有序的集合,可以做范围查找,排行榜应用,取 TOP N 操作等

五大数据类型

redis-key

#移除name key
move name 1

Redis学习
#设置过期时间
expire name 10

Redis学习

查看剩余过期时间:ttl name;

#查看键的类型
type name

Redis学习

String

Redis学习

如果append后的键不存在就创建一个,相当于set

Redis学习

获取一部分值

Redis学习

获取全部的值

Redis学习

替换值

Redis学习

setex 设置过期时间
setnx 如果没有这个键就设置值成功,如果已存在这个键就设置不成功(在分布式锁中常应用 )

Redis学习

批量设置键和值,批量获取值

Redis学习

msetnx 具有原子性

设置对象,以json字符串的形式

Redis学习

getset

Redis学习

List

在redis中,list可以被我们玩成栈,队列,阻塞队列

lpush 放进列表数据
lrange 取出指定位置的数据,可以看出下标是倒着来的。

Redis学习

说明rpush把值放在了队列的最后面

Redis学习

从列表中移除值,可以分为移除左边的和右边的

Redis学习

通过下标获取值

Redis学习

获取列表长度

llen

移除指定的值,可指定数量

Redis学习

通过下标截取指定的长度

Redis学习

移除列表的最后一个元素,将他移动到新列表中

Redis学习

更新指定位置的值

Redis学习

Redis学习

set

Redis学习

Redis学习

Redis学习

Redis学习

Redis学习

hash

Map集合,key-map,那时候这个值是一个map集合,本质和String类型没有太大区别,还是一个简单的key-vlaue

Redis学习

获取hash表的内容长度

Redis学习

获取所有的field和所有的值

Redis学习

hash更适合于对象的存储

Zset(有序集合)

排列

Redis学习

显示工资小于2000的工资排列

Redis学习

移除指定元素

Redis学习

获取集合中的个数

zcard salary

获取指定区间的成员数量

Redis学习

总结:

Redis学习

三种特殊数据类型

Geospatial(地理位置)

geoadd 添加地理位置

geoadd key longitude latitude member [longitude latitude member ...]

geopos获得地理位置详细信息

geopos key member [member ...]

geodist获得两个地点之间的距离,可在后面追加获得结果的单位 km m

geodist key member1 member2 [unit]

georadius获得以某一点经纬度为圆心,一定距离为半径之内的元素

georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC

georadiusbymember获得某一成员为圆心,一定距离为半径之内的元素

geo底层的实现原理是zset,可以使用zset命令来操作geo

hyperloglog

统计基数

可以用作网站的UV(一个人访问网站多次,但是还是算作一个人)

传统的方式是用set,如果存储量太大的话就比较麻烦

测试

Redis学习

bitmaps

位存储

位图,是数据结构,都是操作二进制为来进行操作,只有0和1两个状态

统计一周的打卡情况

Redis学习

查看某一天的打卡情况

Redis学习

查看打卡了几天

127.0.0.1:6379> bitcount si
(integer) 2

事务

本质:一组命令的集合,所有命令都会被序列化,执行过程中,按照顺序执行

redis单挑命令保存原子性,但是事务不保证原子性,没有隔离级别的概念

所有命令在事务中,并不被直接执行,只有发起执行命令的时候才被执行

  1. 开启事务
  2. 命令入队
  3. 执行事务

正常执行事务

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> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec#执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>

放弃事务

discard

编译型异常(代码有问题),事务中所有命令都不会被执行

127.0.0.1:6379> get k1
(nil)
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> get k1
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.#所有事务不会被运行
127.0.0.1:6379>

运行时异常(1/0),其他命令正常执行,错误命令抛出异常

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1#字符串不会加1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range#属于运行时异常
2) "v1" #正常执行
127.0.0.1:6379>

监控

乐观锁

获取version,比较version

监视:

watch key [key …]

执行之前,另外一个线程如果修改了我们的值,事务就会执行失败,就要放弃监视,然后重新监视

放弃监视

unwatch key

Jedis

配置依赖

<dependencies>
        <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>

    </dependencies>

创建连接进行测试

Jedis jedis=new Jedis("127.0.0.1", 6379);
System.out.println(jedis.ping());

springboot整合

底层为lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况,NIO模式

jeids采用直连,多线程不安全,如想避免不安全,就使用jedis pool连接池,BIO模式

操作各种基本类型

Redis学习

获得链接,操作数据库

Redis学习

序列化

放入对象需序列化

@Configuration
public class RedisConfig {

    //&#x7F16;&#x5199;&#x81EA;&#x5DF1;&#x7684;redisTemplate
    @Bean
    @SuppressWarnings("all")

    public RedisTemplate<string, object> redisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {

        RedisTemplate<string,object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //Json&#x5E8F;&#x5217;&#x5316;&#x914D;&#x7F6E;
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper=new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        //String&#x7684;&#x5E8F;&#x5217;&#x5316;
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();

        //key&#x91C7;&#x7528;String&#x7684;&#x5E8F;&#x5217;&#x5316;&#x65B9;&#x5F0F;
        redisTemplate.setKeySerializer(stringRedisSerializer);
        //Hash&#x7684;&#x5E8F;&#x5217;&#x5316;&#x65B9;&#x5F0F;&#x4E5F;&#x91C7;&#x7528;String&#x7684;&#x65B9;&#x5F0F;
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        //value&#x5E8F;&#x5217;&#x5316;&#x65B9;&#x5F0F;&#x91C7;&#x7528;jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}</string,object></string,>

———————–固定配置模板

Redis.conf详解

Redis学习

对大小写不敏感

包含

Redis学习
可包含多个配置文件

网络

bind 127.0.0.1
protected-mode yes #&#x4FDD;&#x62A4;&#x6A21;&#x5F0F;
port 6379 #&#x9ED8;&#x8BA4;&#x7AEF;&#x53E3;

绑定的IP

通用

NOT SUPPORTED ON WINDOWS daemonize no
#window&#x7AEF;&#x4E0D;&#x652F;&#x6301;&#x6B64;&#x914D;&#x7F6E;&#xFF0C;&#x914D;&#x7F6E;&#x6B64;&#x9879;&#x987B;&#x5728;linux&#x7AEF;
daemonize yes #&#x4EE5;&#x5B88;&#x62A4;&#x8FDB;&#x7A0B;&#x7684;&#x65B9;&#x5F0F;&#x8FD0;&#x884C;&#xFF0C;&#x9ED8;&#x8BA4;&#x662F;no&#xFF0C;&#x6211;&#x4EEC;&#x9700;&#x8981;&#x81EA;&#x5DF1;&#x5F00;&#x542F;&#x4E3A;yes

NOT SUPPORTED ON WINDOWS pidfile /var/run/redis.pid
#&#x5982;&#x679C;&#x6307;&#x5B9A;&#x4E3A;&#x540E;&#x53F0;&#x65B9;&#x5F0F;&#x8FD0;&#x884C;&#xFF0C;&#x5219;&#x9700;&#x6307;&#x5B9A;pid&#x6587;&#x4EF6;
pidfile /var/run/redis.pid

#&#x65E5;&#x5FD7;&#x7EA7;&#x522B;
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 "" #&#x65E5;&#x5FD7;&#x6587;&#x4EF6;&#x4F4D;&#x7F6E;
databases 16 #&#x9ED8;&#x8BA4;&#x6570;&#x636E;&#x5E93;&#x6570;&#x91CF;

快照

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

reids是内存数据库,如果没有持久化,那么数据断电即失。

save 900 1 #&#x5982;&#x679C;900s&#x5185;&#xFF0C;&#x6709;&#x4E00;&#x4E2A;key&#x8FDB;&#x884C;&#x4E86;&#x4FEE;&#x6539;&#xFF0C;&#x6211;&#x4EEC;&#x5C06;&#x8FDB;&#x884C;&#x6301;&#x4E45;&#x5316;&#x64CD;&#x4F5C;&#x3002;&#x4EE5;&#x4E0B;&#x7686;&#x540C;
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes#&#x6301;&#x4E45;&#x5316;&#x51FA;&#x9519;&#xFF0C;redis&#x662F;&#x5426;&#x8FD8;&#x7EE7;&#x7EED;&#x5DE5;&#x4F5C;
rdbcompression yes#&#x662F;&#x5426;&#x538B;&#x7F29;rdb&#x6587;&#x4EF6;
rdbchecksum yes #&#x4FDD;&#x5B58;rdb&#x6587;&#x4EF6;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x8FDB;&#x884C;&#x9519;&#x8BEF;&#x7684;&#x68C0;&#x67E5;&#x6821;&#x9A8C;
dir ./ #rdb&#x6587;&#x4EF6;&#x7684;&#x4FDD;&#x5B58;&#x76EE;&#x5F55;

安全

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> config set requirepass 1234 #&#x8BBE;&#x7F6E;&#x5BC6;&#x7801;
OK
127.0.0.1:6379> config get requirepass
(error) NOAUTH Authentication required.

127.0.0.1:6379> auth 1234 #&#x767B;&#x5F55;
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "1234"
127.0.0.1:6379>

Redis学习

客户端连接相关

maxclients 10000a &#x6700;&#x5927;&#x5BA2;&#x6237;&#x7AEF;&#x6570;&#x91CF;
maxmemory <bytes> &#x6700;&#x5927;&#x5185;&#x5B58;&#x9650;&#x5236;
maxmemory-policy noeviction &#x5185;&#x5B58;&#x5230;&#x8FBE;&#x6781;&#x9650;&#x503C;&#x7684;&#x5904;&#x7406;&#x7B56;&#x7565;</bytes>

maxmemory-policy 六种方式

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

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

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

4、allkeys-random:随机删除

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

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

Redis持久化

RDB:Redis Databases

什么是RDB

在指定时间间隔后,将内存中的数据集快照写入数据库 (复制媒介);在恢复时候,直接读取快照文件,进行数据的恢复 ;

Redis学习

默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。

三种触发方式

Redis学习

还有一种就是自动触发

工作原理

在进行 RDB 的时候, redis 的主线程是不会做 io 操作的,主线程会 fork 一个子线程来完成该操作;

  1. Redis 调用forks。同时拥有父进程和子进程。
  2. 子进程将数据集写入到一个临时 RDB 文件中。
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)

Redis学习

触发机制

  1. save的规则满足的情况下,会自动触发rdb原则
  2. 执行flushall命令,也会触发我们的rdb原则
  3. 退出redis,也会自动产生rdb文件

优缺点

优点:

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

缺点:

耗时,耗性能

  1. 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了, 由此引入AOF
  2. fork进程的时候,会占用一定的内容空间。

AOF

Append Only File

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

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

三种策略

always:每条命令都刷盘

everysec:每一秒刷一次

no:操作系统决定

什么是AOF

快照功能(RDB)并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、以及未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。

如果要使用AOF,需要修改配置文件:

Redis学习

appendonly no yes则表示启用AOF

默认是不开启的,我们需要手动配置,然后重启redis,就可以生效了!

如果这个aof文件有错位,这时候redis是启动不起来的,我需要修改这个aof文件

redis给我们提供了一个工具 redis-check-aof --fix

优点和缺点

123456appendonly yes  # &#x9ED8;&#x8BA4;&#x662F;&#x4E0D;&#x5F00;&#x542F;aof&#x6A21;&#x5F0F;&#x7684;&#xFF0C;&#x9ED8;&#x8BA4;&#x662F;&#x4F7F;&#x7528;rdb&#x65B9;&#x5F0F;&#x6301;&#x4E45;&#x5316;&#x7684;&#xFF0C;&#x5728;&#x5927;&#x90E8;&#x5206;&#x7684;&#x60C5;&#x51B5;&#x4E0B;&#xFF0C;rdb&#x5B8C;&#x5168;&#x591F;&#x7528;
appendfilename "appendonly.aof"

appendfsync always # &#x6BCF;&#x6B21;&#x4FEE;&#x6539;&#x90FD;&#x4F1A;sync &#x6D88;&#x8017;&#x6027;&#x80FD;
appendfsync everysec # &#x6BCF;&#x79D2;&#x6267;&#x884C;&#x4E00;&#x6B21; sync &#x53EF;&#x80FD;&#x4F1A;&#x4E22;&#x5931;&#x8FD9;&#x4E00;&#x79D2;&#x7684;&#x6570;&#x636E;
appendfsync no # &#x4E0D;&#x6267;&#x884C; sync ,&#x8FD9;&#x65F6;&#x5019;&#x64CD;&#x4F5C;&#x7CFB;&#x7EDF;&#x81EA;&#x5DF1;&#x540C;&#x6B65;&#x6570;&#x636E;&#xFF0C;&#x901F;&#x5EA6;&#x6700;&#x5FEB;

优点

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

缺点

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

AOF重写

fork出子进程,一个进行重写,一个把重写过程中产生的aof记录写到buffer中,最后添加到AOF新文件中。

RDB和AOP选择

RDB优势与劣势

优势

  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高

劣势

  • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。
  • Fork的时候,内存中的数据被 &#x514B;&#x9686;了一份, &#x5927;&#x81F4;2&#x500D;的膨胀性能需要考虑

AOF优势/劣势

优势

  • 每次修改同步:appendfsync always同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
  • 每秒同步:appendfsync everysec异步操作,每秒记录,如果一秒内宕机,仅一秒内的数据丢失

劣势

  • 相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
  • Aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

RDB消耗性能大,如果宕机可能丢失最后一次所做的修改,但是文件小,恢复快

为什么RDB恢复数据集较快

AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的;
RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可

Redis学习

如何选择使用哪种持久化方式?

一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

常见问题

fork

内存越大,fork时间越长,所以设置参数maxmemory参数

降低fork频率,避免不必要的全量复制

Redis学习

AOF追加阻塞

Redis学习

距离上次同步超过两秒,就阻塞主线程,直到同步完成

参考硬盘优化的相关策略

Redis学习

Redis发布订阅

订阅

subscribe hongdou
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "hongdou"
3) (integer) 1

发布

127.0.0.1:6379> publish hongdou hahaha
(integer) 1

结果:

1) "message"
2) "hongdou"
3) "hahaha"

原理

每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。

Redis学习

客户端订阅,就被链接到对应频道的链表的尾部,退订则就是将客户端节点从链表中移除。

缺点

  1. 如果一个客户端订阅了频道,但自己读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
  2. 这和数据传输可靠性有关,如果在订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。

应用

  1. 消息订阅:公众号订阅,微博关注等等(起始更多是使用消息队列来进行实现)
  2. 多人在线聊天室。

稍微复杂的场景,我们就会使用消息中间件MQ处理。

Redis主从复制

概念

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

默认情况下,每台Redis服务器都是主节点一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。

作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
  2. 故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
  3. 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
  4. 高可用基石:主从复制还是哨兵和集群能够实施的基础。

为什么使用集群

  1. 单台服务器难以负载大量的请求
  2. 单台服务器故障率高,系统崩坏概率大
  3. 单台服务器内存容量有限。

环境配置

我们在讲解配置文件的时候,注意到有一个 replication模块 (见Redis.conf中第8条)

查看当前库的信息: info replication

123456789101112127.0.0.1:6379> info replication
Replication
role:master # &#x89D2;&#x8272;
connected_slaves:0 # &#x4ECE;&#x673A;&#x6570;&#x91CF;
master_replid:3b54deef5b7b7b7f7dd8acefa23be48879b4fcff
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

既然需要启动多个服务,就需要多个配置文件。每个配置文件对应修改以下信息:

  • 端口号
  • pid文件名
  • 日志文件名
  • rdb文件名
注意:window配置方法:

开启多个端口服务器
1.配置文件
将redis.windows-service.conf复制一份,改名为相应文件,并更改配置文件中的端口为指定端口,以6380为例

port 6380

2.安装服务
redis-server –service-install –service-name redis_6380 redis.windows-service-6380.conf
3.启动服务
redis-server –service-start –service-name redis_6380
4.停止服务
redis-server –service-stop –service-name redis_6380
5.卸载服务
redis-server –service-uninstall –service-name redis_6380

指定端口启动客户端:
redis.cli -p 端口号

Redis学习

启动单机多服务集群:

Redis学习

一主二从配置

默认情况下,每台Redis服务器都是主节点;一般情况下只用配置从机就好了!

认老大!一主(79)二从(80,81)

使用 SLAVEOF host port就可以为从机配置主机了。

Redis学习

Redis学习

主机截图:

Redis学习

使用命令搭建是暂时的,真实开发中应该在从机的配置文件中进行配置,这样的话是永久的。

Redis学习

规则

从机只能读,不能写,主机可读可写但是多用于写。

当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。

当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理

复制原理

从机成功连接到主机后会发送一个同步命令

主机启动bgsave,开始生成RDB文件,同时把期间的写命令缓存在内存中,RDB文件生成后发送给slave,slave会先写入磁盘,再从磁盘加载到内存,接着master将缓存中的写命令发送到slave,slave再进行同步

Redis学习

全量复制:主机生成RDB——-》同时缓存写命令———–》发送给从机,从机清空旧数据加载rdb到内存中———》同时基于旧数据版本提供服务

增量复制:每次更新数据同步到从机

过期key处理:主机删掉,模拟一条del指令发给slave

层层链路

主机从机成链路式连接

如果主机断开了连接,我们可以使用 SLAVEOF no one让自己变成主机!其他的节点就可以手动连接到最新的主节点

哨兵模式的全部配置

完整的哨兵模式配置文件 sentinel.conf

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970# Example sentinel.conf

&#x54E8;&#x5175;sentinel&#x5B9E;&#x4F8B;&#x8FD0;&#x884C;&#x7684;&#x7AEF;&#x53E3; &#x9ED8;&#x8BA4;26379
port 26379

&#x54E8;&#x5175;sentinel&#x7684;&#x5DE5;&#x4F5C;&#x76EE;&#x5F55;
dir /tmp

&#x54E8;&#x5175;sentinel&#x76D1;&#x63A7;&#x7684;redis&#x4E3B;&#x8282;&#x70B9;&#x7684; ip port
master-name  &#x53EF;&#x4EE5;&#x81EA;&#x5DF1;&#x547D;&#x540D;&#x7684;&#x4E3B;&#x8282;&#x70B9;&#x540D;&#x5B57; &#x53EA;&#x80FD;&#x7531;&#x5B57;&#x6BCD;A-z&#x3001;&#x6570;&#x5B57;0-9 &#x3001;&#x8FD9;&#x4E09;&#x4E2A;&#x5B57;&#x7B26;".-_"&#x7EC4;&#x6210;&#x3002;
quorum &#x5F53;&#x8FD9;&#x4E9B;quorum&#x4E2A;&#x6570;sentinel&#x54E8;&#x5175;&#x8BA4;&#x4E3A;master&#x4E3B;&#x8282;&#x70B9;&#x5931;&#x8054; &#x90A3;&#x4E48;&#x8FD9;&#x65F6; &#x5BA2;&#x89C2;&#x4E0A;&#x8BA4;&#x4E3A;&#x4E3B;&#x8282;&#x70B9;&#x5931;&#x8054;&#x4E86;
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1

&#x5F53;&#x5728;Redis&#x5B9E;&#x4F8B;&#x4E2D;&#x5F00;&#x542F;&#x4E86;requirepass foobared &#x6388;&#x6743;&#x5BC6;&#x7801; &#x8FD9;&#x6837;&#x6240;&#x6709;&#x8FDE;&#x63A5;Redis&#x5B9E;&#x4F8B;&#x7684;&#x5BA2;&#x6237;&#x7AEF;&#x90FD;&#x8981;&#x63D0;&#x4F9B;&#x5BC6;&#x7801;
&#x8BBE;&#x7F6E;&#x54E8;&#x5175;sentinel &#x8FDE;&#x63A5;&#x4E3B;&#x4ECE;&#x7684;&#x5BC6;&#x7801; &#x6CE8;&#x610F;&#x5FC5;&#x987B;&#x4E3A;&#x4E3B;&#x4ECE;&#x8BBE;&#x7F6E;&#x4E00;&#x6837;&#x7684;&#x9A8C;&#x8BC1;&#x5BC6;&#x7801;
sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

&#x6307;&#x5B9A;&#x591A;&#x5C11;&#x6BEB;&#x79D2;&#x4E4B;&#x540E; &#x4E3B;&#x8282;&#x70B9;&#x6CA1;&#x6709;&#x5E94;&#x7B54;&#x54E8;&#x5175;sentinel &#x6B64;&#x65F6; &#x54E8;&#x5175;&#x4E3B;&#x89C2;&#x4E0A;&#x8BA4;&#x4E3A;&#x4E3B;&#x8282;&#x70B9;&#x4E0B;&#x7EBF; &#x9ED8;&#x8BA4;30&#x79D2;
sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000

&#x8FD9;&#x4E2A;&#x914D;&#x7F6E;&#x9879;&#x6307;&#x5B9A;&#x4E86;&#x5728;&#x53D1;&#x751F;failover&#x4E3B;&#x5907;&#x5207;&#x6362;&#x65F6;&#x6700;&#x591A;&#x53EF;&#x4EE5;&#x6709;&#x591A;&#x5C11;&#x4E2A;slave&#x540C;&#x65F6;&#x5BF9;&#x65B0;&#x7684;master&#x8FDB;&#x884C; &#x540C;&#x6B65;&#xFF0C;
&#x8FD9;&#x4E2A;&#x6570;&#x5B57;&#x8D8A;&#x5C0F;&#xFF0C;&#x5B8C;&#x6210;failover&#x6240;&#x9700;&#x7684;&#x65F6;&#x95F4;&#x5C31;&#x8D8A;&#x957F;&#xFF0C;
&#x4F46;&#x662F;&#x5982;&#x679C;&#x8FD9;&#x4E2A;&#x6570;&#x5B57;&#x8D8A;&#x5927;&#xFF0C;&#x5C31;&#x610F;&#x5473;&#x7740;&#x8D8A; &#x591A;&#x7684;slave&#x56E0;&#x4E3A;replication&#x800C;&#x4E0D;&#x53EF;&#x7528;&#x3002;
&#x53EF;&#x4EE5;&#x901A;&#x8FC7;&#x5C06;&#x8FD9;&#x4E2A;&#x503C;&#x8BBE;&#x4E3A; 1 &#x6765;&#x4FDD;&#x8BC1;&#x6BCF;&#x6B21;&#x53EA;&#x6709;&#x4E00;&#x4E2A;slave &#x5904;&#x4E8E;&#x4E0D;&#x80FD;&#x5904;&#x7406;&#x547D;&#x4EE4;&#x8BF7;&#x6C42;&#x7684;&#x72B6;&#x6001;&#x3002;
sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1

&#x6545;&#x969C;&#x8F6C;&#x79FB;&#x7684;&#x8D85;&#x65F6;&#x65F6;&#x95F4; failover-timeout &#x53EF;&#x4EE5;&#x7528;&#x5728;&#x4EE5;&#x4E0B;&#x8FD9;&#x4E9B;&#x65B9;&#x9762;&#xFF1A;
#1. &#x540C;&#x4E00;&#x4E2A;sentinel&#x5BF9;&#x540C;&#x4E00;&#x4E2A;master&#x4E24;&#x6B21;failover&#x4E4B;&#x95F4;&#x7684;&#x95F4;&#x9694;&#x65F6;&#x95F4;&#x3002;
#2. &#x5F53;&#x4E00;&#x4E2A;slave&#x4ECE;&#x4E00;&#x4E2A;&#x9519;&#x8BEF;&#x7684;master&#x90A3;&#x91CC;&#x540C;&#x6B65;&#x6570;&#x636E;&#x5F00;&#x59CB;&#x8BA1;&#x7B97;&#x65F6;&#x95F4;&#x3002;&#x76F4;&#x5230;slave&#x88AB;&#x7EA0;&#x6B63;&#x4E3A;&#x5411;&#x6B63;&#x786E;&#x7684;master&#x90A3;&#x91CC;&#x540C;&#x6B65;&#x6570;&#x636E;&#x65F6;&#x3002;
#3.&#x5F53;&#x60F3;&#x8981;&#x53D6;&#x6D88;&#x4E00;&#x4E2A;&#x6B63;&#x5728;&#x8FDB;&#x884C;&#x7684;failover&#x6240;&#x9700;&#x8981;&#x7684;&#x65F6;&#x95F4;&#x3002;
#4.&#x5F53;&#x8FDB;&#x884C;failover&#x65F6;&#xFF0C;&#x914D;&#x7F6E;&#x6240;&#x6709;slaves&#x6307;&#x5411;&#x65B0;&#x7684;master&#x6240;&#x9700;&#x7684;&#x6700;&#x5927;&#x65F6;&#x95F4;&#x3002;&#x4E0D;&#x8FC7;&#xFF0C;&#x5373;&#x4F7F;&#x8FC7;&#x4E86;&#x8FD9;&#x4E2A;&#x8D85;&#x65F6;&#xFF0C;slaves&#x4F9D;&#x7136;&#x4F1A;&#x88AB;&#x6B63;&#x786E;&#x914D;&#x7F6E;&#x4E3A;&#x6307;&#x5411;master&#xFF0C;&#x4F46;&#x662F;&#x5C31;&#x4E0D;&#x6309;parallel-syncs&#x6240;&#x914D;&#x7F6E;&#x7684;&#x89C4;&#x5219;&#x6765;&#x4E86;
&#x9ED8;&#x8BA4;&#x4E09;&#x5206;&#x949F;
sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000

SCRIPTS EXECUTION

#&#x914D;&#x7F6E;&#x5F53;&#x67D0;&#x4E00;&#x4E8B;&#x4EF6;&#x53D1;&#x751F;&#x65F6;&#x6240;&#x9700;&#x8981;&#x6267;&#x884C;&#x7684;&#x811A;&#x672C;&#xFF0C;&#x53EF;&#x4EE5;&#x901A;&#x8FC7;&#x811A;&#x672C;&#x6765;&#x901A;&#x77E5;&#x7BA1;&#x7406;&#x5458;&#xFF0C;&#x4F8B;&#x5982;&#x5F53;&#x7CFB;&#x7EDF;&#x8FD0;&#x884C;&#x4E0D;&#x6B63;&#x5E38;&#x65F6;&#x53D1;&#x90AE;&#x4EF6;&#x901A;&#x77E5;&#x76F8;&#x5173;&#x4EBA;&#x5458;&#x3002;
#&#x5BF9;&#x4E8E;&#x811A;&#x672C;&#x7684;&#x8FD0;&#x884C;&#x7ED3;&#x679C;&#x6709;&#x4EE5;&#x4E0B;&#x89C4;&#x5219;&#xFF1A;
#&#x82E5;&#x811A;&#x672C;&#x6267;&#x884C;&#x540E;&#x8FD4;&#x56DE;1&#xFF0C;&#x90A3;&#x4E48;&#x8BE5;&#x811A;&#x672C;&#x7A0D;&#x540E;&#x5C06;&#x4F1A;&#x88AB;&#x518D;&#x6B21;&#x6267;&#x884C;&#xFF0C;&#x91CD;&#x590D;&#x6B21;&#x6570;&#x76EE;&#x524D;&#x9ED8;&#x8BA4;&#x4E3A;10
#&#x82E5;&#x811A;&#x672C;&#x6267;&#x884C;&#x540E;&#x8FD4;&#x56DE;2&#xFF0C;&#x6216;&#x8005;&#x6BD4;2&#x66F4;&#x9AD8;&#x7684;&#x4E00;&#x4E2A;&#x8FD4;&#x56DE;&#x503C;&#xFF0C;&#x811A;&#x672C;&#x5C06;&#x4E0D;&#x4F1A;&#x91CD;&#x590D;&#x6267;&#x884C;&#x3002;
#&#x5982;&#x679C;&#x811A;&#x672C;&#x5728;&#x6267;&#x884C;&#x8FC7;&#x7A0B;&#x4E2D;&#x7531;&#x4E8E;&#x6536;&#x5230;&#x7CFB;&#x7EDF;&#x4E2D;&#x65AD;&#x4FE1;&#x53F7;&#x88AB;&#x7EC8;&#x6B62;&#x4E86;&#xFF0C;&#x5219;&#x540C;&#x8FD4;&#x56DE;&#x503C;&#x4E3A;1&#x65F6;&#x7684;&#x884C;&#x4E3A;&#x76F8;&#x540C;&#x3002;
#&#x4E00;&#x4E2A;&#x811A;&#x672C;&#x7684;&#x6700;&#x5927;&#x6267;&#x884C;&#x65F6;&#x95F4;&#x4E3A;60s&#xFF0C;&#x5982;&#x679C;&#x8D85;&#x8FC7;&#x8FD9;&#x4E2A;&#x65F6;&#x95F4;&#xFF0C;&#x811A;&#x672C;&#x5C06;&#x4F1A;&#x88AB;&#x4E00;&#x4E2A;SIGKILL&#x4FE1;&#x53F7;&#x7EC8;&#x6B62;&#xFF0C;&#x4E4B;&#x540E;&#x91CD;&#x65B0;&#x6267;&#x884C;&#x3002;

#&#x901A;&#x77E5;&#x578B;&#x811A;&#x672C;:&#x5F53;sentinel&#x6709;&#x4EFB;&#x4F55;&#x8B66;&#x544A;&#x7EA7;&#x522B;&#x7684;&#x4E8B;&#x4EF6;&#x53D1;&#x751F;&#x65F6;&#xFF08;&#x6BD4;&#x5982;&#x8BF4;redis&#x5B9E;&#x4F8B;&#x7684;&#x4E3B;&#x89C2;&#x5931;&#x6548;&#x548C;&#x5BA2;&#x89C2;&#x5931;&#x6548;&#x7B49;&#x7B49;&#xFF09;&#xFF0C;&#x5C06;&#x4F1A;&#x53BB;&#x8C03;&#x7528;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#xFF0C;
#&#x8FD9;&#x65F6;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#x5E94;&#x8BE5;&#x901A;&#x8FC7;&#x90AE;&#x4EF6;&#xFF0C;SMS&#x7B49;&#x65B9;&#x5F0F;&#x53BB;&#x901A;&#x77E5;&#x7CFB;&#x7EDF;&#x7BA1;&#x7406;&#x5458;&#x5173;&#x4E8E;&#x7CFB;&#x7EDF;&#x4E0D;&#x6B63;&#x5E38;&#x8FD0;&#x884C;&#x7684;&#x4FE1;&#x606F;&#x3002;&#x8C03;&#x7528;&#x8BE5;&#x811A;&#x672C;&#x65F6;&#xFF0C;&#x5C06;&#x4F20;&#x7ED9;&#x811A;&#x672C;&#x4E24;&#x4E2A;&#x53C2;&#x6570;&#xFF0C;
#&#x4E00;&#x4E2A;&#x662F;&#x4E8B;&#x4EF6;&#x7684;&#x7C7B;&#x578B;&#xFF0C;
#&#x4E00;&#x4E2A;&#x662F;&#x4E8B;&#x4EF6;&#x7684;&#x63CF;&#x8FF0;&#x3002;
#&#x5982;&#x679C;sentinel.conf&#x914D;&#x7F6E;&#x6587;&#x4EF6;&#x4E2D;&#x914D;&#x7F6E;&#x4E86;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#x8DEF;&#x5F84;&#xFF0C;&#x90A3;&#x4E48;&#x5FC5;&#x987B;&#x4FDD;&#x8BC1;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#x5B58;&#x5728;&#x4E8E;&#x8FD9;&#x4E2A;&#x8DEF;&#x5F84;&#xFF0C;&#x5E76;&#x4E14;&#x662F;&#x53EF;&#x6267;&#x884C;&#x7684;&#xFF0C;&#x5426;&#x5219;sentinel&#x65E0;&#x6CD5;&#x6B63;&#x5E38;&#x542F;&#x52A8;&#x6210;&#x529F;&#x3002;
#&#x901A;&#x77E5;&#x811A;&#x672C;
sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh

&#x5BA2;&#x6237;&#x7AEF;&#x91CD;&#x65B0;&#x914D;&#x7F6E;&#x4E3B;&#x8282;&#x70B9;&#x53C2;&#x6570;&#x811A;&#x672C;
&#x5F53;&#x4E00;&#x4E2A;master&#x7531;&#x4E8E;failover&#x800C;&#x53D1;&#x751F;&#x6539;&#x53D8;&#x65F6;&#xFF0C;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#x5C06;&#x4F1A;&#x88AB;&#x8C03;&#x7528;&#xFF0C;&#x901A;&#x77E5;&#x76F8;&#x5173;&#x7684;&#x5BA2;&#x6237;&#x7AEF;&#x5173;&#x4E8E;master&#x5730;&#x5740;&#x5DF2;&#x7ECF;&#x53D1;&#x751F;&#x6539;&#x53D8;&#x7684;&#x4FE1;&#x606F;&#x3002;
&#x4EE5;&#x4E0B;&#x53C2;&#x6570;&#x5C06;&#x4F1A;&#x5728;&#x8C03;&#x7528;&#x811A;&#x672C;&#x65F6;&#x4F20;&#x7ED9;&#x811A;&#x672C;:
<master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
&#x76EE;&#x524D;<state>&#x603B;&#x662F;&#x201C;failover&#x201D;,
<role>&#x662F;&#x201C;leader&#x201D;&#x6216;&#x8005;&#x201C;observer&#x201D;&#x4E2D;&#x7684;&#x4E00;&#x4E2A;&#x3002;
&#x53C2;&#x6570; from-ip, from-port, to-ip, to-port&#x662F;&#x7528;&#x6765;&#x548C;&#x65E7;&#x7684;master&#x548C;&#x65B0;&#x7684;master(&#x5373;&#x65E7;&#x7684;slave)&#x901A;&#x4FE1;&#x7684;
&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#x5E94;&#x8BE5;&#x662F;&#x901A;&#x7528;&#x7684;&#xFF0C;&#x80FD;&#x88AB;&#x591A;&#x6B21;&#x8C03;&#x7528;&#xFF0C;&#x4E0D;&#x662F;&#x9488;&#x5BF9;&#x6027;&#x7684;&#x3002;
sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh</script-path></master-name></role></state></to-port></to-ip></from-port></from-ip></state></role></master-name></script-path></master-name></milliseconds></master-name></numslaves></master-name></milliseconds></master-name></password></master-name></quorum></redis-port></ip></master-name>

缓存穿透与雪崩

缓存穿透(查不到)

概念

在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。

解决方案

布隆过滤器

对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力。

Redis学习

缓存空对象

一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。

Redis学习

这样做有一个缺陷:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间

即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

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

概念

相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。

比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。

解决方案

  1. 设置热点数据永不过期 这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
  2. 加互斥锁(分布式锁) 在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。

缓存雪崩

概念

大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

Redis学习

解决方案

  • redis高可用 这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
  • 限流降级 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 数据预热 数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

缓存一致性

先更新数据库,在更新缓存,先更新缓存,再更新数据库都会有问题

一般使用 先更新数据库再删除缓存可以解决,因为缓存的写入要快于数据库的写入,但是会影响缓存的命中率,

那么缓存删除失败怎么办?

  1. 先删除缓存,再更新数据库
  2. 此时就引入了延时双删:
&#x5220;&#x9664;&#x7F13;&#x5B58;
&#x66F4;&#x65B0;&#x6570;&#x636E;&#x5E93;
&#x7761;&#x7720; &#x9700;&#x8981;&#x5927;&#x4E8E;&#x7B2C;&#x4E8C;&#x4E2A;&#x7EBF;&#x7A0B;&#x8BFB;&#x53D6;&#x6570;&#x636E;&#x5E76;&#x5199;&#x5165;&#x7F13;&#x5B58;&#x7684;&#x65F6;&#x95F4;
&#x5220;&#x9664;&#x7F13;&#x5B58;

睡眠时间难以估算,所以还是先更新数据库再删除缓存比较好

那么为什么不是更新缓存而是删除缓存呢,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算

Redis 过期策略

定期删除+惰性删除

如果还有大量的过期数据既没有被用到过,又没有被定期删除扫描到那怎么办,就引入 淘汰策略

淘汰策略

  • noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
  • allkeys-lru:当内存不足以容纳新写入数据时,在 键空间中,移除最近最少使用的 key(这个是 最常用的)。
  • allkeys-random:当内存不足以容纳新写入数据时,在 键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
  • volatile-lru:当内存不足以容纳新写入数据时,在 设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
  • volatile-random:当内存不足以容纳新写入数据时,在 设置了过期时间的键空间中, 随机移除某个 key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在 设置了过期时间的键空间中,有 更早过期时间的 key 优先移除。

Original: https://www.cnblogs.com/zz01/p/16489834.html
Author: 山野村夫01
Title: Redis学习

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621426/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • feign之间传递oauth2-token的问题和解决~续

    之前写过关于修改hystric的隔离《feign之间传递oauth2-token的问题和解决》方式来在feign调用各个微服务中传递token,修改为SEMAPHORE之后,会有一…

    数据库 2023年6月6日
    086
  • 计算机操作系统(慕课版)思维导图

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    数据库 2023年6月16日
    095
  • 2021 idea热部署

    依赖 org.springframework.boot spring-boot-devtools runtime true 导入 maven 插件 org.springframew…

    数据库 2023年6月14日
    099
  • springboot~ApplicationContextAware与@Autowired注解

    ApplicationContextAware是一个接口,它提供一个方法setApplicationContext,当spring注册完成之后,会把ApplicationConte…

    数据库 2023年6月6日
    081
  • sarama Kafka客户端生产者与消费者梳理

    生产者 sarama 库提供了同步生产者和异步生产者。 SyncProducer 是在 AsyncProducer 基础上加以条件限制实现的。 type SyncProducer …

    数据库 2023年6月16日
    067
  • 16-ArrayList和LinkedList的区别

    1.1、作用 ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用。它们可以对元素的增删改查进行操作 对于ArrayList,它在集合的…

    数据库 2023年6月16日
    077
  • Linux 守护进程

    1. 守护进程是什么 2. 怎么用守护进程 2.1 有趣小例子 2.2 man daemon 3. 源码解析 3.1 GUN C daemon.c 3.2 daemon.c 解析 …

    数据库 2023年6月9日
    072
  • JavaScript详解

    一、快速入门 1.JavaScript的两种引入方式 方式一:直接在html里面写 目的:实现简单的弹窗 点击查看代码 <!DOCTYPE html> <html…

    数据库 2023年6月16日
    060
  • MySQL数据库索引介绍

    一、什么是索引 索引是mysql数据库中的一种数据结构,就是一种数据的组织方式,这种数据结构又称为key 表中的一行行数据按照索引规定的结构组织成了一种树型结构,该树叫B+树 二、…

    数据库 2023年5月24日
    067
  • 从源码角度谈谈open_files_limit的生成逻辑及”Too many open files”的解决思路

    “Too many open files”是一个比较常见的错误,不仅仅是在 MySQL 中。只要是在 Linux 中启动的进程,都有可能遇到这个错误。 究其…

    数据库 2023年6月11日
    0142
  • 如何本地navicat连接虚拟机安装的linux 的mysql

    2022.3.20 如何本地连接虚拟机安装的linux 的mysql 1防火墙开启开启 1.1、开启端口3306 1.2、重启防火墙 1.3查看已经开放的端口: 如果mysql 密…

    数据库 2023年5月24日
    075
  • MySQL锁:03.InnoDB行锁

    传送门:MySQL锁:01.总览传送门:MySQL锁:02.InnoDB锁传送门:MySQL锁:03.InnoDB行锁 InnoDB 行锁 锁排查可以用的视图和数据字典 InnoD…

    数据库 2023年6月16日
    0126
  • Mysql数据库存取原理及性能优化

    一、Mysql的系统架构图 二、Mysql存储引擎 Mysql中的数据是通过一定的方式存储在文件或者内存中的,任何方式都有不同的存储、查找和更新机制,这意味着选择不同的方式对于数据…

    数据库 2023年6月14日
    0101
  • 在centos环境下编译安装myrocksdb

    rocksdb(https://rocksdb.org.cn/)是脸书(facebook)公司开源的一个key-value存储引擎,基于leveldb开发。rocksdb使用的是L…

    数据库 2023年6月14日
    095
  • DASCTF 熟悉的猫

    压缩包需要密码,不是伪加密,爆破也爆不出来,下面的kdbx文件搜了一下可以用keepass ;打开,但是居然还是需要密码 那这个只能是弱口令或者爆破了,但是纯手工肯定不可取,找了很…

    数据库 2023年6月11日
    073
  • MIT 6.824 Lab2C Raft之持久化

    书接上文Raft Part B | MIT 6.824 Lab2B Log Replication。 实验准备 实验代码: git://g.csail.mit.edu/6.824-…

    数据库 2023年6月14日
    063
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球