第3章 小功能大用处-事务与Lua

news2024/11/24 6:56:10

为了保证多条命令组合的原子性,Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题。
首先简单介绍Redis中事务的使用方法以及它的局限性,之后重点介绍Lua语言的基本使用方法,以及如何将Redis和Lua脚本进行集成,最后给出Redis管理Lua脚本的相关命令
1.事务
事务表示一组动作,要么全部执行,要么全部不执行。例如在社交网站上用户A关注了用户B,那么需要在用户A的关注表中加入用户B,并且在用户B的粉丝表中添加用户A,这两个行为要么全部执行,要么全部不执行,否则会出现数据不一致的情况。
Redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,它们之间的命令是原子顺序执行的,例如下面操作实现了上述用户关注问题。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
127.0.0.1:6379> sadd user:b:fans user:a
QUEUED

可以看到sadd命令此时的返回结果是QUEUED,代表命令并没有真正执行,而是暂时保存在Redis中。如果此时另一个客户端执行sismember user:a:follow user:b返回结果应该为0。

127.0.0.1:6379> sismember user:a:follow user:b
(integer) 0

只有当exec执行后,用户A关注用户B的行为才算完成,如下所示返回的两个结果对应sadd命令。

127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 1

如果要停止事务的执行,可以使用discard命令代替exec命令即可。

127.0.0.1:6379> discard
OK
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 0

如果事务中的命令出现错误,Redis的处理机制也不尽相同

  • 命令错误
    例如下面操作错将set写成了sett,属于语法错误,会造成整个事务无法执行,key和counter的值未发生变化:
127.0.0.1:6388> mget key counter
1) "hello"
2) "100"
127.0.0.1:6388> multi
OK
127.0.0.1:6388> sett key world
(error) ERR unknown command 'sett'
127.0.0.1:6388> incr counter
QUEUED
127.0.0.1:6388> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6388> mget key counter
1) "hello"
2) "100"
  • 运行时错误

例如用户B在添加粉丝列表时,误把sadd命令写成了zadd命令,这种就是运行时命令,因为语法是正确的:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
201
127.0.0.1:6379> zadd user:b:fans 1 user:a
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 1

可以看到Redis并不支持回滚功能,sadd user:a:follow user:b命令已经执行成功,开发人员需要自己修复这类问题。
有些应用场景需要在事务之前,确保事务中的key没有被其他客户端修改过,才执行事务,否则不执行(类似乐观锁)。Redis提供了watch命令来解决这类问题,表3-2展示了两个客户端执行命令的时序
在这里插入图片描述
可以看到“客户端-1”在执行multi之前执行了watch命令,“客户端-2”在“客户端-1”执行exec之前修改了key值,造成事务没有执行(exec结果为nil),整个代码如下所示:

#T1:客户端1
127.0.0.1:6379> set key "java"
OK
#T2:客户端1
127.0.0.1:6379> watch key
OK
#T3:客户端1
127.0.0.1:6379> multi
OK
#T4:客户端2
127.0.0.1:6379> append key python
(integer) 11
#T5:客户端1
127.0.0.1:6379> append key jedis
QUEUED
#T6:客户端1
127.0.0.1:6379> exec
(nil)
#T7:客户端1
127.0.0.1:6379> get key
"javapython"

Redis提供了简单的事务,之所以说它简单,主要是因为它不支持事务中的回滚特性,同时无法实现命令之间的逻辑关系计算,当然也体现了Redis的“keep it simple”的特性,下一小节介绍的Lua脚本同样可以实现事务的相关功能,但是功能要强大很多。
2.Lua用法简述
Lua语言是由C语言实现的,虽然简单小巧但是功能强大,所以许多应用都选用它作为脚本语言,尤其是在游戏领域,Web服务器Nginx将Lua语言作为扩展,增强自身功能。Redis将Lua作为脚本语言可帮助开发者定制自己的Redis命令。
2.1.数据类型及其逻辑处理
Lua语言提供了如下几种数据类型:booleans(布尔)、numbers(数值)、strings(字符串)、tables(表格),和许多高级语言相比,相对简单。下面将结合例子对Lua的基本数据类型和逻辑处理进行说明。
(1)字符串
下面定义一个字符串类型的数据:

