七、Redis主从复制

在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,我们称呼被复制的服务器为主服务器,而对主服务器进行复制的服务器则被称为从服务器

1、同步和命令传播(旧版复制功能)

Redis的复制功能分为同步命令传播两个操作:

  • 同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态
  • 命令传播操作则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态

1)、同步

从服务器对主服务器的同步操作需要通过向主服务器发送SYNC命令来完成,步骤:

1)从服务器向主服务器发送SYNC命令

2)收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令

3)当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态

4)主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态

在这里插入图片描述

2)、命令传播

处于一致状态的主从服务器:

在这里插入图片描述

客户端发送DEL k3导致主从服务器处于不一致状态:

在这里插入图片描述

主服务器向从服务器发送命令:

在这里插入图片描述

主服务器会将自己执行的写命令,也就是造成主从服务器不一致的那条写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态

2、完整重同步和部分重同步(新版复制功能)

使用PSYNC命令代替SYNC命令来执行复制时的同步操作,PSYNC命令具有完整重同步部分重同步两种模式:

  • 完整重同步用于处理初次复制情况:完整重同步的执行步骤和SYNC命令的执行步骤基本一样
  • 部分重同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态

在这里插入图片描述

1)、部分重同步的实现

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量和从服务器的复制偏移量
  • 主服务器的复制积压缓冲区
  • 服务器的运行ID

1)复制偏移量

在这里插入图片描述

在这里插入图片描述

主服务器和从服务器会分别维护一个复制偏移量

  • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
  • 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N

通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:

  • 如果主从服务器处于一致状态,那么主从服务器两者的偏移量总是相同的
  • 相反,如果主从服务器两者的偏移量并不相同,那么说明主从服务器并未处于一致状态

2)复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度先进先出队列,默认大小为1MB

在这里插入图片描述

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令写入队到复制积压缓冲区里面

主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量

当从服务器重新连接上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器:

  • 如果offset偏移量之后的数据仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作
  • 相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作

3)服务器运行ID

  • 每个Redis服务器,不论主服务器还是从服务器,都会有自己的运行ID
  • 运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送个从服务器,而从服务器则会将这个运行ID保存起来

当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:

  • 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作
  • 相反,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作

2)、PSYNC命令的实现

PSYNC命令的调用方法有两种:

  • 如果从服务器以前没有复制过任何主服务器,或者之前执行SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步
  • 相反,如果从服务器已经复制过某个主服务器,那么从服务器开始一次新的复制时将向主服务器发送PSYNC <runid> <offset>命令:其中runid是上一次复制的主服务器的运行ID,而offset则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数来判断应该对从服务器执行哪种同步操作

在这里插入图片描述

  • 如果主服务器返回+FULLRESYNC <runid> <offset>回复,那么表示主服务器将与从服务器执行完整重同步操作:其中runid是这个主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset则是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量
  • 如果主服务器返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了
  • 如果主服务器返回-ERR回复,那么表示主服务器的版本低于Redis 2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作

八、Redis哨兵与集群

1、Redis哨兵

由一个或多个Sentinel实例组成的Sentinel系统可以监视多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求

在这里插入图片描述

上图中,server1为当前的主服务器,server2、server3、server4三个从服务器正在复制主服务器server1,而Sentinel系统则在监视所有四个服务器

在这里插入图片描述

如果这时,主服务器server1进入下线状态,那么从服务器server2、server3、server4对主服务器的复制操作将被中止,并且Sentinel系统会察觉到server1已下线

当server1的下线时长超过用户设定的下线时长上限时,Sentinel系统就会对server1执行故障转移操作:

1)首先,Sentinel系统会挑选server1属下的其中一个从服务器,并将这个被选中的从服务器升级为新的主服务器

2)之后,Sentinel系统会向server1属下的所有从服务器发送新的复制指令,让它们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕

3)另外,Sentinel还会继续监视已下线的server1,并在它重新上线时,将它设置为新的主服务器的从服务器

在这里插入图片描述

在这里插入图片描述

1)、检测主观下线状态

在默认情况下,Sentinel会以每秒一次的频率向所有命令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线

Sentinel向实例发送PING命令

在这里插入图片描述

如果一个服务器没有在master-down-after-milliseconds选项所指定的时间内,对向它发送PING命令的Sentinel返回一个有效回复,那么Sentinel就会将这个服务器标记为主观下线

