4.4 消息通知

场景:粉丝要求给博客加入邮件订阅功能,这样当发布新文章后订阅博客的用户就可以收到通知邮件了。那个粉丝还着重强调了一下:“这个功能对不习惯使用RSS的用户很重要,希望能够加上!”

小白心想:“是个好建议!不过话说回来,似乎他还没发现其实我的博客连RSS 功能都没有。” 邮件订阅功能太好实现了,无非是在博客首页放一个文本框供访客输入自己的邮箱地址,提交后博客会将该地址存入Redis的一个集合类型键中(使用集合类型是为了保证同一邮箱地址不会存储多个)。每当发布新文章时,就向收集到的邮箱地址发送通知邮件。

想的简单,可是做出来后小白却发现了一个问题:输入邮箱地址提交后,页面需要很久时间才能载入完。

原来小白为了确保用户没有输入他人的邮箱,在提交之后程序会向用户输入的邮箱发送一封包含确认链接的邮件,只有用户单击这个链接后对应的邮箱地址才会被程序记录。可是由于发送邮件需要连接到一个远程的邮件发送服务器,网络好的情况下也得花上2秒左右的时间,赶上网络不好10秒都必能发完。所以每次用户提交邮箱后页面都要等待程序发送完邮件才能加载出来,而加载出来的页面上显示的内容只是提示用户查看自己的邮箱单击确认链 接。“完全可以等页面加载出来后再发送邮件,这样用户就不需要等了。”小白喃喃道。

4.4.1 任务队列

当页面需要进行如发送邮件、复杂数据运算等耗时较长的操作时会阻塞页面的渲染。为了避免用户等待太久,应该使用独立的线程来完成这类操作。不过一些编程语言或框架不易实现多线程,这时很容易就会想到通过其他进程来实现。就小白的例子来说,设想有一个进程能够完成发邮件的功能,那么在页面中只需要想办法通知这个进程向指定的地址发送邮件就可以了。

通知的过程可以借助任务队列来实现。任务队列顾名思义,就是“传递任务的队列”。与任务队列进行交互的实体有两类,一类是生产者(producer),一类是消费者(consumer)。生产者会将需要处理的任务放入任务队列中,而消费者则不断地从任务队列中读入任务信息并执行。

对于发邮件这个操作来说页面程序就是生产者,而发邮件的进程就是消费者。当需要发送邮件时,页面程序会将收件地址、邮件主题和邮件正文组装成一个任务后存入任务队列中。同时发邮件的进程会不断检查任务队列,一旦发现有新的任务便会将其从队列中取出并执行。由此实现了进程间的通信。

使用任务队列有如下好处。

(1)松耦合。生产者和消费者无需知道彼此的实现细节,只需要约定好任务的描述格式。这使得生产者和消费者可以由不同的团队使用不同的编程语言编写。

(2)易于扩展消费者可以有多个,而且可以分布在不同的服务器中,如图4-1所示。借此可以轻易地降低单台服务器的负载。

            图4-1 可以有多个消费者分配任务队列中的任务

4.4.2 使用Redis实现任务队列

说到队列很自然就能想到Redis的列表类型,3.4.2节介绍了使用LPUSH和RPOP命令实现队列的概念。如果要实现任务队列,只需要让生产者将任务使用LPUSH命令加入到某个键中, 另一边让消费者不断地使用RPOP命令从该键中取出任务即可。

在例子中,完成发邮件的任务需要知道收件地址、邮件主题和邮件正文。所以生产者需要将这三个信息组成对象并序列化成字符串,然后将其加入到任务队列中。而消费者则循环从队列中拉取任务,就像如下伪代码:

#无限循环读取任务队列中的内容

loop

$task=RPOR queue

if $task

#如果任务队列中有任务则执行它

execute( $task)

else

#如果没有则等待1秒以免过于频繁地请求数据

wait 1 second

到此一个使用Redis实现的简单的任务队列就写好了。不过还有一点不完美的地方:当任务队列中没有任务时消费者每秒都会调用一次RPOP命令查看是否有新任务。如果可以实现一 旦有新任务加入任务队列就通知消费者就好了。其实借助 BRPOP 命令就可以实现这样的需 求。

BRPOP命令和RPOP命令相似,唯一的区别是当列表中没有元素时BRPOP命令会一直阻塞住连接,直到有新元素加入。如上段代码可改写为:

loop

#如果任务队列中没有新任务,BRPOP 命令会一直阻塞,不会执行execute()。

$task=BRPOP queue, 0

#返回值是一个数组(见下介绍),数组第二个元素是我们需要的任务。

execute( $task[1])

BRPOP命令接收两个参数,第一个是键名,第二个是超时时间,单位是秒。当超过了此时间仍然没有获得新元素的话就会返回nil。上例中超时时间为“0”,表示不限制等待的时间,即 如果没有新元素加入列表就会永远阻塞下去。

