IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis高可用(二)- 持久化 -> 正文阅读

[大数据]Redis高可用(二)- 持久化

持久化:解决单机备份问题。Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,所以定期将Redis中的数据或写命令从内存保存到硬盘,重启时利用持久化文件实现数据恢复

Redis持久化方式有两种:

  • RDB持久化:保存当前内存数据到硬盘;
  • AOF持久化:保存执行的每条写命令到硬盘(类似于MySQL的binlog);AOF持久化的实时性高,当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式

一、RDB持久化(Redis DataBase)

RDB持久化是将当前进程中的全量数据生成快照保存到硬盘,也称作快照持久化,保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。

1、触发条件

RDB持久化的触发分为两种:手动触发自动触发

手动触发:

1.save命令:阻塞Redis服务器主进程,服务器不能处理任何命令请求,直到RDB文件创建完。

2.bgsave命令:会创建一个子进程,由子进程负责创建RDB文件,父进程(即Redis主进程)继续处理请求。只有创建子进程时会阻塞服务器,后面过程不阻塞;自动触发RDB持久化时,Redis也是使用bgsave。

命令

save

bgsave

IO类型

同步

异步

阻塞

整个流程阻塞

只有在fork子进程时阻塞

复杂度

O(n)

O(n)

优点

不会消耗额外内存

不阻塞客户端命令

缺点

阻塞客户端命令

需要fork,消耗内存

自动触发:

1.在redis.conf配置文件中配置触发条件save m n(当m秒内发生n次变化时则会触发bgsave)。可配置多个,当满足任意一个条件时都会触发bgsave。

https://github.com/redis/redis/blob/3.2/redis.conf#L202
# save <seconds> <changes>

// 可配置多个
// 当时间到900秒时,如果redis数据发生了至少1次变化,则执行bgsave
save 900 1
save 300 10
save 60 10000

2.主从复制场景下,从节点需要全量复制,则主节点会执行bgsave命令,并将rdb文件发送给从节点;

3.debug reload提供debug级别的重启,不清空内存的一种重启,这种方式也会触发RDB文件的生成;

4.执行shutdown命令时,自动执行rdb持久化,如下图日志:

save m n 配置的实现原理:

通过serverCron函数dirty计数器、和lastsave时间戳来实现的。Redis会每隔100ms执行serverCron函数,会检查所有save m n的配置,只要满足一个save m n则执行bgsave。

  • 当前时间-lastsave > m
  • dirty >= n

serverCron函数:Redis服务器的周期性操作函数,默认每隔100ms执行一次,来对各种状态进行维护,如检查save m n配置是否满足条件,满足就执行bgsave;判断当前user_memory和user_memory_peak大小来设置内存使用峰值等等;函数源码地址:redis/server.c at 3.2 · redis/redis · GitHub

dirty计数器:Redis服务器维持的一个状态,记录执行bgsave/save命令后,进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。

// dirty记录的是服务器进行了多少次修改,而不是客户端执行了多少条修改数据的命令。
set mykey helloworld
// 则dirty+1
sadd myset v1 v2 v3
// 则dirty+3

lastsave时间戳:也是Redis服务器维持的一个状态,记录上一次成功执行save/bgsave的时间。

2、执行流程

Redis源码剖析(十)--RDB持久化 - 不学习就没有梦想 - 博客园

  1. Redis父进程判断:当前是否有正在执行save/bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题
  2. 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
  3. 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令
  4. 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换
  5. 子进程发送信号给父进程表示完成,父进程更新统计信息

3、RDB文件

RDB文件是经过压缩的二进制文件

存储路径:

RDB文件的存储路径既可以在启动前配置,也可以通过命令动态设定。

// 配置地址:https://github.com/redis/redis/blob/3.2/redis.conf#L236
// redis.conf文件里面配置,默认是Redis根目录下的dump.rdb文件

// dir配置指定目录
# Note that you must specify a directory here, not a file name.
dir ./

// dbfilename指定文件名
# The filename where to dump the DB
dbfilename dump.rdb

// 动态修改RDB存储路径,在磁盘损害或空间不足时非常有用
config set dir {newdir}
config set dbfilename {newFileName}

?RDB文件格式:

  • REDIS:5字节常量,保存着 "REDIS" 五个字符;
  • db_version:4字节,RDB文件的版本号,非Redis版本号;
  • database 0:一个完整的数据库(0号数据库),同理database 3表示3号数据库。只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);
    • SELECTDB:1字节常量,代表后面跟着的是数据库号码;
    • db_number:数据库号码;
    • key_value_pairs:具体键值对数据,含过期时间的键值对会带有 EXPIRETIME_MS 和过期时间。
    • key_value_pairs键值对中的 TYPE 属性:记录类对象的编码类型,程序会根据 TYPE 属性来决定如何读入和解释value数据。
  • EOF:常量,RDB文件结束标志。
  • check_sum:前面所有内容的校验和(CRC64),通过比较计算来比较,用来检查RDB文件是否出错或损坏。