服务器对PING命令的有效回复可以是以下三种回复的其中一种:

  • 返回+PONG
  • 返回-LOADING错误
  • 返回-MASTERDOWN错误

2)、检查客观下线状态

当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态。当Sentinel从其他Sentinel那里接收到足够数量的已下线判断之后,Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作(客观下线条件只适用于主服务器

当认为主服务器已经进入下线状态的Sentinel的数量,超过Sentinel配置中设置的quorum参数的值,那么该Sentinel就会认为主服务器已经进入客观下线状态。比如说,如果Sentinel在启动时载入了以下配置:

sentinel monitor mymaster 127.0.0.1 6379 2

那么包括当前Sentinel在内,只要总共有两个Sentinel认为主服务器已经进入下线状态,那么当前Sentinel就将主服务器判断为客观下线

2、Redis集群

Redis集群架构图

在这里插入图片描述

上图蓝色圆圈代表Redis服务器节点,它们两两都相连,所以只要客户端连接到其中一台Redis服务器就可以对其他Redis服务器进行读写操作

1)、数据分片

Redis集群有16384个哈希槽,Redis会根据节点数量大致均等的将哈希槽映射到不同的节点。举个例子,比如当前集群有3个节点,那么

  • 节点A包含0到5500号哈希槽
  • 节点B包含5501到11000号哈希槽
  • 节点C包含11001到16383号哈希槽

当需要在Redis集群中存储一个key-value时,Redis先对key使用CRC16算法算出一个结果,然后把结果对16383求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽(CRC16(key) & 16383

当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己

  • 如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令
  • 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向至正确的节点,并再次发送之前想要执行的命令

在这里插入图片描述

2)、故障检测

在这里插入图片描述

集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线

集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息,例如某个节点是处于在线状态、疑似下线状态还是已下线状态

如果在一个集群里面,半数以上负责处理槽的主节点都将某个主节点x报告为疑似下线,那么这个主节点x将被标记为已下线,将主节点x标记为已下线的节点会向集群广播一条关于主节点x的FAIL消息,所有收到这条FAIL消息的节点都会立即将主节点x标记为已下线

3)、故障转移

当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤

1)复制下线主节点的所有从节点里面,会有一个从节点被选出

2)被选中的从节点会执行SLAVEOF no one命令,成为新的主节点

3)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己

4)新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽

5)新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成

九、分布式锁

为了确保分布式锁可用,锁的实现至少要同时满足以下三个条件:

  • 互斥性。在任意时刻,只有一个客户端能持有锁
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁
  • 加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了

1、加锁的实现

public class RedisLock {private static final String LOCK_SUCCESS = "OK";private static final String SET_IF_NOT_EXIST = "NX";private static final String SET_WITH_EXPIRE_TIME = "PX";/*** 尝试获取分布式锁** @param jedis      Redis客户端* @param lockKey    锁标识* @param requestId  请求标识* @param expireTime 超期时间(秒)* @return 是否获取成功*/public static boolean getLock(Jedis jedis, String lockKey, String requestId, int expireTime) {String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime * 1000);if (LOCK_SUCCESS.equals(result)) {return true;}return false;}

加锁过程中主要使用的redis命令set key value nx px expireTime,当key不存在或者已经过期时,进行set操作,返回OK;当key存在时,不做任何操作

对应的Java代码为:jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime * 1000)

  • key:传入锁标识
  • value:传入的是requestId,目的是为了实现加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,requestId可以使用UUID.randomUUID().toString()方法生成
  • nxxx:NX
  • expx:PX
  • time:key的过期时间

setnx保证了如果已有key存在,则函数不会调用成功,只有一个客户端能持有锁,满足互斥性

设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁

将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端