当获得一个元素后BRPOP命令返回两个值,分别是键名和元素值。为了测试BRPOP命令,我们可以打开两个redis-cli实例,在实例A中:

redis A>BRPOP queue 0

键入回车后实例1会处于阻塞状态,这时在实例B中向queue中加入一个元素:

redis B>LPUSH queue task

(integer) 1

在LPUSH命令执行后实例A马上就返回了结果:

1) "queue"

2) "task"

同时会发现queue中的元素已经被取走:

redis>LLEN queue

(integer) 0

除了BRPOP命令外,Redis还提供了BLPOP,和BRPOP的区别在与从队列取元素时BLPOP 会从队列左边取。具体可以参照LPOP理解,这里不再赘述。

4.4.3 优先级队列

前面说到了博客需要在发布文章的时候向每个订阅者发送邮件,这一步骤同样可以使用任务队列实现。由于要执行的任务和发送确认邮件一样,所以二者可以共用一个消费者。然而设想这样的情况:假设订阅博客的用户有1000人,那么当发布一篇新文章后博客就会向任务队列中添加1000个发送通知邮件的任务。如果每发一封邮件需要10秒,全部完成这 1000个任务就需要近3个小时。问题来了,假如这期间有新的用户想要订阅博客,当他提交完自己的邮箱并看到网页提示他查收确认邮件时,他并不知道向自己发送确认邮件的任务被加入到了已经有1000个任务的队列中。要收到确认邮件,他不得不等待近3个小时。多么糟糕的用户体验!而另一方面发布新文章后通知订阅用户的任务并不是很紧急,大多数用户并 不要求有新文章后马上就能收到通知邮件,甚至延迟一天的时间在很多情况下也是可以接受 的。

所以可以得出结论当发送确认邮件和发送通知邮件两种任务同时存在时,应该优先执行前者。为了实现这一目的,我们需要实现一个优先级队列。

BRPOP命令可以同时接收多个键,其完整的命令格式为BLPOP key [key …]timeout,如BLPOP queue:1 queue:2 0。意义是同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则会从该键中弹出元素。例如,打开两个redis-cli实例,在实例A中:

redis A>BLPOP queue:1 queue:2 queue:3 0

在实例B中:

redis B>LPUSH queue:2 task

(integer) 1

则实例A中会返回:

1) "queue:2"

2) "task"

如果多个键都有元素则按照从左到右的顺序取第一个键中的一个元素。我们先在queue:2和queue:3中各加入一个元素:

redis>LPUSH queue:2 task1

1) (integer) 1

redis>LPUSH queue:3 task2

2) (integer) 1

然后执行BRPOP命令:

redis>BRPOP queue:1 queue:2 queue:3 0

1) "queue:2"

2) "task1"

借此特性可以实现区分优先级的任务队列。我们分别使用queue:confirmation.email和queue:notification.email两个键存储发送确认邮件和发送通知邮件两种任务,然后将消费者的 代码改为:

loop

$task = BRPOP queue:confirmation.email,

queue:notification.email,

0

execute( $task[1])

这时一旦发送确认邮件的任务被加入到queue:confirmation.email队列中,无论queue: notification.email还有多少任务,消费者都会优先完成发送确认邮件的任务。

4.4.4 “发布/订阅”模式

除了实现任务队列外,Redis还提供了一组命令可以让开发者实现“发布/订阅”(publish/subscribe)模式。“发布/订阅”模式同样可以实现进程间的消息传递,其原理是这样 的: “发布/订阅”模式中包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或若干个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息。 发布者发布消息的命令是PUBLISH,用法是PUBLISH channel message,如向channel.1说一 声“hi”:

redis>PUBLISH channel.1 hi

(integer) 0

这样消息就发出去了。PUBLISH命令的返回值表示接收到这条消息的订阅者数量。因为此时没有客户端订阅channel.1,所以返回0。发出去的消息不会被持久化,也就是说当有客户 端订阅channel.1后只能收到后续发布到该频道的消息,之前发送的就收不到了。

订阅频道的命令是SUBSCRIBE,可以同时订阅多个频道,用法是SUBSCRIBE channel[channel …]。现在新开一个redis-cli实例A,用它来订阅channel.1:

redis A>SUBSCRIBE channel.1 Reading messages... (press Ctrl-C to quit)

1) "subscribe"

2) "channel.1"

3) (integer) 1

执行SUBSCRIBE命令后客户端会进入订阅状态,处于此状态下客户端不能使用除SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE这4个属于“发布/订阅”模式的命令之外的命令(后面3个命令会在下面介绍),否则会报错。

进入订阅状态后客户端可能收到三种类型的回复。每种类型的回复都包含3个值,第一个 值是消息的类型,根据消息类型的不同,第二、三个值的含义也不同。消息类型可能的取值有:

(1)Subscribe。表示订阅成功的反馈信息。第二个值是订阅成功的频道名称,第三个值是 当前客户端订阅的频道数量。

(2)message。这个类型的回复是我们最关心的,它表示接收到的消息。第二个值表示产生 消息的频道名称,第三个值是消息的内容。

(3)unsubscribe。表示成功取消订阅某个频道。第二个值是对应的频道名称,第三个值是当 前客户端订阅的频道数量,当此值为0时客户端会退出订阅状态,之后就可以执行其他非“发 布/订阅”模式的命令了。

上例中当实例A订阅了channel.1进入订阅状态后收到了一条subscribe类型的回复,这时我们打开另一个redis-cli实例B,并向channel.1发送一条消息:

redis B>PUBLISH channel.1 hi!

(integer) 1

返回值为1表示有一个客户端订阅了channel.1,此时实例A 收到了类型为message的回复:

1) "message"

2) "channel.1"

3) "hi!"

使用UNSUBSCRIBE命令可以取消订阅指定的频道,用法为UNSUBSCRIBE [channel[channel …]],如果不指定频道则会取消订阅所有频道① 。

注释:①由于redis-cli的限制我们无法在其中测试UNSUBSCRIBE命令。

4.4.5 按照规则订阅

除了可以使用SUBSCRIBE命令订阅指定名称的频道外,还可以使用PSUBSCRIBE命令订阅指定的规则。规则支持glob风格通配符格式(见3.1节),下面我们新打开一个redis-cli实例C 进行演示:

redis C>PSUBSCRIBE channel.?* Reading messages... (press Ctrl-C to quit)

1) "psubscribe"

2) "channel.?*"

3) (integer) 1

规则channel.?*可以匹配channel.1和channel.10,但不会匹配channel.。这时在实例B中发布消息:

redis B>PUBLISH channel.1 hi!

(integer) 2

返回结果为2是因为实例A和实例C两个客户端都订阅了channel.1频道。实例C接收到的回复是:

1) "pmessage"

2) "channel.?*"

3) "channel.1"

4) "hi!"

第一个值表示这条消息是通过PSUBSCRIBE命令订阅频道而收到的,第二个值表示订阅时使用的通配符,第三个值表示实际收到消息的频道命令,第四个值则是消息内容。

提示  使用PSUBSCRIBE命令可以重复订阅一个频道,如某客户端执行了 PSUBSCRIBE channel.? channel.?*,这时向channel.2发布消息后该客户端会收到两条消 息,而同时PUBLISH命令返回的值也是2而不是1。同样的,如果有另一个客户端执行了 SUBSCRIBE channel.10,和PSUBSCRIBE channel.?*的话,向channel.10发送命令该客户端 也会收到两条消息(但是是两种类型,message 和pmessage),同时PUBLISH命令会返回2。

PUNSUBSCRIBE命令可以退订指定的规则,用法是PUNSUBSCRIBE [pattern[pattern …]], 如果没有参数则会退订所有规则。

注意  使用PUNSUBSCRIBE命令只能退订通过PSUBSCRIBE命令订阅的规则,不会影响直接通过SUBSCRIBE命令订阅的频道;同样UNSUBSCRIBE命令也不会影响通过 PSUBSCRIBE命令订阅的规则。另外容易出错的一点是使用PUNSUBSCRIBE命令退订某 个规则时不会将其中的通配符展开,而是进行严格的字符串匹配,所以PUNSUBSCRIBE* 无法退订channel.*规则,而是必须使用PUNSUBSCRIBE channel.*才能退订。

4.5 管道

客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输,这两个部分的总耗时称为往返时延。根据网络性能 不同,往返时延也不同,大致来说到本地回环地址(loop backaddress)的往返时延在数量级上 相当于Redis处理一条简单命令(如LPUSH list 1 2 3)的时间。如果执行较多的命令,每个命令 的往返时延累加起来对性能还是有一定影响的。

在执行多个命令时每条命令都需要等待上一条命令执行完(即收到Redis的返回结果)才 能执行,即使命令不需要上一条命令的执行结果。如要获得post:1、post:2和post:3这3个键中的 title字段,需要执行三条命令,如图4-2所示。

           图4-2 不使用管道时的命令执行示意图(纵向表示时间)

Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往 返时延累计值的目的,如图4-3所示。

图4-3 使用管道时的命令执行示意图

4.6 节省空间

Jim Gray曾经说过:“内存是新的硬盘,硬盘是新的磁带。”内存的容量越来越大,价格也越来越便宜。2012年年底,亚马逊宣布即将发布一个拥有240GB内存的EC2实例,如果放到若 干年前来看,这个容量就算是对于硬盘来说也是很大的了。即便如此,相比于硬盘而言,内存 在今天仍然显得比较昂贵。而Redis是一个基于内存的数据库,所有的数据都存储在内存中,所以如何优化存储,减少内存空间占用对成本控制来说是一个非常重要的话题。