默认采用LZF算法对RDB文件进行压缩。虽然压缩耗时,但是可以大大减小RDB文件的体积。RDB文件的压缩并不是针对整个文件,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20字节)时才会进行

// https://github.com/redis/redis/blob/3.2/redis.conf#L225
// redis.conf配置中压缩默认开启
rdbcompression yes

// 通过命令关闭rdb压缩
config set rdbcompression no

??

4、启动时加载

  • RDB文件的载入是服务器启动时自动执行的,没有专门的命令;
  • 但AOF的优先级更高,当AOF开启时,Redis会优先载入AOF文件恢复数据;只有当AOF关闭时,才会自动载入RDB文件
  • 服务器载入RDB文件期间处于阻塞状态,直到载入完成为止;
  • 载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。

5、RDB常用配置总结

// RDB文件和AOF文件所在目录
dir ./

// RDB文件名
dbfilename dump.rdb

// bgsave自动触发的条件;如果没有save m n配置,相当于自动的RDB持久化关闭,不过此时仍可以通过其他方式触发
save <seconds> <changes>

// 当bgsave出现错误时,Redis是否停止执行写命令;
// 设置为yes,当硬盘出现问题时,可以及时发现,避免数据的大量丢失;
// 设置为no,则Redis无视bgsave的错误继续执行写命令,当对Redis服务器的系统(尤其是硬盘)使用了监控时,该选项考虑设置为no
stop-writes-on-bgsave-error yes:

// 是否开启RDB文件压缩
rdbcompression yes

// 是否开启RDB文件的校验,在写入文件和读取文件时都起作用;
// 关闭checksum在写入文件和启动文件时大约能带来10%的性能提升,但是数据损坏时无法发现。
rdbchecksum yes

四、AOF持久化(Append-only File)

AOF持久化即Append Only File,将Redis执行的写命令记录到AOF日志文件中(有点像MySQL的binlog),Redis重启时再次执行AOF文件中的命令来恢复数据。与RDB相比,AOF实时性更好,因此已成为主流的持久化方案。

1、开启AOF

Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置文件中配置;

// 配置地址:https://github.com/redis/redis/blob/3.2/redis.conf#L593
// aof开关
appendonly no

// aof文件名
appendfilename "appendonly.aof"

2、执行流程

由于需要记录Redis的每条写命令,因此AOF不需要触发,AOF的执行流程:

  • 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
  • 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
  • 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。

命令追加(append):

  • 将写命令追加到缓冲区,而不是直接写入文件,避免每次有写命令的磁盘IO,影响性能;
  • 命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,简单易读易操作;
  • 在AOF文件中,除了用于指定数据库的select命令(如select 0 为选中0号数据库)是由Redis添加的,其他都是客户端发送来的写命令;

文件写入(write)和文件同步(fsync):

Redis提供了多种AOF缓存区的同步策略,策略涉及到操作系统的write函数和fsync函数。

  • write函数:将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里;虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失;
  • fsync、fdatasync等同步函数:可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
// 同步策略配置,https://github.com/redis/redis/blob/3.2/redis.conf#L622
// 通过appendfsync来配置
appendfsync everysec

同步策略各种配置值的作用:

  • always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回;写命令都进行磁盘IO,Redis只能支持大约几百TPS写入,严重降低了Redis的性能。即便使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会降低SSD的寿命。
  • no:命令写入aof_buf后调用系统write操作;同步由操作系统负责,通常同步周期为30秒。文件同步时间不可控,缓冲区堆积数据多,数据安全无法保证。
  • everysec:命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认和推荐配置

命令

alwarys

everysec

no

优点

不丢失数据

每秒一次fsync

缺点

IO开销大,一般的sata磁盘只支持击败TPS

最多丢1s的数据

时间不可控、数据安全不可保证

3、文件重写(rewrite)

随着时间积累,AOF文件的写命令越来越多,文件体积也越来越大;过大的AOF文件不仅会影响服务器的运行,也会导致数据恢复时间过长。AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

  1. AOF重写会减少AOF文件的体积
  2. 加速服务恢复速度

文件重写为什么能压缩AOF文件体积?

  • 过期数据不再写入文件
  • 无效的命令不再写入文件:如数据多次重复设置(set key v1,set key v2)、删除数据(sadd myset v1, del myset)等等;
  • 多条命令合并:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。为了防止单条命令过大造成客户端缓冲区溢出,list、set、hash、zset类型的key,并不一定只使用一条命令;而是以某个常量为界将命令拆分为多条。这个常量在redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD中定义,不可更改,3.0版本中值是64。