1)、错误示例1

    public static void getLock(Jedis jedis, String lockKey, String requestId, int expireTime) {Long result = jedis.setnx(lockKey, requestId);if (result == 1) {//若在这里程序突然崩溃,则无法设置过期时间,将发生死锁         jedis.expire(lockKey, expireTime);}}

实现思路:使用jedis.setnx()jedis.expire()组合实现加锁

存在的问题:通过两条Redis命令,不具有原子性,如果程序在执行完jedis.setnx()之后突然崩溃,导致锁没有设置过期时间,那么将会发生死锁(低版本的jedis并不支持多参数的set()方法)

2)、错误示例2

    public static boolean getLock(Jedis jedis, String lockKey, int expireTime) {long expires = System.currentTimeMillis() + expireTime;String expiresStr = String.valueOf(expires);//如果当前锁不存在,返回加锁成功if (jedis.setnx(lockKey, expiresStr) == 1) {return true;}//如果锁存在,获取锁的过期时间String currentValueStr = jedis.get(lockKey);if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {//锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间String oldValueStr = jedis.getSet(lockKey, expiresStr);if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {//考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才有权利加锁return true;}}//其他情况,一律返回加锁失败return false;}

实现思路:使用jedis.setnx()命令实现加锁,其中key是锁,value是锁的过期时间。通过setnx()方法尝试加锁,如果当前锁不存在,返回加锁成功。 如果锁已经存在则获取锁的过期时间,和当前时间比较,如果锁已经过期,则设置新的过期时间,返回加锁成功

存在的问题:

  • 由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步
  • 当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖
  • 锁不具备拥有者标识,即任何客户端都可以解锁

2、解锁的实现

    private static final Long RELEASE_SUCCESS = 1L;/*** 释放分布式锁** @param jedis     Redis客户端* @param lockKey   锁标识* @param requestId 请求标识* @return 是否释放成功*/public static boolean releaseLock(Jedis jedis, String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;}

这段Lua代码的功能是首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。使用eval()方法执行Lua语言来实现可以确保上述操作是原子性的

1)、错误示例

    public static void releaseLock(Jedis jedis, String lockKey, String requestId) {//判断加锁与解锁是不是同一个客户端if (requestId.equals(jedis.get(lockKey))) {//若在此时,这把锁突然不是这个客户端的,则会误解锁jedis.del(lockKey);}}

存在的问题:如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了

3、Redisson实现原理

1)、加锁源码分析

org.redisson.RedissonLock中的tryLock(long waitTime, long leaseTime, TimeUnit unit)方法

    @Overridepublic boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {//获取最大等待时间long time = unit.toMillis(waitTime);//记录当前时间long current = System.currentTimeMillis();//获取当前线程id(判断是否可重入锁的关键)long threadId = Thread.currentThread().getId();//尝试申请锁,返回还剩余的锁过期时间Long ttl = tryAcquire(leaseTime, unit, threadId);//如果为空,表示申请锁成功if (ttl == null) {return true;}//申请锁的耗时如果大于等于最大等待时间,则申请锁失败time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(threadId);return false;}current = System.currentTimeMillis();//订阅锁释放事件,并通过await方法阻塞等待锁释放,解决了无效的锁申请浪费资源的问题//当锁被其它资源占用时,当前线程通过Redis的channel订阅锁的释放事件,一旦锁释放会发消息通知待等待的线程进行竞争RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);//await()方法返回false,说明等待时间已经超出获取锁最大等待时间,取消订阅并返回获取锁失败//await()方法返回true,进入循环尝试获取锁if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {unsubscribe(subscribeFuture, threadId);}});}acquireFailed(threadId);return false;}try {//计算获取锁的总耗时,如果大于等于最大等待时间,则获取锁失败time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(threadId);return false;}//收到锁释放的信号后,在最大等待时间之内,循环一次接着一次的尝试获取锁//获取锁成功,则立马返回true//若在最大等待时间之内还没获取到锁,则认为获取锁失败,返回false结束循环while (true) {long currentTime = System.currentTimeMillis();//再次尝试申请锁ttl = tryAcquire(leaseTime, unit, threadId);//成功获取锁则直接返回true结束循环if (ttl == null) {return true;}//超过最大等待时间则返回false结束循环,获取锁失败time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(threadId);return false;}//阻塞等待锁currentTime = System.currentTimeMillis();if (ttl >= 0 && ttl < time) {//如果剩余时间(ttl)小于wait time,就在ttl时间内,从Entry的信号量获取一个许可(除非被中断或者一直没有可用的许可)subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {//则就在wait time时间范围内等待可以通过信号量subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}//更新剩余的等待时间(最大等待时间-已经消耗的阻塞时间)time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(threadId);return false;}}} finally {//无论是否获得锁,都要取消订阅解锁消息unsubscribe(subscribeFuture, threadId);}}
    private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) {return get(tryAcquireAsync(leaseTime, unit, threadId));}
    private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {if (leaseTime != -1) {return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);}RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e != null) {return;}// lock acquiredif (ttlRemaining == null) {scheduleExpirationRenewal(threadId);}});return ttlRemainingFuture;}
    <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,//如果缓存中的key不存在,则执行hset命令(hset key UUID+threadId 1),然后通过pexpire命令设置锁的过期时间                            "if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +//如果key已经存在,并且value也匹配,表示是当前线程持有的锁,则执行hincrby命令,重入次数加1,并且设置失效时间                            "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +//如果key已经存在,但是value不匹配,说明锁已经被其他线程持有,通过pttl命令获取锁的剩余存活时间并返回,至此获取锁失败                            "return redis.call('pttl', KEYS[1]);",Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}