注释:Jim Gray是1998年的图灵奖得主,在数据库(尤其是事务)方面做出过卓越的贡献。其于2007年独自驾船在海上失踪。

4.6.1 精简键名和键值

精简键名和键值是最直观的减少内存占用的方式,如将键名very.important.person:20改成 VIP:20。当然精简键名一定要把握好尺度,不能单纯为了节约空间而使用不易理解的键名(比如将VIP:20修改为V:20,这样既不易维护,还容易造成命名沖突)。又比如一个存储用户性别 的字符串类型键的取值是male和female,我们可以将其修改成m和f来为每条记录节约几个字 节的空间(更好的方法是使用0和1来表示性别,稍后会详细介绍原因)① 。 注释:①3.2.4节还介绍过使用字符串类型的位操作来存储性别,更加节约空间。

4.6.2 内部编码优化

有时候仅凭精简键名和键值所减少的空间并不足以满足需求,这时就需要根据Redis内部编码规则来节省更多的空间。Redis为每种数据类型都提供了两种内部编码方式,以散列类型为例,散列类型是通过散列表实现的,这样就可以实现0(1)时间复杂度的查找、赋值操作,然 而当键中元素很少的时候,0(1)的操作并不会比0(n)有明显的性能提高,所以这种情况下Redis 会采用一种更为紧凑但性能稍差(获取元素的时间复杂度为0(n))的内部编码方式。内部编码 方式的选择对于开发者来说是透明的,Redis会根据实际情况自动调整。当键中元素变多时 Redis会自动将该键的内部编码方式转换成散列表。如果想查看一个键的内部编码方式可以使 用OBJECT ENCODING命令,例如:

redis>SET foo bar

OK

redis>OBJECT ENCODING foo

"raw"

Redis的每个键值都是使用一个redisObject结构体保存的,redisObject的定义如下:

typedef struct redisObject {

unsigned type:4;

unsigned notused:2; /* Not used */

unsigned encoding:4;

unsigned lru:22; /* lru time (relative to server.lruclock) */

int refcount;

void *ptr;

}robj;

其中type字段表示的是键值的数据类型,取值可以是如下内容:

#define REDIS_STRING 0

#define REDIS_LIST 1

#define REDIS_SET 2

#define REDIS_ZSET 3

#define REDIS_HASH 4

encoding字段表示的就是Redis键值的内部编码方式,取值可以是:

#define REDIS_ENCODING_RAW 0 /* Raw representation */

#define REDIS_ENCODING_INT 1 ed as integer */

#define REDIS_ENCODING_HT 2 /* Encoded as hash table */

#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */

#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */

#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */

#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */

#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */

各个数据类型可能采用的内部编码方式以及相应的OBJECT ENCODING命令执行结果如表4-2所示。

表4-2  每个数据类型都可能采用两种内部编码方式之一来存储

数据类型

内部编码方式

OBJECT ENCODING命令结果

字符串类型

REDIS_ENCODING_RAW

REDIS_ENCODING_INT

“raw”

“int”

散列类型

REDIS_ENCODING_HT

REDIS_ENCODING_ZIPLIST

“hashtable”

“ziplist”

列表类型

REDIS_ENCODING_LINKEDLIST

REDIS_ENCODING_ZIPLIST

“linkedlist”

“ziplist”

集合类型

REDIS_ENCODING_HT

REDIS_ENCODING_INTSET

“hashtable”

“intset”

有序集合类型

REDIS_ENCODING_SKIPLIST

REDIS_ENCODING_ZIPLIST

“skiplist”

“ziplist”

下面针对每种数据类型分别介绍其内部编码规则及优化方式。

1.字符串类型

Redis使用一个sdshdr类型的变量来存储字符串,而redisObject的ptr字段指向的是该变量 的地址。sdshdr的定义如下:

struct sdshdr {

int len;

int free;

char buf[];

};

其中len字段表示的是字符串的长度,free字段表示buf中的剩余空间,而buf字段存储的才 是字符串的内容。

所以当执行SET key foobar时,存储键值需要占用的空间是 sizeof(redisObject)+sizeof(sdshdr)+strlen("foobar")=30字节① ,如图4-4所示。 注释:①本节所说的字节数以64位Linux系统为前提。

图4-4 字符串键值“foobar”的存储结构

而当键值内容可以用一个64位有符号整数表示时,Redis会将键值转换成long类型来存储。如SET key 123456,实际占用的空间是sizeof(redisObject)=16字节,比存储"foobar"节省了一半的存储空间,如图4-5所示。

图4-5 字符串键值“123456”的内存结构