文件重写的触发

触发分为手动触发自动触发

手动触发:调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞。

自动触发:根据配置auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数,以及aof_current_size和aof_base_size状态确定触发时机。

auto-aof-rewrite-min-size 64mb:AOF文件大小超过64m时重写;

auto-aof-rewrite-percentage 100:当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值超过100%时进行重写。

当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。

?文件重写的流程:

  • Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。基于性能方面的考虑。
  • 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的。
  • 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。
  • fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,为了防止新AOF文件生成期间丢失这部分数据,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。
  • 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。
  • 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。
  • 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。
  • 新的AOF文件替换老文件,完成AOF重写。

在AOF重写的过程中,如果有新的写命令到来了,会影响AOF重写吗?

不会,新的写命令不仅会记录AOF缓冲区,还会记录到AOF重写缓冲区,这样当AOF重写结束后,把AOF重写缓冲区数据写到新AOF文件,就不会丢失了。

4、启动时加载

当AOF配置开启时,Redis会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会使用RDB文件恢复数据。

文件校验:与载入RDB文件类似,载入AOF文件时,也会进行校验,如果文件损坏,则日志中打印错误,Redis启动失败。但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,Redis忽略掉AOF文件的尾部,启动成功。aof-load-truncated参数默认是开启的。

伪客户端:因为AOF文件存储的是Redis命令,且命令只能在客户端上下文中执行;Redis服务器在载入AOF文件之前,会创建一个没有网络连接的客户端,来执行AOF文件中的命令,命令执行的效果与带网络连接的客户端完全一样,恢复数据。

5. AOF常用配置总结

// 是否开启AOF,默认关闭
appendonly no

// AOF文件名
appendfilename "appendonly.aof"

// RDB文件和AOF文件所在目录
dir ./

// fsync持久化策略
appendfsync everysec

// AOF重写期间是否禁止fsync;
// 启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡。
no-appendfsync-on-rewrite no

// 文件重写触发条件之一,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值超过100%时进行重写。
auto-aof-rewrite-percentage 100
// 文件重写触发提交之一, AOF文件大小超过64m时重写
auto-aof-rewrite-min-size 64mb

// 如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件
aof-load-truncated yes

五、方案选择与常见问题

1、RDB和AOF优缺点

  • RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。
  • RDB文件的缺点在于其数据快照的持久化方式决定了实时差,数据的大量丢失无法接受只能选择AOF持久化。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
  • 与RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好;缺点是文件大、恢复速度慢、对性能影响大。

持久化

RDB

AOF

启动优先级

体积

恢复速度

数据安全性

丢数据

可以高实时性,根据策略

轻重

2、持久化策略选择

无论是RDB还是AOF,持久化的开启都是要付出性能的代价。

  • RDB持久化,bgsave进行fork操作时阻塞主进程;子进程向硬盘写数据也会带来IO压力;
  • AOF持久化,向硬盘写数据的频率大大提高(everysec策略下为秒级),IO压力更大。可能造成AOF追加阻塞问题。
  1. Redis完全用作DB层数据的cache层,那么无论是单机还是主从,都可以不进行任何持久化。
  2. 在单机环境下(对于个人开发者),如果可以接受十几分钟或更多数据丢失,可以选择RDB持久化;只能接受秒级别的数据丢失,应该选择AOF。
  3. 多数生产环境下会配置主从或集群,slave既可以实现数据热备,也可以进行读写分离,以及在master宕掉后继续提供服务。可以:
    1. master:完全关闭持久化(包括RDB和AOF),让master的性能达到最好;
    2. slave:关闭RDB,开启AOF(对数据安全要求不高,开启RDB关闭AOF也可以),定时对持久化文件进行备份。关闭AOF的自动重写,通过定时任务在每天Redis闲时(如凌晨12点)调用bgrewriteaof。
  4. 异地灾备:同机房断电、火宅导致磁盘损坏。所有需要将定时将RDB文件或重写后的AOF文件拷贝到远程机器上。一般,由于RDB文件文件小、恢复快,因此灾难恢复常用RDB文件;异地备份的频率根据数据安全性的需要及其他条件来确定,但最好不要低于一天一次。

Redis重启数据加载逻辑

3、fork阻塞:CPU的阻塞

在实践中,为什么Redis的单机内存不能过大?

  • 当请求暴增,需要从库扩容时,Redis内存过大会导致扩容时间太长;
  • 持久化过程中的fork操作时,生成内存页时间变长;
  • 当主机宕机时,主从切换,Redis内存过大导致挂载速度过慢;