参数说明

  • KEYS[1]:Collections.<Object>singletonList(getName()),表示分布式锁的key
  • ARGV[1]:internalLockLeaseTime,即锁的租约时间(持有锁的有效时间),默认30s
  • ARGV[2]:getLockName(threadId),是获取锁时set的唯一值value,即UUID+threadId

加锁流程图

在这里插入图片描述

2)、解锁源码分析

    public void unlock() {try {get(unlockAsync(Thread.currentThread().getId()));} catch (RedisException e) {if (e.getCause() instanceof IllegalMonitorStateException) {throw (IllegalMonitorStateException) e.getCause();} else {throw e;}}}
    public RFuture<Void> unlockAsync(long threadId) {RPromise<Void> result = new RedissonPromise<Void>();RFuture<Boolean> future = unlockInnerAsync(threadId);future.onComplete((opStatus, e) -> {cancelExpirationRenewal(threadId);if (e != null) {result.tryFailure(e);return;}if (opStatus == null) {IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "+ id + " thread-id: " + threadId);result.tryFailure(cause);return;}result.trySuccess(null);});return result;}
    protected RFuture<Boolean> unlockInnerAsync(long threadId) {return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,//如果分布式锁存在,但是value不匹配,表示锁已经被其他线程占用,无权释放锁,那么直接返回空值                              "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +"return nil;" +"end; " +//如果value匹配,则就是当前线程占有分布式锁,那么将重入次数减1                              "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +//重入次数减1后的值如果大于0,表示分布式锁有重入过,那么只能更新失效时间,还不能删除                              "if (counter > 0) then " +"redis.call('pexpire', KEYS[1], ARGV[2]); " +"return 0; " +//重入次数减1后的值如果为0,这时就可以删除这个key,并发布解锁消息,返回1                              "else " +"redis.call('del', KEYS[1]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1; "+"end; " +"return nil;",Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));}

解锁流程图

在这里插入图片描述

参考:http://ifeve.com/慢谈-redis-实现分布式锁-以及-redisson-源码解析

十、布隆过滤器

布隆过滤器可以用来判断一个元素是否在一个集合中。它的优势是只需要占用很小的内存空间以及有着高效的查询效率

对于布隆过滤器而言,它的本质是一个位数组:位数组就是数组的每个元素都只占用1bit ,并且每个元素只能是0或者1

布隆过滤器除了一个位数组,还有K个哈希函数。当一个元素加入布隆过滤器中的时候,会进行如下操作:

  • 使用K个哈希函数对元素值进行K次计算,得到K个哈希值
  • 根据得到的哈希值,在位数组中把对应下标的值置为1

下图表示有三个hash函数,比如一个集合中有x、y、z三个元素,分别用三个hash函数映射到二进制序列的某些位上

假设判断w是否在集合中,同样用三个hash函数来映射,结果发现取得的结果不全为1,则表示w不在集合里面

在这里插入图片描述

数组的容量即使再大,也是有限的。那么随着元素的增加,插入的元素就会越多,位数组中被置为1的位置因此也越多,这就会造成一种情况:当一个不在布隆过滤器中的元素,经过同样规则的哈希计算之后,得到的值在位数组中查询,有可能这些位置因为之前其它元素的操作先被置为1了

所以,有可能一个不存在布隆过滤器中的会被误判成在布隆过滤器中。这就是布隆过滤器的一个缺陷。但是,如果布隆过滤器判断某个元素不在布隆过滤器中,那么这个值就一定不在布隆过滤器中。总结就是:

  • 布隆过滤器说某个元素在,可能会被误判
  • 布隆过滤器说某个元素不在,那么一定不在

十一、缓存雪崩、缓存击穿、缓存穿透

1、缓存雪崩

1)、什么是缓存雪崩?

如果缓存集中在一段时间内失效,发生大量的缓存击穿,所有的查询都落在数据库上,造成了缓存雪崩

由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机

2)、有什么解决方案来防止缓存雪崩?

1)加锁排队

mutex互斥锁解决,Redis的SETNX去set一个mutex key,当操作返回成功时,再进行加载数据库的操作并回设缓存,否则,就重试整个get缓存的方法

2)数据预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key

3)双层缓存策略