redisObject中的refcount字段存储的是该键值被引用数量,即一个键值可以被多个键引用。Redis启动后会预先建立10000个分别存储从0到9999这些数字的redisObject类型变量作为共享 对象,如果要设置的字符串键值在这10000个数字内(如SET key1 123)则可以直接引用共享对 象而不用再建立一个redisObject了,也就是说存储键值占用的空间是0字节,如图4-6所示。

图4-6 当执行了SET key1 123和SET key2 123后,key1和key2两个键都直接引用了一个已经建立好的共享对象,节省了存储空间

由此可见,使用字符串类型键存储对象ID这种小数字是非常节省存储空间的,Redis只需 存储键名和一个对共享对象的引用即可。

提示  当通过配置文件参数maxmemory设置了Redis可用的最大空间大小时,Redis不 会使用共享对象,因为对于每一个键值都需要使用一个redisObject来记录其LRU信息。

2.散列类型

散列类型的内部编码方式可能是REDIS_ENCODING_HT或 REDIS_ENCODING_ZIPLIST① 。在配置文件中可以定义使用REDIS_ENCODING_ZIPLIST方式 编码散列类型的时机:

注释:①在Redis 2.4及以前的版本中散列类型的键采用REDIS_ENCODING_HT或 REDIS_ENCODING_ZIPMAP的编码方式。

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

当散列类型键的字段个数少于hash-max-ziplist-entries参数值且每个字段名和字段值的长 度都小于hash-max-ziplist-value参数值(单位为字节)时,Redis就会使用REDIS_ ENCODING_ZIPLIST来存储该键,否则就会使用REDIS_ENCODING_HT。转换过程是透明的, 每当键值变更后Redis都会自动判断是否满足条件来完成转换。

REDIS_ENCODING_HT编码即散列表,可以实现O(1)时间复杂度的赋值取值等操作,其字段和字段值都是使用redisObject存储的,所以前面讲到的字符串类型键值的优化方法同样 适用于散列类型键的字段和字段值。

提示  Redis的键值对存储也是通过散列表实现的,与REDIS_ENCODING_HT编码方式类似,但键名并非使用redisObject存储,所以键名“123456”并不会比“abcdef”占用更少 的空间。之所以不对键名进行优化是因为绝大多数情况下键名都不会是纯数字。

补充知识  Redis支持多数据库,每个数据库中的数据都是通过结构体redisDb存储的。 redisDb的定义如下:

typedef struct redisDb {

dict *dict; /* The keyspace for this DB */

dict *expires; /* Timeout of keys with a timeout set */

dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */

dict *ready_keys; /* Blocked keys that received a PUSH */

dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */

int id;

} redisDb;

dict类型就是散列表结构,expires存储的是数据的过期时间。当Redis启动时会根据配 置文件中databases参数指定的数量创建若干个redisDb类型变量存储不同数据库中的数据。

REDIS_ENCODING_ZIPLIST编码类型是一种紧凑的编码格式,它牺牲了部分读取性能以 换取极高的空间利用率,适合在元素较少时使用。该编码类型同样还在列表类型和有序集合 类型中使用。REDIS_ENCODING_ZIPLIST编码结构如图4-7所示,其中zlbytes是uint32_t类型,表示整个结构占用的空间。zltail也是uint32_t类型,表示到最后一个元素的偏移,记录zltail使得 程序可以直接定位到尾部元素而无需遍历整个结构,执行从尾部弹出(对列表类型而言)等操 作时速度更快。zllen是uint16_t类型,存储的是元素的数量。zlend是一个单字节标识,标记结构 的末尾,值永远是255。

图4-7 REDIS_ENCODING_ZIPLIST编码的内存结构

在REDIS_ENCODING_ZIPLIST 中每个元素由4个部分组成。

第一个部分用来存储前一个元素的大小以实现倒序查找,当前一个元素的大小小于254字 节时第一个部分占用1个字节,否则会占用5个字节。

第二、三个部分分别是元素的编码类型和元素的大小,当元素的大小小于或等于63个字 节时,元素的编码类型是ZIP_STR_06B(即0<<6),同时第三个部分用6个二进制位来记录元 素的长度,所以第二、三个部分总占用空间是1字节。当元素的大小大于63且小于或等于16383 字节时,第二、三个部分总占用空间是2字节。当元素的大小大于16383字节时,第二、三个部 分总占用空间是5字节。

第四个部分是元素的实际内容,如果元素可以转换成数字的话Redis会使用相应的数字类 型来存储以节省空间,并用第二、三个部分来表示数字的类型(int16_t、int32_t等)。

使用REDIS_ENCODING_ZIPLIST编码存储散列类型时元素的排列方式是:元素1存储字 段1,元素2存储字段值1,依次类推,如图4-8所示。

图4-8 使用REDIS_ENCODING_ZIPLIST编码存储散列类型的内存结构

例如,当执行命令HSET hkey foo bar命令后,hkey键值的内存结构如图4-9所示。