local strings val = "world"

其中,local代表val是一个局部变量,如果没有local代表是全局变量。print函数可以打印出变量的值,例如下面代码将打印world,其中"–"是Lua语言的注释。

-- 结果是"world"
print(hello)

(2)数组
在Lua中,如果要使用类似数组的功能,可以用tables类型,下面代码使用定义了一个tables类型的变量myArray,但和大多数编程语言不同的是,Lua的数组下标从1开始计算:

local tables myArray = {"redis", "jedis", true, 88.0}
--true
print(myArray[3])

如果想遍历这个数组,可以使用for和while,这些关键字和许多编程语言是一致的
(a)for
下面代码会计算1到100的和,关键字for以end作为结束符:

local int sum = 0
for i = 1, 100
do
	sum = sum + i
end
-- 输出结果为5050
print(sum)

要遍历myArray,首先需要知道tables的长度,只需要在变量前加一个#号即可:

for i = 1, #myArray
do
	print(myArray[i])
end

除此之外,Lua还提供了内置函数ipairs,使用for index,value ipairs(tables)可以遍历出所有的索引下标和值:

for index,value in ipairs(myArray)
do
	print(index)
	print(value)
end

(b)while
下面代码同样会计算1到100的和,只不过使用的是while循环,while循环同样以end作为结束符。

local int sum = 0
local int i = 0
while i <= 100
do
	sum = sum +i
	i = i + 1
end
--输出结果为5050
print(sum)

(c)if else
要确定数组中是否包含了jedis,有则打印true,注意if以end结尾,if后紧跟then:

local tables myArray = {"redis", "jedis", true, 88.0}
for i = 1, #myArray
do
	if myArray[i] == "jedis"
	then
		print("true")
		break
	else
		--do nothing
	end
end

(3)哈希
如果要使用类似哈希的功能,同样可以使用tables类型,例如下面代码定义了一个tables,每个元素包含了key和value,其中strings1…string2是将两个字符串进行连接:

local tables user_1 = {age = 28, name = "tome"}
--user_1 age is 28
print("user_1 age is " .. user_1["age"])

如果要遍历user_1,可以使用Lua的内置函数pairs:

for key,value in pairs(user_1)
do print(key .. value)
end

函数定义:在Lua中,函数以function开头,以end结尾,funcName是函数名,中间部分是函数体:

function funcName()
...
end
contact函数将两个字符串拼接:
function contact(str1, str2)
	return str1 .. str2
end
--"hello world"
print(contact("hello ", "world"))

3 Redis与Lua
3.1.在Redis中使用Lua
在Redis中执行Lua脚本有两种方法:eval和evalsha。
(1)eval

eval 脚本内容 key个数 key列表 参数列表

下面例子使用了key列表和参数列表来为Lua脚本提供更多的灵活性:

127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world "hello redisworld"

此时KEYS[1]=“redis”,ARGV[1]=“world”,所以最终的返回结果是"hello redisworld"。
如果Lua脚本较长,还可以使用redis-cli–eval直接执行文件。例如

 //03.lua脚本代码如下:
local name=redis.call("get",KEYS[1])
local age=redis.call("get",KEYS[2])
if name=="LLL" then
    redis.call("set",KEYS[1],ARGV[1])
    redis.call("incr",KEYS[2])
end

把03.lua文件传到服务器/root/application/program/luascript/03.lua目录下

192.168.127.128:6379>get name
"LLL"
192.168.127.128:6379>get age
"15"
//执行改脚本的命令,必须在Linux的命令行,不是在Redis的命令行
 [root@linux ~]# /root/application/program/redis-tool/redis-cli -h 192.168.127.128 -p 6379 --eval /root/application/program/luascript/03.lua name age , patrickLiu
192.168.127.128:6379>get name
	"patrickLiu"