C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期

4)定时更新缓存策略

实效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存

5)设置不同的过期时间,让缓存失效的时间点尽量均匀

2、缓存击穿

1)、什么是缓存击穿?

在平常高并发的系统中,大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿

2)、会带来什么问题

会造成某一时刻数据库请求量过大,压力剧增

3)、如何解决

上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存

3、缓存穿透

1)、什么是缓存穿透?

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到对应key的value,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库

2)、有什么解决方案来防止缓存穿透?

1)缓存空值

如果一个查询返回的数据为空(不管是数据不存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过5分钟。通过这个设置的默认值存放到缓存,这样第二次到缓存中获取就有值了,而不会继续访问数据库

2)采用布隆过滤器BloomFilter

优势:占用内存空间很小,位存储;性能特别高,使用key的hash判断key存不存在

将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力

在缓存之前在加一层BloomFilter,在查询的时候先去BloomFilter去查询key是否存在,如果不存在就直接返回,存在再去查询缓存,缓存中没有再去查询数据库

十二、常见的几种缓存模式

1、Cache Aside

  • 应用在查询数据的时候,先从缓存Cache中读取数据,如果缓存中没有,则再从数据库中读取数据,得到数据库的数据之后,将这个数据也放到缓存Cache中
  • 如果应用要更新某个数据,也是先去更新数据库中的数据,更新完成之后,则通过指令让缓存Cache中的数据失效

1)这里为什么不让更新操作在写完数据库之后,紧接着去把缓存Cache中的数据也修改了呢?

主要是因为这样做的话,就有2个写操作的事件了,可能在并发的情况下会导致脏数据,举个例子:假如同时有2个请求,请求A和请求B,并发的执行。请求A是要去读数据,请求B是要去更新数据。初始状态缓存中是没有数据的,当请求A读到数据之后,准备往回写的时候,此刻,请求B正好要更新数据,更新完了数据库之后,又去把缓存更新了,那请求A再往缓存中写的就是旧数据了,属于脏数据

2)那么Cache Aside模式就没有脏数据问题了吗?

在极端情况下也可能会产生脏数据。例如,同时有2个请求,请求A和请求B,并发的执行。请求A是要去读数据,请求B是要去写数据。假如初始状态缓存中没有这个数据,那请求A发现缓存中没有数据,就会去数据库中读数据,读到了数据准备写回缓存中,就在这个时候,请求B是要去写数据的,请求B在写完数据库的数据之后,又去设置了缓存失效。这个时候,请求A由于在数据库中读到了之前的旧数据,开始往缓存中写数据了,此时写进入的就也是旧数据。那么最终就会导致,缓存中的数据与数据库的数据不一致,造成了脏数据

2、Read/Write Through

  • 应用要读数据和更新数据都直接访问缓存服务
  • 缓存服务同步地将数据更新到数据库

出现脏数据的概率较低,但是就强依赖缓存,对缓存服务的稳定性有较大要求

3、Write Behind模式

  • 应用要读数据和更新数据都直接访问缓存服务
  • 缓存服务异步地将数据更新到数据库(通过异步任务)

