Redis核心技术与实战学习笔记
Redis核心技术与实战学习笔记
- 最近想沉下心来看下redis,买了蒋德钧老师的《Redis 核心技术与实战》,这里记录一些学习笔记 希望能够坚持下去
- 有想一起学习的童鞋,可以点击跳转到文章尾部获取学习资源,仅供学习不要用于任何商业用途!!!
redis知识全景图
-
学习要有一个全局的系统观念
redis问题画像图
- redis问题可以多套到这个图中分析形成统一的印象
- 问题 --> 主线 --> 技术点
SimpleKV需要考虑的问题
-
存什么样的数据
- KV对 key为string,value为基本类型
-
数据有哪些操作
- 增删改查 PUT/GET/DEL/Scan
- 不论是增删改查都涉及到索引问题,因为要先定位到key对应的内存位置再读写 这里有涉及到索引 可以考虑全局hash
- 这里如果value是复杂结构 hash后还要再次用到不同的索引算法
- 如果是增和删除 又涉及到内存管理 因为需要分配内存或者释放内存,如果value大小不一致 内存分配算法至关重要
-
数据存在哪里
- 存在内存比较快 但内存没了容易丢失,所以还要解决持久化的问题,每次都落地则性能不行,可定时落地到文件
-
客户端怎么访问
- .so动态连接库 单机访问
- socket连接 可以跨机器 但要解决连接管理,协议解析
- 多个客户端并发访问 如何高性能 涉及到IO模型
-
容灾怎么来做
- 涉及到主从或者集群
-
重启后怎么快速初始化或者恢复
-
也是涉及到持久化
-
redis数据结构
值的数据类型
- String、List、Hash、Set、sortedSet
KV保存用到的数据结构
整体图示
-
在 Redis 3.0 版本中 List 对象的底层数据结构由「双向链表」或「压缩表列表」实现,但是在 3.2 版本之后,List 数据类型底层数据结构是由 quicklist 实现的;
-
在最新的 Redis 代码(还未发布正式版本)中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了
全局Hash索引示意
hash冲突解决办法
-
链式hash 冲突的时候用链表的方式保存冲突的数据,所以多个冲突的数据要遍历就要逐个遍历了
-
2个全局hash,渐进式rehash
-
直接全量rehash涉及大量内存复制操作,可能阻塞客户端请求处理,所以采取的是渐进式rehash
-
redis数据结构全景图_xiaolin
-
redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;
-
dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用,具体什么是 rehash,我在本文的哈希表数据结构会讲;
-
ditctht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;
-
dictEntry 结构,表示哈希表节点的结构,结构里存放了 void * key 和 void * value 指针, *key 指向的是 String 对象,而 *value 则可以指向 String 对象,也可以指向集合类型的对象,比如 List 对象、Hash 对象、Set 对象和 Zset 对象
-
void * key 和 void * value 指针指向的是 Redis 对象
redisObject数据结构示意
-
type,标识该对象是什么类型的对象(String 对象、 List 对象、Hash 对象、Set 对象和 Zset 对象);
-
encoding,标识该对象使用了哪种底层的数据结构;
-
ptr,指向底层数据结构的指针
简单字符串SDS(simple dynamic string)
SDS数据结构
struct SDS{len //这样获取字符串长度的时候,只需要返回这个成员变量值就行,时间复杂度只需要 O(1)alloc //分配空间长度 类似capicity 分配给字符数组的空间长度。这样在修改字符串的时候,可以通过 alloc - len 计算出剩余的空间大小,可以用来判断空间是否满足修改需求,如果不满足的话,就会自动将 SDS 的空间 扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用 SDS 既不需要手动修改 SDS 的空间大小,也不会出现前面所说 的缓冲区溢出的问题当判断出缓冲区大小不够用时,Redis 会自动将扩大 SDS 的空间大小(小于 1MB 翻倍扩容,大于 1MB 按 1MB 扩容flag //用来表示不同类型的 SDS。一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64每种类型占用内存不一样 更加灵活 而且是禁止内存字节对齐的,节省内存buf[] 字符数组,用来保存实际数据。不仅可以保存字符串,也可以保存二进制数据
}
链表List
typedef struct listNode {//前置节点struct listNode *prev;//后置节点struct listNode *next;//节点的值void *value;
} listNode;
在这个基础上增加
typedef struct list {//链表头节点listNode *head;//链表尾节点listNode *tail;//节点值复制函数void *(*dup)(void *ptr);//节点值释放函数void (*free)(void *ptr);//节点值比较函数int (*match)(void *ptr, void *key);//链表节点数量unsigned long len;
} list;
Redis 的链表实现优点如下:
- listNode 链表节点的结构里带有 prev 和 next 指针,获取某个节点的前置节点或后置节点的时间复杂度只需O(1),而且这两个指针都可以指向 NULL,所以链表是无环链表;
- list 结构因为提供了表头指针 head 和表尾节点 tail,所以获取链表的表头节点和表尾节点的时间复杂度只需O(1);
- list 结构因为提供了链表节点数量 len,所以获取链表中的节点数量的时间复杂度只需O(1);
- listNode 链表节使用 void* 指针保存节点值,并且可以通过 list 结构的 dup、free、match 函数指针为节点设置该节点类型特定的函数,因此链表节点可以保存各种不同类型的值;
链表的缺陷也是有的:
- 链表每个节点之间的内存都是不连续的,意味着无法很好利用 CPU 缓存。能很好利用 CPU 缓存的数据结构就是数组,因为数组的内存是连续的,这样就可以充分利用 CPU 缓存来加速访问。
- 还有一点,保存一个链表节点的值都需要一个链表节点结构头的分配,内存开销较大。
因此,Redis 3.0 的 List 对象在数据量比较少的情况下,会采用「压缩列表」作为底层数据结构的实现,它的优势是节省内存空间,并且是内存紧凑型的数据结构。
不过,压缩列表存在性能问题(具体什么问题,下面会说),所以 Redis 在 3.2 版本设计了新的数据结构 quicklist,并将 List 对象的底层数据结构改由 quicklist 实现。
然后在 Redis 5.0 设计了新的数据结构 listpack,沿用了压缩列表紧凑型的内存布局,最终在最新的 Redis 版本,将 Hash 对象和 Zset 对象的底层数据结构实现之一的压缩列表,替换成由 listpack 实现。
压缩列表
压缩列表的最大特点,就是它被设计成一种内存紧凑型的数据结构,占用一块连续的内存空间,不仅可以利用 CPU 缓存,而且会针对不同长度的数据,进行相应编码,这种方法可以有效地节省内存开销。
但是,压缩列表的缺陷也是有的:
- 不能保存过多的元素,否则查询效率就会降低;
- 新增或修改某个元素时,压缩列表占用的内存空间需要重新分配,甚至可能引发连锁更新的问题。
因此,Redis 对象(List 对象、Hash 对象、Zset 对象)包含的元素数量较少,或者元素值不大的情况才会使用压缩列表作为底层数据结构。
-
*zlbytes*,记录整个压缩列表占用对内存字节数;
-
*zltail*,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;
-
*zllen*,记录压缩列表包含的节点数量;
-
*zlend*,标记压缩列表的结束点,固定值 0xFF(十进制255)。
-
*prevlen*,记录了「前一个节点」的长度;
-
*encoding*,记录了当前节点实际数据的类型以及长度;
-
*data*,记录了当前节点的实际数据;
当我们往压缩列表中插入数据时,压缩列表就会根据数据是字符串还是整数,以及数据的大小,会使用不同空间大小的 prevlen 和 encoding 这两个元素里保存的信息,这种根据数据大小和类型进行不同的空间大小分配的设计思想,正是 Redis 为了节省内存而采用的。
分别说下,prevlen 和 encoding 是如何根据数据的大小和类型来进行不同的空间大小分配。
压缩列表里的每个节点中的 prevlen 属性都记录了「前一个节点的长度」,而且 prevlen 属性的空间大小跟前一个节点长度值有关,比如:
- 如果前一个节点的长度小于 254 字节,那么 prevlen 属性需要用 1 字节的空间来保存这个长度值;
- 如果前一个节点的长度大于等于 254 字节,那么 prevlen 属性需要用 5 字节的空间来保存这个长度值;
encoding 属性的空间大小跟数据是字符串还是整数,以及字符串的长度有关:
- 如果当前节点的数据是整数,则 encoding 会使用 1 字节的空间进行编码。
- 如果当前节点的数据是字符串,根据字符串的长度大小,encoding 会使用 1 字节/2字节/5字节的空间进行编码。
连锁更新
压缩列表除了查找复杂度高的问题,还有一个问题。
压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降。
前面提到,压缩列表节点的 prevlen 属性会根据前一个节点的长度进行不同的空间大小分配:
- 如果前一个节点的长度小于 254 字节,那么 prevlen 属性需要用 1 字节的空间来保存这个长度值;
- 如果前一个节点的长度大于等于 254 字节,那么 prevlen 属性需要用 5 字节的空间来保存这个长度值;
现在假设一个压缩列表中有多个连续的、长度在 250~253 之间的节点
因为这些节点长度值小于 254 字节,所以 prevlen 属性需要用 1 字节的空间来保存这个长度值
这时,如果将一个长度大于等于 254 字节的新节点加入到压缩列表的表头节点,即新节点将成为 e1 的前置节点
e1 原本的长度在 250~253 之间,因为刚才的扩展空间,此时 e1 的长度就大于等于 254 了,因此原本 e2 保存 e1 的 prevlen 属性也必须从 1 字节扩展至 5 字节大小。
正如扩展 e1 引发了对 e2 扩展一样,扩展 e2 也会引发对 e3 的扩展,而扩展 e3 又会引发对 e4 的扩展…. 一直持续到结尾。
这种在特殊情况下产生的连续多次空间扩展操作就叫做「连锁更新」,就像多米诺牌的效应一样,第一张牌倒下了,推动了第二张牌倒下;第二张牌倒下,又推动了第三张牌倒下….,
压缩列表的缺陷
空间扩展操作也就是重新分配内存,因此连锁更新一旦发生,就会导致压缩列表占用的内存空间要多次重新分配,这就会直接影响到压缩列表的访问性能。
所以说,虽然压缩列表紧凑型的内存布局能节省内存开销,但是如果保存的元素数量增加了,或是元素变大了,会导致内存重新分配,最糟糕的是会有「连锁更新」的问题。
因此,压缩列表只会用于保存的节点数量不多的场景,只要节点数量足够小,即使发生连锁更新,也是能接受的。
虽说如此,Redis 针对压缩列表在设计上的不足,在后来的版本中,新增设计了两种数据结构:quicklist(Redis 3.2 引入) 和 listpack(Redis 5.0 引入)。这两种数据结构的设计目标,就是尽可能地保持压缩列表节省内存的优势,同时解决压缩列表的「连锁更新」的问题。
仅供学习 切记用于任何商业用途
关注 _微_信_公_众_号 疯子爱淡定 回复 Redis核心技术学习
Hash
typedef struct dict {…//两个Hash表,交替使用,用于rehash操作dictht ht[2]; …
} dict;
---------------------------->
typedef struct dictht {//哈希表数组dictEntry **table;//哈希表大小unsigned long size; //哈希表大小掩码,用于计算索引值unsigned long sizemask;//该哈希表已有的节点数量unsigned long used;
} dictht;
---------------------------->
typedef struct dictEntry {//键值对中的键void *key;//键值对中的值union {void *val;uint64_t u64;int64_t s64;double d;} v;//指向下一个哈希表节点,形成链表struct dictEntry *next;
} dictEntry;
负载因子=已保存节点数量/hash表大小
- 当负载因子大于等于 1 ,并且 Redis 没有在执行 bgsave 命令或者 bgrewiteaof 命令,也就是没有执行 RDB 快照或没有进行 AOF 重写的时候,就会进行 rehash 操作。
- 当负载因子大于等于 5 时,此时说明哈希冲突非常严重了,不管有没有有在执行 RDB 快照或 AOF 重写,都会强制进行 rehash 操作
相关文章:
Redis核心技术与实战学习笔记
Redis核心技术与实战学习笔记 最近想沉下心来看下redis,买了蒋德钧老师的《Redis 核心技术与实战》,这里记录一些学习笔记 希望能够坚持下去有想一起学习的童鞋,可以点击跳转到文章尾部获取学习资源,仅供学习不要用于任何商业用途!!! redis知识全景图 …...
力扣经典题目之->设计循环队列 的超详细讲解与实现
一:题目 二:思路讲解 前提: a:本文采取数组来实现队列去解决题目 b:开辟k1个空间,front指向队首,rear指向队尾的后一个,rear这样会更好的判空和判满 以下根据pop和push感受满和空…...
【数据结构】排序算法——Lesson2
Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…...
Ubuntu编译ffmpeg并添加cmake工程
文章目录 前言前提须知为什么要自己编译 FFmpeg前提软件包与工具的安装编译ffmpeg写CMakeList.txt包含ffmpeg到我们项目中 总结 前言 FFmpeg 是一个领先的多媒体框架,能够解码、编码、转码、复用、解复用、流化、过滤和播放几乎所有人类和机器创造的内容。FFmpeg 包…...
Vue.js[组件(Component)]
什么是: 拥有专属的HTML,CSS,数据的,可重用的页面独立区域 一个页面由多个组件聚合而成一个大型的页面 在代码层面上,一个组件就是一个可反复使用的自定义标签。 vs jq插件 vs boot组件 boot插件: 虽然可重用,但仍需…...
基于微信小程序+SpringBoot+Vue的校园自助打印系统(带1w+文档)
基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 管理信息可以处理复杂的信息从而提高用户的工作效率,减少失误。所以本基于Vue和微信小程序的校园自助打印系统的开发非常有意义,本系…...
qt设置过滤器
1.创建事件过滤器类,在主窗口中安装事件过滤器 class PasteFilter : public QObject {Q_OBJECTpublic:PasteFilter(QObject *parent nullptr) : QObject(parent) {}protected:bool eventFilter(QObject *obj, QEvent *event) override {if (event->type() QEv…...
线上环境服务器CPU飙升排查
前因 收到线上服务器CPU使用率100%的告警信息。 环境 jdk1.8CentOS Linux ;CentOS Linux 排查 查看服务器CPU使用率 果然cpu已经达到了100%了 命令 top 使用arthas工具 使用方式 arthas 执行命令java -jar arthas-boot.jar 然后执行命令 thread 看到有两个…...
unity文字||图片模糊
一.文字模糊 1、增大字体大小后等比缩放 快捷键R 2、更改字体渲染模式 二.图片模糊 1、更改过滤模式 2、更改格式或者压缩 3、如果只是图片边缘看不清,可以增加canvas/图片的每单位参考像素...
香薰学习笔记
1 喷香水的方法 ChatGPT-4o 学习使用香水是提升个人形象的一个好方法。 喷香水的方法如下: 皮肤吸收:香水最好喷在皮肤上,因为皮肤的温度能帮助香水散发出更好的香味。喷在衣服上可能会影响香水的原始味道。脉搏点:将香水喷在脉搏…...
iOS ------ weak的基本原理
1.weak的基本概念 weak弱引用,所引用的对象的引用计数不会加一,引用对象被释放的时候会自动设置为nil多用于解决对象间的相互引用造成内存泄露的循环引用的问题 2.实现原理 Person *object [[Person alloc] init]; id __weak objc object;Runtime维…...
实时更新UI界面
1.处理实时通信,几种方案 1:当一个用户发送一条需要实时更新的信息,我可以直接查找在线用户,通过在线用户来进行判断条件,发送更新请求 2:用户在一个需要实时更新的界面时,就不断的向服务端发…...
为什么Spring不推荐@Autowired用于字段注入
背景 Spring是Java程序员常用的框架之一。官方从Spring 4.0开始不推荐使用Autowired进行字段注入。 Spring注入方式 基于构造器注入:在构造器上使用Autowired。 优点:可以声明字段为final,确保字段在构造时被初始化。 基于setter方法注入&…...
【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十九章 Linux MISC驱动
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...
基于MobileNetv2的垃圾分类函数式自动微分-昇思25天打卡
基于MobileNetv2的垃圾分类 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入,对图像中的垃圾物体进行检测,并且将检测结果图片保存到文件中。 1、实验目的 了解熟悉垃圾分类应用代码的编写(Python语言)&a…...
STM32CubeIDE(CAN)
目录 一、概念 1、简述 2、CAN 的几种模式 二、实践 1、环回模式轮询通信 1.1 软件配置 1.2 代码编写 2、环回模式中断通信 2.1 软件配置 2.2 代码编写 一、概念 1、简述 STM32微控制器系列包含多个型号,其中一些型号集成了CAN(Controller Are…...
GO Channel使用详解(各种场景下的最佳实践)
GO Channel使用详解(各种场景下的最佳实践) 一个知识点:通过反射的方式执行 select 语句,在处理很多的 case clause,尤其是不定长的 case clause 的时候,非常有用。而且,在后面介绍任务编排的实现时,我也会采用这种方法,所以,我先带你具体学习下 Channel 的反射用法…...
SwiftUI 5.0(iOS 17)滚动视图的滚动目标行为(Target Behavior)解惑和实战
概览 在 SwiftUI 的开发过程中我们常说:“屏幕不够,滚动来凑”。可见滚动视图对于超长内容的呈现有着多么秉轴持钧的重要作用。 这不,从 SwiftUI 5.0(iOS 17)开始苹果又为滚动视图增加了全新的功能。但是官方的示例可…...
picker 构建记录
picker 构建记录 tomlinuxtom:~/openverify/picker$ cd picker bash: cd: picker: 没有那个文件或目录 tomlinuxtom:~/openverify/picker$ export BUILD_XSPCOMM_SWIGpython tomlinuxtom:~/openverify/picker$ make rm -rf temp build /home/tom/Tools/verible-v0.0-3724/bin/…...
Docker部署kafka,Docker所在宿主机以外主机访问
# 安装启动zookeeper docker run -d --name zookeeper --publish 2181:2181 --volume /etc/localtime:/etc/localtime zookeeper:latest --network 指定的网络 -p:设置映射端口(默认2181) -d:后台启动 # 启动kafka docker run -d…...
控制欲过强的Linux小进程
控制欲强?视奸?普通人那才叫视奸,您是皇帝,天下大事无一逃过您的耳目,您想看什么就看什么,臣怀疑他在朋友圈私养兵士,囤积枪甲,蓄意谋反,图谋皇位啊! 哈哈哈哈开个玩笑&…...
探讨元宇宙和VR虚拟现实之间的区别
在数字时代,人们对虚拟现实的兴趣与日俱增。在虚拟现实技术的推动下,出现了两个概念:元宇宙和VR虚拟现实。虽然这两个概念都与虚拟现实有关,但它们有着不同的特点和用途。在本文中,我们将探讨元宇宙和VR虚拟现实之间的…...
Docker Desktop安装
0 Preface/Foreward 1 安装 1.1 运行docker安装包 安装完Docker Desktop后,运行Docker Desktop,出现WSL 2安装不完整情况,具体情况如下: 解决方法:旧版 WSL 的手动安装步骤 | Microsoft Learn 也可以直接下载新的安…...
《Towards Black-Box Membership Inference Attack for Diffusion Models》论文笔记
《Towards Black-Box Membership Inference Attack for Diffusion Models》 Abstract 识别艺术品是否用于训练扩散模型的挑战,重点是人工智能生成的艺术品中的成员推断攻击——copyright protection不需要访问内部模型组件的新型黑盒攻击方法展示了在评估 DALL-E …...
vscode调试nextjs前端后端程序、nextjs api接口
最近有一个项目使用了nextjs框架,并且使用nextjs同时实现了前后端,由于之前前后端都是分离的,前端的调试可以通过在代码种添加debugger或者直接在浏览器中打断点实现,现在想调试后端接口,前面的方式就不适用了。故研究…...
《SeTformer Is What You Need for Vision and Language》
会议:AAAI 年份:2024 论文:DDAE: Towards Deep Dynamic Vision BERT Pretraining - AMinerhttps://www.aminer.cn/pub/6602613613fb2c6cf6c387c2/ddae-towards-deep-dynamic-vision-bert-pretraining 摘要 这篇论文介绍了一种新型的变换器…...
[保姆级教程]uniapp安装使用uViewUI教程
文章目录 创建 UniApp 项目下载uView UI下载安装方式步骤 1: 安装 uView UI步骤 2: 查看uView UI是否下载成功步骤 3: 引入 uView 主 JS 库步骤 4: 引入 uView 的全局 SCSS 主题文件步骤 5: 引入 uView 基础样式步骤 6: 配置 easycom 组件模式注意事项 NPM方式步骤 1: 安装 uVi…...
网络安全法规对企业做等保有哪些具体规定?
网络安全法规对企业做等保的具体规定 根据《中华人民共和国网络安全法》,企业作为网络运营者,需要履行网络安全等级保护制度的相关义务,确保网络安全和数据保护。具体规定包括: 网络安全等级保护制度:企业应根据网络安…...
Java开发中超好用Orika属性映射工具
Orika属性映射工具 引入pom依赖 <dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.4</version></dependency>上干货 封装的工具类:OriUtilsimport ma.glasnost.orika.Map…...
qt初入门8:下拉框,输入框模糊查询,提示简单了解 (借助QCompleter)
实现一个简单的模糊查询的逻辑,输入框能提示相关项。 主要借助qt的QCompleter 类( Qt 框架中提供的一个用于自动补全和模糊搜索的类),结合一些控件,比如QComboBox和QLineEdit,实现模糊查询的功能。 1&…...
书生网站/西安网站seo技术厂家
e.g. 给定链表 1->2->3->4,返回 2->1->4->3 的头节点。 我写了个常见的从头节点遍历,少量的奇数个或偶数个数据都能成功重新排列。但链表过长时结果显示 Time Limit Exceeded 。 1 ListNode* swapPairs(ListNode* head) {2 ListNo…...
信息技术初二做网站/小红书外链管家
300W摄像头OV3640 初始化寄存器表大全(包含VGA/XGA/SXGA等)http://www.gooogleman.com/forum.php?modviewthread&tid19133&fromuid1转载于:https://www.cnblogs.com/gooogleman/archive/2011/05/05/2037458.html...
全球十大设计公司/个人博客seo
virsh命令来创建虚拟机步骤 (1)生成硬盘镜像文件: 格式:raw或qcow2 # qemu-img create -f raw fdisk.img 10G # qemu-img info fdisk.img (2)编写xml配置文件,这一步在后面做详细介绍 (3&a…...
做网站怎么做起来的/免费网络推广软件有哪些
目录问题描述题目解析暴力法差分数组法C代码问题描述 【题目描述】 【输入格式】 【输出格式】 【样例输入】 【样例输出】 【样例解释】 【数据约定】 题目解析 暴力法 循环每一轮攻击,当生命值为负数时输出当前攻击轮数并返回。 这道题采用暴力法可以…...
做网站有哪些好处/网站流量监控
在我维护的(遗留)代码中,人们正在使用WindowsError。我可以继续用OSError替换所有出现的情况,但是遗憾的是,winerror属性被使用了,令人高兴的是,只有三种情况下,即123:try:mtime int(os.path.g…...
汉阳做网站/企业宣传方式有哪些
#!/bin/bash #zabbix一键安装脚本v1.0 #zabbix版本:3.4.15 #日期:2020年3月9号 #editor:zongyonghao #linux准备工作 echo "------------------开始安装zabbix,整个过程可能要10-20分钟,请耐心等待--------------…...