192.168.127.128:6379>get age
     "16"
//说明带参数的执行Lua脚本成功

eval命令和–eval参数本质是一样的,客户端如果想执行Lua脚本,首先在客户端编写好Lua脚本代码,然后把脚本作为字符串发送给服务端,服务端会将执行结果返回给客户端,整个过程如下。
在这里插入图片描述
(2)evalsha
除了使用eval,Redis还提供了evalsha命令来执行Lua脚本。如图3-8所示,首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和,evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻在服务端,脚本功能得到了复用。
在这里插入图片描述
加载脚本:script load命令可以将脚本内容加载到Redis内存中,例如下面将04.lua加载到Redis中,得到SHA1为:7c1ebd792518eeefe306d1a755ebbc4d6d585963

//04.lua文件
local b1=redis.call("hgetall",KEYS[1])
return b1
root@iZ2zec3etasicvrmg7svj0Z:/usr/ztt# ls
03.lua  dump.rdb      jdk-8u401-linux-x64.tar.gz  redis-6.2.7.tar.gz
04.lua  jdk1.8.0_401  redis-6.2.7                 rocketmq-all-4.9.6-bin-release.zip
root@iZ2zec3etasicvrmg7svj0Z:/usr/ztt# /usr/local/bin/redis-cli -a 1.tiantian script load "$(cat 04.lua)"
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"7c1ebd792518eeefe306d1a755ebbc4d6d585963"

执行脚本:evalsha的使用方法如下,参数使用SHA1值,执行逻辑和eval一致。

evalsha 脚本SHA1值 key个数 key列表 参数列表

所以只需要执行如下操作,就可以调用04.lua脚本:

127.0.0.1:6379> hgetall myhash
1) "name"
2) "zhangsan"
3) "sex"
4) "nan"
5) "address"
6) "hebeibaoding"
7) "school"
8) "laiyuanyizhong"
127.0.0.1:6379> evalsha 7c1ebd792518eeefe306d1a755ebbc4d6d585963 1 myhash
1) "name"
2) "zhangsan"
3) "sex"
4) "nan"
5) "address"
6) "hebeibaoding"
7) "school"
8) "laiyuanyizhong" 

3.2.Lua的Redis API
Lua可以使用redis.call函数实现对Redis的访问,例如下面代码是Lua使用redis.call调用了Redis的set和get操作:

redis.call("set", "hello", "world")
redis.call("get", "hello")

放在Redis的执行效果如下:

127.0.0.1:6379> eval 'return redis.call("get", KEYS[1])' 1 hello
"world"

除此之外Lua还可以使用redis.pcall函数实现对Redis的调用,redis.call和redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返回错误,而redis.pcall会忽略错误继续执行脚本,所以在实际开发中要根据具体的应用场景进行函数的选择。
开发提示:Lua可以使用redis.log函数将Lua脚本的日志输出到Redis的日志文件中,但是一定要控制日志级别。
Redis3.2提供了Lua Script Debugger功能用来调试复杂的Lua脚本,具体可以参考:http://redis.io/topics/ldb。
4.案例
Lua脚本功能为Redis开发和运维人员带来如下三个好处:

  • Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
  • Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这些命令常驻在Redis内存中,实现复用的效果。
  • Lua脚本可以将多条命令一次性打包,有效地减少网络开销。
  • 下面以一个例子说明Lua脚本的使用,当前列表记录着热门用户的id,
    假设这个列表有5个元素,如下所示:
127.0.0.1:6379> lpush hot:user:list user:72:ratio user:99:ratio user:3:ratio user:8:ratio user:1:ratio
(integer) 5
127.0.0.1:6379> lrange hot:user:list 0 -1
1) "user:1:ratio"
2) "user:8:ratio"
3) "user:3:ratio"
4) "user:99:ratio"
5) "user:72:ratio"

user:{id}:ratio代表用户的热度,它本身又是一个字符串类型的键:

127.0.0.1:6379> mset  user:1:ratio 986  user:8:ratio 762  user:3:ratio 556  user:99:ratio 400 user:72:ratio 101
OK
127.0.0.1:6379> mget  user:1:ratio user:8:ratio user:3:ratio user:99:ratio user:72:ratio
1) "986"
2) "762"
3) "556"
4) "400"
5) "101"

现要求将列表内所有的键对应热度做加1操作,并且保证是原子执行,此功能可以利用Lua脚本来实现。
1)将列表中所有元素取出,赋值给mylist:

local mylist = redis.call("lrange", KEYS[1], 0, -1)

2)定义局部变量count=0,这个count就是最后incr的总次数:

local count = 0

3)遍历mylist中所有元素,每次做完count自增,最后返回count:

for index,key in ipairs(mylist)
do
	redis.call("incr",key)
	count = count + 1
end
return count

将上述脚本写入lrange_and_mincr.lua文件中,并执行如下操作,返回结果为5。

redis-cli --eval lrange_and_mincr.lua hot:user:list
(integer) 5

执行后所有用户的热度自增1:

127.0.0.1:6379> mget user:1:ratio user:8:ratio user:3:ratio user:99:ratio user:72:ratio
1) "987"
2) "763"
3) "557"
4) "401"
5) "102"

lrange_and_mincr.lua文件的汇总内容为如下:

local mylist = redis.call("lrange", KEYS[1], 0, -1)
local count = 0
for index,key in ipairs(mylist)
do
	redis.call("incr",key)
	count = count + 1
end
return count

lrange_and_mincr.lua文件上传到/usr/ztt目录下

root@iZ2zec3etasicvrmg7svj0Z:/usr/ztt# rz

root@iZ2zec3etasicvrmg7svj0Z:/usr/ztt# /usr/local/bin/redis-cli -a 1.tiantian --eval lrange_and_mincr.lua hot:user:list
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
(integer) 5

执行后所有用户的热度自增1:

127.0.0.1:6379> mget user:1:ratio user:8:ratio user:3:ratio user:99:ratio
user:72:ratio
1) "987"
2) "763"
3) "557"
4) "401"
5) "102"

5 Redis如何管理Lua脚本
Redis提供了4个命令实现对Lua脚本的管理,下面分别介绍。
(1)script load

script load script

此命令用于将Lua脚本加载到Redis内存中,前面已经介绍并使用过了,这里不再赘述。
(2)script exists

scripts exists sha1 [sha1 …]

此命令用于判断sha1是否已经加载到Redis内存中:

127.0.0.1:6379> script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
1) (integer) 1

返回结果代表sha1[sha1…]被加载到Redis内存的个数。
(3)script flush

script flush

此命令用于清除Redis内存已经加载的所有Lua脚本,在执行script flush后,a5260dd66ce02462c5b5231c727b3f7772c0bcc5不再存在:

127.0.0.1:6379> script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
1) (integer) 1
127.0.0.1:6379> script flush
OK
127.0.0.1:6379> script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
1) (integer) 0

(4)script kill

script kill

此命令用于杀掉正在执行的Lua脚本。如果Lua脚本比较耗时,甚至Lua脚本存在问题,那么此时Lua脚本的执行会阻塞Redis,直到脚本执行完毕或者外部进行干预将其结束。下面我们模拟一个Lua脚本阻塞的情况进行说明。
下面的代码会使Lua进入死循环:

while 1 == 1
do
end

执行Lua脚本,当前客户端会阻塞:

127.0.0.1:6379> eval 'while 1==1 do end' 0

Redis提供了一个lua-time-limit参数,默认是5秒,它是Lua脚本的“超时时间”,但这个超时时间仅仅是当Lua脚本时间超过lua-time-limit后,向其他命令调用发送BUSY的信号,但是并不会停止掉服务端和客户端的脚本执行,所以当达到lua-time-limit值之后,其他客户端在执行正常的命令时,将会收到“Busy Redis is busy running a script”错误,并且提示使用script kill或者shutdown nosave命令来杀掉这个busy的脚本:

127.0.0.1:6379> get hello
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

