Redis的高可用哨兵Sentinel

  • 什么是Sentinel
  • 如何启动Sentinel服务
  • Sentinel状态与实例结构
  • 工作流程
    • 主观下线
    • 客观下线
    • 选举领头Sentinel
    • 故障转移

什么是Sentinel

在我的上一篇博客Redis持久化与主从复制中,给大家介绍了Redis的主从模式,实现了读写分离、数据备份,但如果在服务运行过程中某一台服务器发生故障宕机了,就会引发问题。如果宕机的是从服务器,那么读性能就会下降;如果宕机的是主服务器,那么用户将无法向Redis中写入数据。

针对上述问题,最理想的解决方案是:在主服务器宕机后,提拔某一个从服务器为新的主服务器,再将所有其他从服务器连到这个新的主服务器下,当原本的主服务器重新上线后,挂到这个新的主服务器下,成为从服务器。

我们先来设想一下人为的、手动的实现上面的解决方案:
①主服务器宕机了,赶紧拎一个从服务器执行slaveof no one,提升为主服务器;
②一个一个的执行slaveof 新的主服务器,把其他从服务器挂到新的主服务器下;
③原来的主服务器重新上线了,他这时还是一个主服务器,只不过没有任何从服务器与他连接,再次执行slaveof 新的主服务器,完成故障转移。

不难看出,人工的去进行故障转移的效率是相当低下的,更要命的是没有人可以预料到服务器什么时候会宕机,公司还得多雇一个人天天盯着,当然肯定不会有公司真的这样去做,因为Sentinel机制就为我们自动做了这份工作,并且更加高效可靠。

Sentinel,中文译为“哨兵”,是一个特殊的Redis服务器。它在运行过程中不断地监视一个或多个主服务器,以及这些主服务器下属的从服务器,确认它们的运行状态,一旦主服务器挂了,Sentinel是可以感知到的,并向其他所有监视这个主服务器的Sentinel进行确认,接下来会选举出一个领头Sentinel,由这个领头Sentinel负责上述的故障转移操作。

如何启动Sentinel服务

第一步: 进入你的redis目录,执行cp sentinel.conf xxx.conf将默认的sentinel配置文件复制几份,我这里复制了三份。

第二步: 编辑配置文件,介绍几个配置选项,在下文会有更为详细的的解释:

  1. port < sentinel-port >:此sentinel的端口号,我们说过sentinel也是一个redis服务;
  2. sentinel monitor < master-name > < ip > < redis-port > < quorum >:监视的主服务器的名称、ip、端口号和判定为客观下线所需要的投票数;
  3. sentinel auth-pass < master-name > < password >:如果你的主服务器设置了密码,要在这里配置密码进行验证;
  4. sentinel down-after-milliseconds < master-name > < milliseconds >:判定为主观下线所需的毫秒数;
  5. sentinel failover-timeout < master-name > < milliseconds >:刷新故障迁移状态的最大时限;
  6. sentinel parallel-syncs < master-name > < numslaves >:执行故障转移操作时,可同时对新的主服务器进行同步的从服务器数量。

第三步: 进入src目录,先将主从redis启动,关于主从redis的配置和启动也可以见我的上篇博客Redis持久化与主从复制 φ(>ω<*) 。

[root@localhost src]# ./redis-server ../redis6380.conf
[root@localhost src]# ./redis-server ../redis6382.conf
[root@localhost src]# ./redis-server ../redis6384.conf
[root@localhost src]# ps -ef|grep redis
root      2131     1  0 20:58 ?        00:00:00 ./redis-server *:6380
root      2136     1  0 20:58 ?        00:00:00 ./redis-server *:6382
root      2142     1  0 20:58 ?        00:00:00 ./redis-server *:6384
root      2148  2025  0 20:58 pts/0    00:00:00 grep --color=auto redis

可以看到现在我的主服务器是6380:

127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6382,state=online,offset=1050,lag=0
slave1:ip=127.0.0.1,port=6384,state=online,offset=1050,lag=0
master_replid:b2ee2431571ac88bba0069cb05ef1bb49e89aeaa
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1050
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1050

第三步: 新开三个窗口,执行./redis-sentinel ../你的配置文件名.conf,开启Sentinel,看到redis图标就表示开启成功了,这里有个小彩蛋,在三个哨兵都启动完成后每个窗口的底端都会显示如下信息:

2261:X 21 Mar 21:17:45.660 # +monitor master mymaster 127.0.0.1 6380 quorum 2
2261:X 21 Mar 21:17:45.661 * +slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:17:45.662 * +slave slave 127.0.0.1:6384 127.0.0.1 6384 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:17:46.463 * +sentinel sentinel 002b53ddd997b745c09435e4917a431ef55c06e2 127.0.0.1 26380 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:18:16.902 * +sentinel sentinel dfc3007ad02bea8e63da9d356ef597489505d427 127.0.0.1 26384 @ mymaster 127.0.0.1 6380

它们的含义相信看到后面你就会明白了。

至此已经Sentinel已经成功启动了,接下来我们来测试一下他的功能:
进入主服务器(6380)的命令行客户端,执行SHUTDOWN命令,模拟宕机,根据你的配置选项,过一段时间后,你会发现三个Sentinel又发来新消息了,如下,这是一个领头Sentinel:

2261:X 21 Mar 21:28:09.747 # +sdown master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:09.814 # +odown master mymaster 127.0.0.1 6380 #quorum 2/2
2261:X 21 Mar 21:28:09.814 # +new-epoch 1
2261:X 21 Mar 21:28:09.814 # +try-failover master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:09.825 # +vote-for-leader 81f9400e026a178a459aa46d5de90833d9da4917 1
2261:X 21 Mar 21:28:09.837 # 002b53ddd997b745c09435e4917a431ef55c06e2 voted for 81f9400e026a178a459aa46d5de90833d9da4917 1
2261:X 21 Mar 21:28:09.839 # dfc3007ad02bea8e63da9d356ef597489505d427 voted for 81f9400e026a178a459aa46d5de90833d9da4917 1
2261:X 21 Mar 21:28:09.877 # +elected-leader master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:09.878 # +failover-state-select-slave master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:09.944 # +selected-slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:09.944 * +failover-state-send-slaveof-noone slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:10.010 * +failover-state-wait-promotion slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:10.730 # +promoted-slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:10.730 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:10.796 * +slave-reconf-sent slave 127.0.0.1:6384 127.0.0.1 6384 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:10.904 # -odown master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:11.767 * +slave-reconf-inprog slave 127.0.0.1:6384 127.0.0.1 6384 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:11.767 * +slave-reconf-done slave 127.0.0.1:6384 127.0.0.1 6384 @ mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:11.826 # +failover-end master mymaster 127.0.0.1 6380
2261:X 21 Mar 21:28:11.826 # +switch-master mymaster 127.0.0.1 6380 127.0.0.1 6382
2261:X 21 Mar 21:28:11.826 * +slave slave 127.0.0.1:6384 127.0.0.1 6384 @ mymaster 127.0.0.1 6382
2261:X 21 Mar 21:28:11.826 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382
2261:X 21 Mar 21:28:14.897 # +sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382

粗略的看一下,大概意思为:

  1. 第1行表示,这个Sentinel判定主服务器主观下线;
  2. 第2行表示,这个Sentinel判定主服务器客观下线(投票数2/2);
  3. 第3到8行表示,选举领头Sentinel的过程,其他两个都给此sentinel投票了,此sentinel成为了领头sentinel;
  4. 第9到13行表示,6382端口的从服务器被选中并提升为主服务器;
  5. 第14行到18行表示,将6380原本的从服务器挂到6382下;
  6. 第18行往后表示,将该sentinel监视的主服务器转为6382,重新获取从服务器信息

重新启动redis6380服务器,执行info replication,发现已经变成redis6382的从服务器了:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6382
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1616335409
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2a65826247f478cba9b4bd43642247c142d5516d
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

至此,大家已经知道怎么样配置和启动Sentinel了,接下来的内容将会为你揭开Sentinel神秘的面纱。

Sentinel状态与实例结构

初始化状态
每一个Sentinel在启动的时候都会初始化一个sentinelState结构(简称“Sentinel状态”),这个结构保存了所有与当前Sentinel相关的信息,这里只考虑与本文内容相关的,如下图。其中,current_epoch表示当前纪元,其实就是个计数器,用于选举领头Sentinel;masters表示这个Sentinel监视的主服务器,是一个字典类型,key为主服务器的名称(由开发人员在配置文件中指定),值为sentinelRedisInstance(实例结构)。