速度快,效率会非常高,但是数据的一致性比较差,还可能会有数据的丢失情况,实现逻辑也较为复杂

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. unbuntu如何设置盖上笔记本,但是不会让客户端连接断开

    sudo vim /etc/systemd/logind.conf 然后将其中的: #HandleLidSwitch=suspend 改成: HandleLidSwitch=ignore //保存退出 重启 service systemd-logind restart...

    2024/4/27 21:39:02
  2. Python数据分析三大框架之 numpy (一)数组

    学习数据分析的三大入门框架之一的numpy,整理以下学习的知识点。数组部分数组类型# arrays data type (int float double ....) 数组类型 a = np.array([2,23,4],dtype = np.float) print(a) # print array data type print(a.dtype)数组维度#print number of dimation 维度 di…...

    2024/4/28 3:11:40
  3. 小白终于懂了!!!C# 枚举Enum你会了吗?

    C# 枚举(Enum) 枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。 C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。 声明 enum 变量 声明枚举的一般语法: enum <enum_name> { enumeration list }; 其中, enum_name 指定枚举的类型名…...

    2024/4/14 11:22:38
  4. 最大子序和 题解

    思路:这种算法有个前提,所求的最大子序和不能小于0. 有了这个前提,我们就可以直接从左往右依次累加。每次累加之后,若和大于0,则把值赋给Max,否则就清空 和,重新开始累加。class Solution { public:int maxSubArray(vector<int>& nums) {int max,add;max=num…...

    2024/4/14 11:22:34
  5. 制作动画

    针对前面的例子 《坎贝尔图分析》================================================== 1.在求解完之后,查看Results Summary可看出: 施加了9次转速,每次施加求解一次,共9个载荷步。 扩展了30阶模态,也就是每个载荷步内有30个子步。 最后一个载荷步便是加载完全的,他的30…...

    2024/4/28 9:03:39
  6. 华为IPV6-IPV4双栈过渡通信

    一、实现内网PC1与PC4/PC5能同时通信 二、配置思路 1.配置PC和路由器接口地址 2.在R1上配置两条默认路由指向R2和R3 3.在R2和R3分别配置静态路由指向内网网段 4.验证配置效果 三、PC的配置 1.PC1配置2.PC4配置3.PC5配置四、路由器的配置 1.R1配置 [R1]ipv6 R1]interface g0/0/0…...

    2024/4/14 11:22:30
  7. Mac上使用svn

    一、场景需求产品文档、原型等文件用的是SVN二、SVN的使用方法及常用命令 1. 打开终端cd xxx 进入到需要存储的位置2. 将文件checkout到本地目录 (svn checkout xxx )svn checkout svn://119.3.132.91/产品中心/版本3.0 输入密码3. 往版本库中添加新的文件 (svn add xxx)…...

    2024/4/14 11:22:26
  8. Job for mysqld.service failed because the control process exited with error code.

    centos7上使用yum安装mysql后,启动报错 [root@localhost ~]# systemctl start mysqld Job for mysqld.service failed because the control process exited with error code. See "systemctl status mysqld.service" and "journalctl -xe" for details.查…...

    2024/4/14 11:22:22
  9. Vue-cl 1.x / 2.x 更新至Vue-cl 4.x

    官方文档说明关于旧版本 Vue CLI 的包名称由 vue-cli 改成了 @vue/cli。 如果你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你需要先通过 npm uninstall vue-cli -g 或 yarn global remove vue-cli 卸载它。Node 版本要求 Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11…...

    2024/4/28 5:47:34
  10. pcl总结

    基本用法1、pcl功能介绍(解释各个模块和功能)2、开始/基本结构(介绍pcl基本数据结构和相关代码示例) 3、在自己项目中使用PCL(如何使用cmake PCL链接自己的项目) 4、如何编译PCL在POSIX / Unix系统。 5、解释PCL的cmake选项(基本的PCL cmake选项,以及如何调整以适应您的…...

    2024/4/14 11:22:14
  11. Java课程设计——仿windows系统计算器的设计

    仿windows系统计算器的设计 一、设计要求 参考Windows 2000/XP系统提供的计算器,设计实现一个计算器软件,具体实现以下几个功能: (1)提供以下运算:+、-、*、/、sqrt、1/x、xn、n!、log、ln、PI、sin、cos、exp及 ( )等运算。 (2)设置标准运算界面和科学运算界面,合理组…...

    2024/4/14 11:22:12
  12. 利用 JS 制作一个下拉列表(进阶版)

    利用 JS 制作一个下拉列表(进阶版) 直接上代码var w1 = document.querySelectorAll("#odiv .w1");for (let i=0 ;i<w1.length ; i++) {w1[i].addEventListener("click",function(){var div1 = w1[i].querySelector("#odiv .w1 .font2");div…...

    2024/4/14 11:22:05
  13. 代码段

    AdblocksPlus屏蔽百度热搜www.baidu.com##div[tpl="right_toplist"]www.baidu.com###content_rightWindows清除图标缓存taskkill /im explorer.exe /fcd /d %userprofile%\appdata\localdel iconcache.db /astart explorer.exeexit...

    2024/4/14 11:22:05
  14. PANet Few-Shot Image Semantic Segmentation with Prototype Alignment 论文理解

    文章目录解决什么问题本文创新点\贡献前人方法本文IDEA来源方法方法概述AlignmentPrototype learningNon-parametric metric learningPrototype alignment regularization (PAR)训练实验PAR的分析Generalization to weaker annotations总结 解决什么问题 少训练样本的分割本文创…...

    2024/4/18 4:00:17
  15. MySQl连接报错com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value

    我所遇到的这种错误的时候,console陷入了死循环.查看代码,并无错误,查看错误原因是要配置一个参数.我是使用的maven自动给我配置的Mysql8的驱动 原来: MySQL jdbc 6.0 版本以上必须配置“serverTimezone”参数 UTC代表的是全球标准时间 若我们使用的时间是北京时区也就是东八区…...

    2024/4/28 5:48:44
  16. vue 简介以及常见的指令

    vue 渐进式 js 框架 data数据与模板指令结合 以及样式 指令以及相关的概念 所有指令都要在指定的html标签内执行才有用 {{}} 显示的指令 data将数据与模板结合 指令的值应该看作js的表达 或者是data里面的值 模板指令中字符串的引用要用单引号文本渲染指令 {{js表达式}} …...

    2024/4/14 11:21:50
  17. Linux用户信息与文件管理

    文章目录用户身份文件权限与归属文件的特殊权限文件的隐藏属性文件访问控制你列表su命令与sudo服务 用户身份 Linux系统中用户分为三种:管理员UID为0:管理员用户(root)系统用户UID为1~999:负责运行服务程序普通用户UID从1000开始:是有管理员创建的用于日常工作的用户 UID…...

    2024/4/14 11:21:46
  18. 元素的隐藏和显示

    Vue中的元素显示和隐藏 v-if/v-showv-if:如果条件为false,则销毁节点,true:重新渲染节点(如果表达式默认条件为false,则不会渲染节点,懒汉式) v-show:通过css display属性来控制显示和隐藏(频繁时建议使用)小程序中的元素显示和隐藏 wx-if /hiddenwx:if 如果条件为f…...

    2024/4/14 11:21:42
  19. centos U盘启动盘制作

    1.安装软碟通,完成安装后打开软碟通,文件->打开,打开我们的iso镜像2.然后选择我们的U盘3.然后点击启动->写入硬盘映像4.写入方式有zip和hdd两种,一般我们选择hdd或hdd+,选择了写入方式之后要先格式化,格式化完毕之后点击写入等待写入完毕即可5.选择好写入方式直接写…...

    2024/4/14 11:21:38
  20. Keepalived+LVS 高可用负载均衡集群部署

    Keepalived为LVS调度器提供高可用功能,防止调度器单点故障,为用户提供Web服务 部署5台虚拟机,1台作为客户端主机、2台作为LVS调度器、2台作为Real Server client 192.168.4.1 proxy1 192.168.4.10 proxy2 192.168.4.11 web1 192.168.4.50 web2 192.168.4.60 VIP 192.168.4.2…...

    2024/4/14 11:21:34

最新文章

  1. Altera FPGA 配置flash读写

    目录 一、读写控制器的配置 二、生成flash的配置文件 三、关于三种配置文件的大小 四、其他 一、读写控制器的配置 Altera ASMI Parallel&#xff08;下文简称ASMI)这个IP就仅仅是个Flash读写控制器&#xff0c;可以自由的设计数据来源。 关于这个IP的使用&#xff0c;可以…...

    2024/4/28 13:49:09
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Weblogic任意文件上传漏洞(CVE-2018-2894)漏洞复现(基于vulhub)

    &#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…...

    2024/4/12 13:23:08
  4. 分享一个Python爬虫入门实例(有源码,学习使用)

    一、爬虫基础知识 Python爬虫是一种使用Python编程语言实现的自动化获取网页数据的技术。它广泛应用于数据采集、数据分析、网络监测等领域。以下是对Python爬虫的详细介绍: 架构和组成:下载器:负责根据指定的URL下载网页内容,常用的库有Requests和urllib。解析器:用于解…...

    2024/4/23 7:26:06
  5. N5171B是德科技N5171B信号发生器

    181/2461/8938产品概述&#xff1a; N5171B EXG 射频模拟信号发生器具有最佳的 EXG&#xff0c;旨在满足您对组件参数测试和接收器校准的信号需求。其出色的硬件性能可提供更快的吞吐量、更长的正常运行时间以及极好的准确性和可重复性。 Agilent / HP N5171B EXG 射频模拟信…...

    2024/4/27 10:27:08
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57