图4-9 hkey键值的内存结构

下次需要执行HSET hkey foo anothervalue时Redis需要从头开始找到值为foo的元素(查找 时每次都会跳过一个元素以保证只查找字段名),找到后删除其下一个元素,并将新值 anothervalue插入。删除和插入都需要移动后面的内存数据,而且查找操作也需要遍历才能完 成,可想而知当散列键中数据多时性能将很低,所以不宜将hash-max-ziplist-entries和hash-maxziplist-value两个参数设置得很大。

3.列表类型

列表类型的内部编码方式可能是REDIS_ENCODING_LINKEDLIST或REDIS ENCODINGZIPLIST。同样在配置文件中可以定义使用REDIS_ENCODING_ZIPLIST方式编码 的时机:

list-max-ziplist-entries 512

list-max-ziplist-value 64

具体转换方式和散列类型一样,这里不再赘述。 REDIS_ENCODING_LINKEDLIST编码方式即双向链表,链表中的每个元素是用 redisObject存储的,所以此种编码方式下元素值的优化方法与字符串类型的键值相同。 而使用REDIS_ENCODING_ZIPLIST编码方式时具体的表现和散列类型一样,由于REDIS_ENCODING_ZIPLIST编码方式同样支持倒序访问,所以采用此种编码方式时获取两端 的数据依然较快。

4.集合类型

集合类型的内部编码方式可能是REDIS_ENCODING_HT或REDIS_ENCODING_INTSET。当集合中的所有元素都是整数且元素的个数小于配置文件中的set-max-intset-entries参数指定 值(默认是512)时Redis会使用REDIS_ENCODING_INTSET编码存储该集合,否则会使用 REDIS_ENCODING_HT来存储。

REDIS_ENCODING_INTSET编码存储结构体intset的定义是:

typedef struct intset {

uint32_t encoding;

uint32_t length;

int8_t contents[];

} intset;

其中contents存储的就是集合中的元素值,根据encoding的不同,每个元素占用的字节大小 不同。默认的encoding是INTSET_ENC_INT16(即2个字节),当新增加的整数元素无法使用2个 字节表示时,Redis会将该集合的encoding升级为INTSET_ENC_INT32(即4个字节)并调整之前 所有元素的位置和长度,同样集合的encoding还可升级为INTSET_ENC_INT64(即8个字节)。

REDIS_ENCODING_INTSET编码以有序的方式存储元素(所以使用SMEMBERS命令获得 的结果是有序的),使得可以使用二分算法查找元素。然而无论是添加还是删除元素,Redis都 需要调整后面元素的内存位置,所以当集合中的元素太多时性能较差。

当新增加的元素不是整数或集合中的元素数量超过了set-max-intset-entries参数指定值 时,Redis会自动将该集合的存储结构转换成REDIS_ENCODING_HT。

注意  当集合的存储结构转換成REDIS_ENCODING_HT后,即使将集合中的所有非 整数元素删除,Redis也不会自动将存储结构转換回REDIS_ENCODING_INTSET。因为如 果要支持自动回转,就意味着Redis在每次删除元素时都需要遍历集合中的键来判断是否 可以转換回原来的编码,这会使得删除元素变成了时间复杂度为0(n)的操作。

5.有序集合类型

有序集合类型的内部编码方式可能是REDIS_ENCODING_SKIPLIST或 REDIS_ENCODING_ZIPLIST。同样在配置文件中可以定义使用REDIS_ENCODING_ZIPLIST方 式编码的时机:

zset-max-ziplist-entries 128

zset-max-ziplist-value 64

具体规则和散列类型及列表类型一样,不再赘述。 当编码方式是REDIS_ENCODING_SKIPLIST时,Redis使用散列表和跳跃列表(skiplist)两 种数据结构来存储有序集合类型键值,其中散列表用来存储元素值与元素分数的映射关系以 实现0(1)时间复杂度的ZSCORE等命令。跳跃列表用来存储元素的分数及其到元素值的映射以 实现排序的功能。Redis对跳跃列表的实现进行了几点修改,其中包括允许跳跃列表中的元素 (即分数)相同,还有为跳跃链表每个节点增加了指向前一个元素的指针以实现倒序查找。

采用此种编码方式时,元素值是使用redisObject存储的,所以可以使用字符串类型键值的优化方式优化元素值,而元素的分数是使用double类型存储的。

使用REDIS_ENCODING_ZIPLIST编码时有序集合存储的方式按照“元素1的值,元素1的 分数,元素2的值,元素2的分数”的顺序排列,并且分数是有序的。

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