实例结构
实例结构(sentinelRedisInstance)可以代表一个被Sentinel监视的主服务器、从服务器或是监视同一主服务器的其他Sentinel,总是作为某个字典类型属性的value值。
主服务器实例结构
主服务器的实例结构是Sentinel状态中masters属性的value值,其包含的属性非常多,部分如下:

  • flags:记录当前实例类型和实例状态
  • name:主服务器的名字,也就是配置文件中sentinel monitor <master-name> <ip> <redis-port> <quorum>选项的< master-name >值
  • runid:实例的运行id
  • config_epoch:配置纪元,实现故障转移
  • addr:实例的地址,是一个指针,指向sentinelAddr结构
  • down_after_period:实例多少毫秒无响应才会被判定为主观下线,也就是配置文件中sentinel down-after-milliseconds <master-name> <milliseconds>的值
  • quorum:判断实例为客观下线所需要的投票数,也就是配置文件中sentinel monitor <master-name> <ip> <redis-port> <quorum>选项的< quorum >值
  • parallel_syncs:执行故障转移操作时,可同时对新的主服务器进行同步的从服务器数量,对应配置文件中sentinel parallel-syncs <master-name> <numslaves>选项
  • failover_timeout:刷新故障迁移状态的最大时限,对应配置文件中sentinel failover-timeout <master-name> <milliseconds>选项

至此,Sentinel状态结构可以如下表示:

从服务器的实例结构
当你的sentinel启动后,会根据配置文件的信息向目标主服务器简历两条网络连接,一条是命令连接,用于向主服务器发送命令、接收回复;一条是订阅链接,订阅__sentinel__:hello频道。

假如你对redis的发布/订阅模式不太了解的话,在这里我简单的概括一下:

如上图,订阅者通过SUBSCRIBE [channel ...]订阅一个或多个频道,发布者通过PUBLISH channel message向频道发布消息,所有订阅这个频道的订阅者都会收到消息,上图中发布者执行PUBLISH channel1 "hello"命令后,订阅者1和订阅者2都会收到消息。在Sentinel机制中,Sentinel既可以通过命令连接向__sentinel__:hello频道发布消息,也可以通过订阅连接收到消息。

当Sentinel与目标主服务器建立了连接后,默认将会以10秒一次的频率向被监视的主服务器发送INFO命令,并通过INFO命令的回复来获取有关信息,不熟悉INFO命令的小伙伴可以在自己的命令行客户端上敲一下,观察一下会返回什么样的信息。Sentinel收到回复后会做两件事,一是更新自己正在监视的这台主服务器的对应的实例结构,如runid……;二是创建这台主服务器下属的从服务器的实例结构(如果之前没有创建过的话),再将这个实例结构与主服务器实例结构中的slaves属性关联起来,也称“发现从服务器”。

至此,再一步更新Sentinel状态图,如下:

注意

  1. slaves也是一个字典类型,它的键是从服务器的ip+端口,值是从服务器的实例结构
  2. 开发人员不需要给从服务器起名,由于从服务器是Sentinel自动发现的,所以Sentinel会自动以ip+端口作为从服务器的名称
  3. 实例结构中的flags属性指明了服务的类型

在发现了从服务器后,Sentinel也会想从服务器建立命令连接和订阅连接,并以默认10秒一次的频率向从服务器发送INFO命令,根据返回的从服务器信息更新自己的从服务器实例结构。
Sentinel哨兵的实例结构
默认情况下,Sentinel会以2秒一次的频率通过命令连接向__sentinel__:hello频道发布消息,消息包含两部分信息:一部分是关于Sentinel自身的信息,如Sentinel的IP地址、端口、运行id和当前配置纪元;另一部分信息是关于当前监视的主服务器的,包括主服务器的名字、IP地址、端口号和配置纪元。

由于所有监视同一个服务器的Sentinel都订阅了同一个频道,所以当某一个Sentinel向频道发送消息时,监视同一服务器的所有Sentinel都会收到消息,包括发布消息的这个Sentinel自己。

当一个Sentinel收到这条消息后,它首先检查的是消息中的Sentinel运行ID,并与自己的运行ID比对,如果一致则表示这是自己发布的消息,接下来它会丢弃这条消息,不做任何处理;如果不一致则表示这是监视同一台服务器的其他Sentinel,那么它将提取消息中的主服务器信息,更新这台主服务器实例结构中的sentinels属性,sentinels属性也是一个字典类型,key为Sentinel的ip+端口,value为Sentinel的实例结构,所有需要的信息从消息中关于Sentinel的那一部分提取。


与从服务器的实例结构类似,都是自动发现得到的,发现其他Sentinel后Sentinel之间会相互创建一条命令连接,至此我们终于画完了Sentinel状态图。

工作流程

主观下线

Sentinel会以默认两秒一次的频率通过命令连接向所有与它相连的实例(主服务器、从服务器、其他Sentinel)发送PING命令,我们之前在配置文件中设置了判定一个实例主观下线所需要的毫秒数,假如超过了这个时限Sentinel还没有收到来自这个实例对于PING命令的任何有效回复(PONG、LOADING、MASTERDOWN)的话,那么将会判定此实例主观下线,并更新实例结构中的flags属性,打开SRI_S_DOWN标识。