此时Redis已经阻塞,无法处理正常的调用,这时可以选择继续等待,但更多时候需要快速将脚本杀掉。使用shutdown save显然不太合适,所以选择script kill,当script kill执行之后,客户端调用会恢复:

127.0.0.1:6379> script kill
OK
127.0.0.1:6379> get hello
"world"

但是有一点需要注意,如果当前Lua脚本正在执行写操作,那么script kill将不会生效。例如,我们模拟一个不停的写操作:

while 1==1
do
redis.call("set","k","v")
end

此时如果执行script kill,会收到如下异常信息:(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.上面提示Lua脚本正在向Redis执行写命令,要么等待脚本执行结束要么使用shutdown save停掉Redis服务。可见Lua脚本虽然好用,但是使用不当破坏性也是难以想象的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1862356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

圈复杂度.

圈复杂度是衡量代码的重要标准 配置&#xff1a; eslint里面&#xff1a;rules&#xff1a;complexity&#xff1a;[error,10]

CppInsights: 学习C++模版的神器

CppInsights&#xff1a;深入理解C代码的利器 C是一门强大而复杂的编程语言&#xff0c;其复杂性主要体现在语言的多层次抽象和丰富的语法特性上。尽管这些特性使得C能够高效地处理复杂的任务&#xff0c;但也给开发者带来了理解和调试代码的巨大挑战。CppInsights正是在这一背…

DDL-表操作-数据类型

一.DDL-表操作-数据类型 MySQL中的数据类型有很多,主要分为三类:数值类型,字符串类型,日期类型。 二.关系表 注意: 无符号和有符号的取值范围不是一样的,无符号需要加上UNSIGNED范围。 BLOB&#xff1a;用来描述二进制数据 TEXT:用来描述字符串 三.定长字符串和变长字符串 c…

vue3实现模拟地图上,站点名称按需显示的功能

很久很久没有更新博客了&#xff0c;因为实在是太忙了&#xff0c;每天都有公司的事情忙不完....... 最近在做车辆模拟地图&#xff0c;在实现控制站点名称按需显示时&#xff0c;折腾了好一段时间&#xff0c;特此记录一下。最终界面如下图所示&#xff1a; 站点显示需求&…

绘制全球各大洲典型流域的时间序列图

流量世界第一、长度第二的亚马逊流域&#xff08;Amazon&#xff09;、南美洲第四大、整条河流位于巴西的圣弗朗西斯科流域&#xff08;Sao Francisco&#xff09;、世界第四长、北美洲最长的密西西比流域&#xff08;Mississippi&#xff09;、欧洲最长的伏尔加流域&#xff0…

数字化转型第三步:数字化业务创新与发展,提升收入和利润

引言&#xff1a;之前笔者的文章发布了企业数字化转型业务部分&#xff0c;如【开源节流】如何通过数字化转型增强盈利能力&#xff1f;企业供应链数字化转型如何做&#xff1f;让企业盈利能力增强再飞一会 【财务数字化转型之底座】集团企业财务数据中台系统建设方案 等文章&a…

CLion2024 for Mac[po] C和C++的跨平台解代码编辑器

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

Netty学习(一)——基础组件

根据黑马程序员netty视频教程学习所做笔记。 笔记demo&#xff1a;https://gitee.com/jobim/netty_learn_demo.git 参考博客&#xff1a;https://blog.csdn.net/cl939974883/article/details/122550345 一、概述 1.1 什么是Netty Netty is an asynchronous event-driven netw…

基于改进YOLOv5的安全帽检测算法 | 引入Ghost卷积 + 添加CA注意力机制 + 更换Neck网络之BiFPN + 更换损失函数之WIoU

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。为了解决建筑工地、隧道、煤矿等施工场景中现有安全帽检测算法对于小目标、密集目标以及复杂环境下的检测精度低的问题&#xff0c;设计实现了一种基于YOLOv5的改进目标检测算法&#xff0c;记为YOLOv5-GBCW。首先使用Ghos…

【昇思初学入门】第七天打卡-模型训练

训练模型 学习心得 构建数据集。这通常包括训练集、验证集&#xff08;可选&#xff09;和测试集。训练集用于训练模型&#xff0c;验证集用于调整超参数和监控过拟合&#xff0c;测试集用于评估模型的泛化能力。 &#xff08;mindspore提供数据集https://www.mindspore.cn/d…

Fusion WAN:企业出海与全球组网的数字网络底座

众多中国企业与品牌正将目光投向海外市场&#xff0c;积极寻求发展新机遇&#xff0c;并且在这一过程中取得了显著的成果。"出海"战略已经成为一些企业转型升级的关键选择。 随着国内市场的竞争日益激烈&#xff0c;越来越多的企业开始寻求海外市场的拓展&#xff0c…

压电风扇的显著特点及其在电子系统中的应用

压电已经存在了一个多世纪&#xff0c;人们发现某些晶体结构在受到机械应力时产生表面电荷。 这种形式的压电传感器是压电传感器的工作方式。与压电传感器&#xff08;或发电机&#xff09;类似&#xff0c;压电致动器&#xff08;或电机&#xff09;使用补丁[1,3]形式的压电陶…

探索PHP中的魔术常量

PHP中的魔术常量&#xff08;Magic Constants&#xff09;是一些特殊的预定义常量&#xff0c;它们在不同的上下文中具有不同的值。这些常量可以帮助开发者获取文件路径、行号、函数名等信息&#xff0c;从而方便调试和日志记录。本文将详细介绍PHP中的魔术常量&#xff0c;帮助…

2024地理信息相关专业大学排名

在开始之前&#xff0c;不得不提一下今年福耀科技大学不能招生的遗憾&#xff0c;不知道明年是否能一切准备就绪开始招生呢&#xff1f; 如果这所大学能招生了&#xff0c;不知道它有没有地理信息相关专业呢&#xff1f; 言归正转&#xff0c;我们现在就基于公开资料&#xf…

力扣随机一题 哈希表 排序 数组

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 2491.划分技能点相等的团队【中等】 题目&#xff1a; 给你一个正整数数组…

Qt添加Dialog对话框

Qt版本&#xff1a;5.12.12 1.添加【模块】 Base class&#xff1a;可以选择QDialog、QWidget、QMainWindow 会自动生成MyDialog.h和MyDialog.cpp文件以及MyDialog.ui文件&#xff0c; 2.添加代码&#xff1a; &#xff08;1&#xff09;TestDialog.h #pragma once#include…

三分之一的违规行为未被发现

Gigamon 调查显示&#xff0c;随着漏洞的针对性越来越强、越来越复杂&#xff0c;企业在检测漏洞方面也面临越来越大的困难&#xff0c;超过三分之一的企业表示&#xff0c;现有的安全工具无法在漏洞发生时检测到它们。 随着混合云环境变得越来越复杂&#xff0c;以及恶意行为…

Docker 查看源地址/仓库地址,更改

一、源地址文件配置路径。若有docker文件夹&#xff0c;没有json&#xff0c;可以新增&#xff0c;复制进去内容 /etc/docker/daemon.json {"registry-mirrors": ["https://dockerhub.azk8s.cn","https://hub-mirror.c.163.com"&#xff0c;&q…

conda如何修改虚拟环境的python版本

有时候安装虚拟环境的时候&#xff0c;忘记指定python的版本&#xff0c;本文介绍一下如何在虚拟环境创建之后&#xff0c;修改python的版本。 1 如果安装了Anaconda Navigator。 2 终端 参考&#xff1a;conda修改当前环境中的python版本_conda更换python版本-CSDN博客

电机故障检测系统的通用性限制分析

电机故障检测系统因应用环境、功能需求、经济性等多方面差异而难以实现通用。工厂与实验室在环境条件、使用频率、功能需求、成本、维护及数据处理方面有显著不同&#xff0c;此外&#xff0c;LabVIEW软件在两者中的应用和数据处理也存在差异&#xff0c;这进一步限制了系统的通…