相关文章

  1. docker入门课程

    Docker 架构Ubuntu Docker 安装使用官方安装脚本自动安装手动安装Docker 镜像加速Docker Hello World运行交互式的容器启动容器(后台模式)停止容器Docker 容器使用获取镜像启动容器启动已停止运行的容器后台运行停止一个容器进入容器导出和导入容器删除容器Docker 镜像使用列…...

    2024/4/19 23:31:50
  2. mysql 数据库 1130 - Host ‘*‘ is not allowed to connect to this MySQL server

    在服务器上安装了mysql后,数据库安装好了本地用Navicat连不上:提示 1130 - Host * is not allowed to connect to this MySQL server 出现这个情况的原因大致为服务器上mysql没有允许远程连接,需要我们在服务器上通过命令行进行修改cmd + R 进入命令行mysql -u root - p接下…...

    2024/4/20 15:10:43
  3. C++万能头文件<bits/stdc++.h>文件

    文件内容如下 // C++ includes used for precompiling -*- C++ -*-// Copyright (C) 2003-2013 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under th…...

    2024/4/21 7:58:48
  4. 【博主个人在uni-app里使用的基本标签样式】

    博主个人在uni-app里使用的基本标签样式这些样式我都是放在pages的新建目录common,新建.css文件命名为base.css文件里的, 引用是在app.vue里写上 @import “./pages/common/base.css”; 就可以在每个页面用了/* 7.30遮幕 按钮*/.zhemu{position: fixed;top: 0;left: 0;widt…...

    2024/4/21 3:35:53
  5. Hbase都包含那几部分

    Rowkey键 列族, 值 时间戳。版本 按照Rowkey字典(ASCII顺序存储),基于Rowkey的高校检索,同时继承HDFS的吞吐能力...

    2024/5/8 11:41:44
  6. 数据增强之Gridmask

    paper: https://arxiv.org/abs/2001.04086 code: https://github.com/Jia-Research-Lab/GridMask 概述 作者首先回顾了数据增强(Data augmentation)方法,指出当前方法有三类:spatial transformation, color distortion, 以及 information dropping。本文提出的方法属于 inf…...

    2024/4/21 10:22:42
  7. 1200元的学费学来的防骗小妙招

    1200元的学费学来的防骗小妙招 这是一花了1200块学来的经验, 不得不说闲鱼上面的PZ很厉害.简单说下我的购买经历吧: 图便宜,在闲鱼买二手耳机,air pods pro (苹果耳机第三代)官方价1999,我话1200买的,卖家说是过生日闺蜜送的,全新未拆封,带发票,我这个理由确实很吸引人,对,吸引…...

    2024/4/21 17:29:59
  8. PCIE插槽数据传输理论值

    ...

    2024/5/4 12:52:39
  9. Java8新特性JDK8之summarizing集合统计

    Java新特性玩转JDK8之summarizing集合统计summarizing 资料summarizing 是用来做统计相关的东西 分类(统计类型不同,使用方式相同)summarizingInt //统计int类型 summarizingLong //统计Long类型 summarizingDouble //统计Double类型summa…...

    2024/4/22 1:36:53
  10. python希尔排序,从小到大

    希尔排序的原理是按照一定的间距 d 将数组中的元素分组,进行分别插入排序,再逐步缩小 d 的值,反复进行插入排序,最后按照正确顺序输出。 假设一个数组为 array=[2,5,3,9,1,4,7,0],共8个元素 采用二分法确定每次排序的间距 d ,则 d =4,2,1 当 d=4 时,原数组分为(2,1)(…...

    2024/4/21 20:58:31
  11. 开放、普惠、高性能-SLS时序存储助力打造企业级全方位监控方案

    简介: SLS新增时序存储,面向时序类数据提供一站式接入、存储、可视化、告警、智能运维等功能。方案完整支持各主流开源监控平台,提供低成本、免运维的监控数据存储与服务能力。 无所不在的时序数据 时间带走一切,长年累月会把你的名字、外貌、性格、命运都改变。 —柏拉图 …...

    2024/4/27 17:37:51
  12. js jquery 获取元素(父节点,子节点,兄弟节点),元素筛选

    一、js 获取元素(父节点,子节点,兄弟节点)var test = document.getElementById("test"); var parent = test.parentNode; // 父节点 var chils = test.childNodes; // 全部子节点 var first = test.firstChild; // 第一个子节点 var last = test.lastChile; // 最后一…...

    2024/4/21 7:26:45
  13. leetcode103. 二叉树的锯齿形层次遍历(bfs)

    给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。例如: 给定二叉树 [3,9,20,null,null,15,7],3/ \9 20/ \15 7 返回锯齿形层次遍历如下:[[3],[20,9],[15,7] ]代码 /*** Definition for a bin…...

    2024/5/3 2:28:03
  14. adb命令查看data目录下的文件并拷贝到电脑

    查看sqlite的数据库文件 adb shell su cd /data/data/wintec.pos.apos/databases 进入到databases目录 ls -al 查看databases下的文件 发现有database database-journal 使用DOS命令即可将database文件拷贝到电脑上,使用navicat for sql查看 新打开一个cmd黑窗体 adb…...

    2024/4/24 7:11:22
  15. Git学习 - Git修改commit的内容(包括历史版本)

    Git学习 - Git修改commit的内容 1、引言: 场景一: 有时候,我们提交了一个 commit 后,发现有些部分遗漏掉了,尤其是有的内容忘记添加到暂存区里了,导致这些内容没有被 commit ,但是如果把这些遗漏掉的内容作为一个新的 commit 再次提交的话就会显得很low 而且会有两个 co…...

    2024/5/1 1:39:20
  16. PyQt5基本控件之菜单栏QMenuBar

    QMenuBar 基本方法方法 效果menuBar() 返回主窗口的QMenuBar对象addMenu() 在菜单栏中添加一个新的QMenu对象addAction() 向QMenu小控件中添加一个操作按钮,其中包含文本或图标setEnabled() 将操作按钮设置为启用/禁用addSeperator() 在菜单中添加一条分割线clear() 删除菜单栏…...

    2024/4/25 8:08:49
  17. 自动生成代码工具--APT

    APT(Annotation Processing Tool)注解处理器,是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。它可以生成Java代码,同时这些代码会跟手工白那些的Java的代码一样被Javac编译。虚处理器(javax.annotation.processing.AbstractProcessor)每个处理器都继承自A…...

    2024/4/21 7:38:42
  18. 自己需要学习的技术和工具

    一、开发上的技术 深挖源码,分析底层原理 1. spring security和spring boot(spring cloud)的整合 2. spring cloud 的相关组件 2.1 consule的使用 2.1 gateway的使用 2.2 spring cloud config的使用 3. 消息中间件的使用 3.1 rabbitMq的使用 80% 3.2 rocketMq的使用 二、工具…...

    2024/5/1 4:04:00
  19. JMeter Cannot create PoolableConnectionFactory (ORA-00923: FROM keyword not found where expected )错误

    错误:jmeter JDBC Connection Configuration 连接数据库报错解决办法:Validation Query选择 【select 1 from dual】...

    2024/5/3 17:19:15
  20. 淘宝运营 淘宝补流量的作用 如何安全补单

    大家在做淘宝运营时看的指标就是数据,而这些数据就代表了一个店铺的好坏、店铺流量的高低,为了让数据好看,让店铺获得的流量更多,为了让数据好看,商家就会 从很多途径来获取流量,而最多的方法就是补流量,补流量的作用是什么呢?怎样才能安全的补流量呢?这就是我们今天要…...

    2024/4/21 15:08:03

最新文章

  1. Redis-五大数据类型-Hash(哈希)

    五大数据类型-Hash(哈希) 简介 Hash是一个键值对的集合。 Hash 是一个 String 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。 Hash 是 Redis 中出现最为频繁的复合…...

    2024/5/8 13:32:03
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/5/7 10:36:02
  3. ROS2高效学习第十章 -- ros2 高级组件之大型项目中的 launch 其二

    ros2 高级组件之大型项目中的 launch 1 前言和资料2 正文2.1 启动 turtlesim,生成一个 turtle ,设置背景色2.2 使用 event handler 重写上节的样例2.3 turtle_tf_mimic_rviz_launch 样例 3 总结 1 前言和资料 早在ROS2高效学习第四章 – ros2 topic 编程…...

    2024/5/7 4:57:36
  4. 三防笔记本丨工业笔记本电脑丨车辆检修的应用以及优势

    伴随着汽车技术的不断更新迭代以及车辆复杂性的增加,现代车辆检修工作需要更高效、更精确的方法来确保车辆的安全和性能。在这过程中,工业笔记本电脑作为一种强大的工具,为车辆检修提供了诊断、记录、分析和解决问题的核心功能 故障诊断与维修…...

    2024/5/8 2:06:31
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/7 19:05:20
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/7 22:31:36
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...

    2024/5/8 1:37:40
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...

    2024/5/7 14:19:30
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...

    2024/5/8 1:37:39
  10. 【Objective-C】Objective-C汇总

    方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/7 16:57:02
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    👨‍💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...

    2024/5/7 14:58:59
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/7 1:54:46
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...

    2024/5/7 21:15:55
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/8 1:37:35
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/7 16:05:05
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/7 16:04:58
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/8 1:37:32
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...

    2024/5/7 16:05:05
  19. --max-old-space-size=8192报错

    vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...

    2024/5/8 1:37:31
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...

    2024/5/8 1:37:31
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...

    2024/5/8 12:44:41
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...

    2024/5/8 9:51:44
  23. python django 小程序图书借阅源码

    开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图&#xff0…...

    2024/5/8 1:37:29
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/7 17:09:45
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

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

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

    %读入6幅图像(每一幅图像的大小是564*564) 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
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:16:58
  44. 如何在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