但是,看问题不能太主观了,Sentinel也是一样,接下来它会向其他Sentinel们确认该实例的状态。
注意: 每个Sentinel设置的主观下线时限可能不同,彼此独立判断,互不干扰。

客观下线

当一个Sentinel认为某个主服务器为主观下线后,将会向其他Sentinel发送SENTINEL is-master-down-by-adrr命令来确认这个主服务器是不是真的下线了。这条命令携带四个参数:

  • ip:主观下线的主服务器IP地址
  • port:主观下线的主服务器的端口号
  • current_epoch:此Sentinel当前配置纪元,用于选举领头Sentinel
  • runid:可以是* 或该Sentinel的运行id,当是 * 时表示此次发送命令只用于检测主服务器下线状态,而Sentinel运行ID则用于选举领头Sentinel

当一个Sentinel接收到来自另一个Sentinel的SENTINEL is-master-down-by-adrr命令后,会根据命令参数中的主服务器ip和端口检查该服务器是否被自己判定为主观下线,返回一条包含三个参数的Muilt Bulk回复:

  1. down_state:此Sentinel对主服务器的检查结果,1代表已下线,0代表未下线
  2. leader_runid:此Sentinel的局部领头Sentinel的运行id,用于选举领头Sentinel;也可以是 * ,表示此回复只用于检查下线状态
  3. leader_epoch:此Sentinel的局部领头Sentinel的配置纪元,用于选举领头Sentinel;当leader_runid参数是 * 时,此参数值一定是0

当Sentinel收到的回复中down_state参数为1的回复大于等于自己配置文件中quorum参数的值(即判定此主服务器为客观下线所需的最小投票数)时,这个Sentinel会将对应的主服务器实例结构中的flags属性更新,打开SRI_O_DOWN标识,标识自己判定这个主服务器客观下线。

选举领头Sentinel

当一个Sentinel判定主服务器进入客观下线状态后,就会张罗其他Sentinel选举出一个领头Sentinel进行故障转移,只有当某个Sentinel收到了总选民人数(Sentinel数量)一半以上的选票时,领头Sentinel才会诞生。

选举规则:

  • 每一次进行选举领头Sentinel后,无论是否成功,所有Sentinel的配置纪元的值都会加1
  • 每一个判定主服务器客观下线的Sentinel都会要求其他Sentinel将自己设置为它的局部领头Sentinel,通过发送SENTINEL is-master-down-by-adrr命令来完成这个操作,此时runid的值不再是 * ,而是自己的运行id
  • 局部领头Sentinel就相当于一张选票,采用先到先得的方式,即Sentinel会将最先向自己发送带有投票请求的SENTINEL is-master-down-by-adrr命令的Sentinel设置为自己的领头Sentinel,领头Sentinel一旦被设置,在当前纪元内不可更改
  • 参选的Sentinel在收到SENTINEL is-master-down-by-adrr命令的回复后,检查回复中的leader_epochleader_runid参数,并与自己的配置纪元和运行ID对比,在都吻合的情况下该Sentinel便可得知自己收到了一张选票
  • 当一个Sentinel半数以上选民的支持后,选举结束,领头Sentinel诞生

故障转移

我们已经选举出了领头Sentinel,这个领头Sentinel要做三件事,完成故障转移:

  1. 挑选出新的主服务器:
    将从服务器提升为主服务器很简单,就像开头提到的“人工Sentinel”一样,发送SLAVEOF no one命令即可,关键是怎样挑选新的主服务器,筛选主服务器的依据如下:
    ①该从服务器是正常工作的
    ②该从服务器近5秒内回复过领头Sentinel的INFO命令,即近期成功通信过
    ③与原主服务器的连接断开时长不得超过down_after_period选项设置的毫秒数的10倍,即保证该从服务器的数据是比较新的
    ④在上述条件都满足的前提下,根据服务器的优先级选择新的主服务器
  2. 修改从服务器复制目标:
    领头Sentinel通过与从服务器相连的命令连接向所有从服务器发送SLAVEOF <newMasterIp>:<newMasterPort>命令,将原本的其他从服务器挂到新的主服务器下
  3. 将旧的主服务器设为从服务器:
    原主服务器下线后,领头Sentinel仍会监控它的状态,待它上线后,同样发送SLAVEOF <newMasterIp>:<newMasterPort>命令,使其成为新主服务器的从服务器

以上就是本篇博客的全部内容,给坚持看到这里的你点个赞,欢迎交流指正。