fork操作细节:

fork采用操作系统提供的写时复制机制(copy-on-write,COW),避免一次性拷贝大量内存数据给主进程造成阻塞问题,fork时会拷贝进程必要的数据结构,如内存页表(虚拟内存和物理内存的映射索引表)。拷贝过程会消耗大量CPU资源,拷贝完成之前进程是阻塞的,所有阻塞时间取决于整个内存大小,实例越大,内存页表越大,fork阻塞时间越长。

拷贝完内存页表后,子进程与父进程指向相同的内存地址空间。也就是说,子进程没有复制内存,而是共享内存,通过内存页表指向同一内存地址。

但是kenel会把所有内存页设置为read-only只读权限,当主线程进行写操作时,会触发页异常中断(page-fault),页中断处理会copy复制出新的内存页。所以当没有数据改变时,主进程和子进程共享内存数据,只有数据变更时,才进行copy操作。即copy-on-warite。

RDB持久化的bgsave和AOF重写的bgrewriteaof都会fork出子进程操作,如果redis内存过大,会导致fork操作时复制内存页表耗时过多;而fork会堵塞主进程。Redis单机内存达到了10GB,fork时耗时可能会达到百毫秒级别(如果使用Xen虚拟机,这个耗时可能达到秒级别),可通过命令info stats,查看latest_fork_usec的值,单位为微秒

4、AOF追加阻塞:硬盘的阻塞

AOF的everysec策略流程图:

  1. 文件写入:主线程中,命令写入aof_buf后调用系统write操作,write完成后主线程返回。
  2. 文件同步:由专门的文件同步线程每秒调用系统的fsync同步硬盘
  3. 主线程每次AOF会对比上次fsync成功的时间,距上次不到2s,主线程直接返回;超过2s,则主线程阻塞直到fsync同步完成

因此系统硬盘负载过大导致fsync速度太慢,会导致Redis主线程的阻塞;使用everysec配置,AOF最多可能丢失2s的数据,而不是1s

这样处理的好处是Redis主线程高速的想aof_buf写入命令,磁盘负载可能越来越大,IO资源占用越来越多,如果Redis异常退出,丢失的数据也会越多。

AOF追加阻塞问题定位的方法:

  1. info Persistence中的aof_delayed_fsync:AOF追加阻塞发生的累积次数(即主线程等待fsync而阻塞)。
  2. AOF阻塞时的Redis日志
  3. 频繁发生AOF阻塞时,说明系统的硬盘负载太大,可以更换IO速度更快的硬盘,或通过IO监控分析工具对系统的IO负载进行分析,如iostat(系统级io)、iotop(io版的top)、pidstat等。

5、info Persistence命令

看持久化相关状态的方法

rdb_last_bgsave_status:上次bgsave 执行结果,可以用于发现bgsave错误
rdb_last_bgsave_time_sec:上次bgsave执行时间(单位是s),可以用于发现bgsave是否耗时过长
aof_enabled:AOF是否开启
aof_last_rewrite_time_sec: 上次文件重写执行时间(单位是s),可以用于发现文件重写是否耗时过长
aof_last_bgrewrite_status: 上次bgrewrite执行结果,可以用于发现bgrewrite错误
aof_buffer_length和aof_rewrite_buffer_length:aof缓存区大小和aof重写缓冲区大小
aof_delayed_fsync:AOF追加阻塞情况的统计

6、混合持久化

重启Redis很少使用RDB来恢复内存,因为会丢失大量数据;通常使用AOF回放,但相对于RDB会慢很多,启动花费时间长。

Redis4.0提出了混合使用AOF日志和内存快照的方法。内存快照以一定的频率执行,在两次快照之间,还用AOF日志记录着期间的所有命令操作。这样可以避免频繁的生成快照;AOF只记录两次快照间记录,减少文件大小,避免重写开销。

在Redis重启时,可以先加载RDB的内容,然后再重放增量AOF日志就可以完全替代之前AOF全量文件重放,重启效率的到提升。

总结:

1、持久化在Redis高可用中的作用:数据备份,与主从复制相比强调的是由内存到硬盘的备份。

2、RDB持久化:将数据快照备份到硬盘;介绍了其触发条件(包括手动出发和自动触发)、执行流程、RDB文件等,特别需要注意的是文件保存操作由fork出的子进程来进行。

3、AOF持久化:将执行的写命令备份到硬盘(类似于MySQL的binlog),介绍了其开启方法、执行流程等,特别需要注意的是文件同步策略的选择(everysec)、文件重写的流程。

4、一些现实的问题:包括如何选择持久化策略,以及需要注意的fork阻塞、AOF追加阻塞等。

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:53:18  更:2022-07-03 10:55:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 2:54:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码