Redis高可用哨兵Sentinel相关推荐

  1. 3台服务器Redis高可用哨兵模式

    3台服务器Redis高可用哨兵模式 @(学习)[redis, 高可用] 3台服务器Redis高可用哨兵模式 介绍 redis程序安装 哨兵模式配置 1 主redis配置 2 从redis配置 3 启动 ...

  2. mysql哨兵模式_3台服务器Redis高可用哨兵模式实现

    1. 介绍 Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务: 监控(Monitoring): Sentinel 会不断地检查你的主 ...

  3. 【Redis】Redis高可用之Sentinel哨兵模式详解(Redis专栏启动)

  4. Redis高可用方案:sentinel(哨兵模式)和集群

    一. redis高可用方案–sentinel(哨兵模式) 当我们搭建好redis主从复制方案后会发现一个问题,那就是当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力, ...

  5. Redis高可用解决方案:sentinel(哨兵模式)和集群

    一. redis高可用方案–sentinel(哨兵模式) 当我们搭建好redis主从复制方案后会发现一个问题,那就是当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力, ...

  6. python redis 哨兵_Redis高可用哨兵机制及SpringBoot整合哨兵

    前言:在前面讲到了Redis分片机制可实现内存数据的扩容来提高执行速率---Redis分片机制,可是Redis分片依旧有一些问题,如果redis分片的节点如果有一个服务器宕机,则直接影响用户的使用.R ...

  7. redis 系列25 哨兵Sentinel (高可用演示 下)

    原文:redis 系列25 哨兵Sentinel (高可用演示 下) 一. Sentinel 高可用环境准备 1.1 Sentinel 集群环境 环境 说明 操作系统版本 CentOS  7.4.17 ...

  8. 采用 redis主从 + 哨兵(sentinel) + vip漂移搭建一套redis高可用集群

    一.单个实例 当系统中只有一台redis运行时,一旦该redis挂了,会导致整个系统无法运行. 单个实例 二.备份 由于单台redis出现单点故障,就会导致整个系统不可用,所以想到的办法自然就是备份( ...

  9. Linux的企业-Redis数据库、缓存和哨兵Sentinal、Redis高可用

    一.Redis简介 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件.     它支持多种类型的数据结构,如        字符串(strings) ...

最新文章

  1. vue 如何判断两个数组相同_如何判断车头与障碍物的距离,教你两个办法,轻松靠墙10公分...
  2. 线上飙升800%,load达到12的解决过程
  3. Vue学习笔记(4)(Vue-router)
  4. 谷歌android wear智能腕表 价格,谷歌Android Wear 2.0更新推送:仅三款智能手表可享受...
  5. python画散点图带直线和图例_带图例的Matplotlib散点图
  6. Redux Toolkit 使用指南
  7. 阿里云镜像加速Docker
  8. htc 常见错误和解决方案
  9. jar命令成功完成 java -jar 命令却提示“没有主清单属性”!
  10. 理解“正反馈”和“负反馈”,学会系统性思考
  11. 修改gitlab 去掉双重认证 Two-Factor Authentication
  12. Tap4fun杨祥吉:手游大佬最怕员工知道的那些事儿
  13. 光线:提高照片的艺术感
  14. GCPC 2013_A Boggle DFS+字典树 CSU 1457
  15. GD32开发资料汇总下载软件硬件工具
  16. CAD绘图设计中怎样删除CAD图层?怎样清理CAD图层文件?
  17. NLP判断语言情绪_健玲:NLP的基本精神12条前提假设,打破你的思维墙
  18. win10系统vvv连接不上,提示:“在连接完成前,连接被远程计算机终止”的解决办法
  19. 三、12.13.14.15.编写三角形类Triangle
  20. 风华秋实再度递表港交所:非控股权益亏损扩大,涉三七互娱等

热门文章

  1. 量子通信利用量子力学原理产生密钥对信息进行加密和解密,并采用量子纠缠效应进行密钥分发,被认为是当今最安全的通信系统.有两项特性,一个是不可分割,一个是不可复制...
  2. 电脑连接linux系统怎么样,如今连Linux都弄不懂-当时我如果那么学习培训电脑操作系统就好啦...
  3. 组合学:使用10个数字与52个字母生成1477万个不重复的4位串码V4衍生版本
  4. 块存储、文件存储与对象存储的区别与应用场景
  5. 三星80亿美元收购Harmon真正意图是它?
  6. 关于ITIL证书更新的重要通知
  7. 2022年度学习总结
  8. Php 网站性能优化,关于ThinkPHP中网站性能优化研究
  9. 【NOIP模拟】我的天
  10. 【网单服务端】神鬼世界一键安装服务端双镜像优化版网游单机