深入理解Redis线程模型

news2024/11/30 18:08:39

前置目标:搭建一个Redis单机服务器。搭建过程参考前面的文档(https://blog.csdn.net/Zhuxiaoyu_91/article/details/143904807)

建议调整的redis核心配置:

    daemonize yes   # 允许后台启动
    protected‐mode no #关闭保护模式,开启的话,只有本机才可以访问redis
    #bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)
 #访问密码,建议开启
 requirepass 123qweasd

这一章节,主要是理解Redis的核心数据模型,并引申出Redis应用的相关注意点。

一、Redis是什么?有什么用?

1、Redis是什么?

关于Redis,你一定或多或少听说过,互联网集中式缓存的代名词。但是,2024年了,Redis到底是什么呢?

Redis 全称 REmote DIctionary Server,远程字典服务,是一个完全开源的,高性能的Key-Value数据库。官网地址: Redis - The Real-time Data Platform 。引用官网上的一个问答,带你重新了解下Redis。

核心总结:

  • 数据结构复杂。

Redis相比于传统的K-V型数据库,能够支撑更更复杂的数据类型。这意味着Redis已经远远超出了缓存的范围,可以实现很多复杂的业务场景。并且还在不断发展更多的业务场景。

  • 数据保存在内存,但是持久化到硬盘。

数据全部保存在内存,意味着Redis进行数据读和写的性能非常高。是集中式缓存的不二之选。

数据持久化到硬盘,意味着Redis上保存的数据是非常安全的。目前Redis完全可以当做一个数据库来用。

所以,官方对Redis的作用,也已经定位成了三个方面:Cache(缓存),Database(数据库),Vector Search(向量搜索)

截图来自于Redis Cloud

2、2024年的Redis是什么样的?

在2023年之前,Redis是一个纯粹的开源数据库。但是,在最近的这两年,Redis正在进行华丽的蜕变。从一个缓存产品变成一整套生态服务。

其中,Redis Cloud是一套云服务,基于AWS,Azure等公有云,提供了一整套完整的企业服务。 并提供了Redis Enterprise,企业级的收费产品服务。

Redis Insight是一套Redis服务的安装及管理套件。可以简单理解为是Redis官方推出的一个图形化客户端。以往使用Redis都需要寻找各种第三方的客户端,现在不用了。并且Redis Insight也可以在Redis Cloud上直接使用。

而在功能层面。目前已经形成了Redis OSS和Redis Stack两套服务体系。 其中Redis OSS就是以前常用的开源的服务体系。而Redis Stack可以认为是基于Redis OSS打造的一套更完整的技术栈。基于Redis Cloud提供服务,在Redis OSS功能的基础上,提供了很多高级的扩展功能。

二、Redis到底是单线程还是多线程?

这是Redis面试过程中最喜欢问的问题。几乎伴随着Redis的整个发展过程。

首先:整体来说,Redis的整体线程模型可以简单解释为 客户端多线程,服务端单线程

Redis为了能够与更多的客户端进行连接,还是使用的多线程来维护与客户端的Socket连接。在redis.conf中就有一个参数maxclients维护了最大的客户端连接数

# Redis is mostly single threaded, however there are certain threaded

# operations such as UNLINK, slow I/O accesses and other things that are

# performed on side threads.

#

# Now it is also possible to handle Redis clients socket reads and writes

# in different I/O threads. Since especially writing is so slow, normally

# Redis users use pipelining in order to speed up the Redis performances per

# core, and spawn multiple instances in order to scale more. Using I/O

# threads it is possible to easily speedup two times Redis without resorting

# to pipelining nor sharding of the instance.

#

# By default threading is disabled, we suggest enabling it only in machines

# that have at least 4 or more cores, leaving at least one spare core.

# Using more than 8 threads is unlikely to help much. We also recommend using

# threaded I/O only if you actually have performance problems, with Redis

# instances being able to use a quite big percentage of CPU time, otherwise

# there is no point in using this feature.

#

# So for instance if you have a four cores boxes, try to use 2 or 3 I/O

# threads, if you have a 8 cores, try to use 6 threads. In order to

# enable I/O threads use the following configuration directive:

#

# io-threads 4

# Set the max number of connected clients at the same time. By default

# this limit is set to 10000 clients, however if the Redis server is not

# able to configure the process file limit to allow for the specified limit

# the max number of allowed clients is set to the current file limit

# minus 32 (as Redis reserves a few file descriptors for internal uses).

#

# Once the limit is reached Redis will close all the new connections sending

# an error 'max number of clients reached'.

#

# IMPORTANT: When Redis Cluster is used, the max number of connections is also

# shared with the cluster bus: every node in the cluster will use two

# connections, one incoming and another outgoing. It is important to size the

# limit accordingly in case of very large clusters.

#

# maxclients 10000

但是,在服务端,Redis响应网络IO和键值对读写的请求,则是由一个单独的主线程完成的。Redis基于epoll实现了IO多路复用,这就可以用一个主线程同时响应多个客户端Socket连接的请求。

在这种线程模型下,Redis将客户端多个并发的请求转成了串行的执行方式。因此,在Redis中,完全不用考虑诸如MySQL的脏读、幻读、不可重复读之类的并发问题。并且,这种串行化的线程模型,加上Redis基于内存工作的极高性能,也让Redis成为很多并发问题的解决工具。

然后,严格来说,Redis后端的线程模型跟Redis的版本是有关系的。

Redis4.X以前的版本,都是采用的纯单线程。但是在2018年10月份,Redis5.x版本进行了一次大的核心代码重构。 到Redis6.x和7.x版本中,开始用一种全新的多线程机制来提升后台工作。尤其在现在的Redis7.x版本中,Redis后端的很多比较费时的操作,比如持久化RDB,AOF文件、unlink异步删除、集群数据同步等,都是由额外的线程执行的。例如,对于 FLUSHALL操作,就已经提供了异步的方式。

实际上,现代CPU早就是多核架构了,Redis如果一直使用单线程,就不能发挥多核CPU的性能优势,迟早是跟不上时代的。并且那些非常耗时的操作,也必然会对主线程产生影响。所以,多线程是一个必然结果。只不过,对于Redis来说,为了保持快速,多线程会走得非常谨慎。

Redis一直保持核心线程的单线程模型,其实是因为对于现代的Redis来说,CPU通常不会成为Redis的性能瓶颈。影响Redis的性能瓶颈大部分是内存和网络。因此,核心线程改为多线程的要求并不急切。另外,Redis的这种单线程为主的工作机制还可以减少线程上下文切换的性能消耗。而且,如果Redis将核心线程改为多线程并发执行,那么就必然带来资源竞争,反而会极大增加Redis的业务复杂性,影响Redis的业务执行效率。

三、Redis如何保证指令原子性

对于核心的读写键值的操作,Redis是单线程处理的。如果多个客户端同时进行读写请求,Redis只会排队串行。也就是说,针对单个客户端,Redis并没有类似MySQL的事务那样保证同一个客户端的操作原子性。像下面这种情况,返回的k1的值,就很难确定。

如何控制Redis指令的原子性呢?这是一系列的问题,在不同的业务场景下,Redis也提供了不同的思路。我们需要在项目中能够灵活选择。

1、复合指令

Redis内部提供了很多复合指令,他们是一个指令,可是明显干着多个指令的活。 比如 MSET(HMSET)、GETSET、SETNX、SETEX。这些复合指令都能很好的保持原子性。

2、Redis事务

像MySQL一样,Redis也提供了事务机制。

127.0.0.1:6379> help @transactions

  DISCARD (null)  -- 放弃事务
  summary: Discards a transaction.
  since: 2.0.0

  EXEC (null)  -- 执行事务
  summary: Executes all commands in a transaction.
  since: 1.2.0

  MULTI (null)  -- 开启事务
  summary: Starts a transaction.
  since: 1.2.0

  UNWATCH (null)  --去掉监听
  summary: Forgets about watched keys of a transaction.
  since: 2.2.0

  WATCH key [key ...]   --监听某一个key的变化。key有变化后,就执行当前事务
  summary: Monitors changes to keys to determine the execution of a transaction.
  since: 2.2.0

使用方式也很典型,开启事务后,接入一系列操作,然后根据执行情况选择是执行事务还是回滚事务。例如

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> EXEC  --执行事务
1) OK
2) (integer) 3
3) "3"
127.0.0.1:6379> DISCARD  -- 放弃事务

但是,这和数据库中的事务,是不是同一回事呢?看下面这个例子。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> lpop k2
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (integer) 3
3) "3"
4) (error) WRONGTYPE Operation against a key holding the wrong kind of value
5) (integer) 4
6) "4"

lpop指令是针对list的操作,现在针对string类型的k2操作,那么肯定会报错。但是,结果是这行错误的指令并没有让整个事务回滚,甚至后面的指令都没有受到影响。

从这里可以看到。Redis的事务并不是像数据库的事务那样,保证事务中的指令一起成功或者一起失败。Redis的事务作用,仅仅只是保证事务中的原子操作是一起执行,而不会在执行过程中被其他指令加塞。

实际上,在事务指令敲击的过程中可以看到, 开启事务后,所有操作的返回结果都是QUEUED,表示这些操作只是排好了队,等到EXEC后一起执行。

对于Redis事务的更多说明,参见官网Transactions | Docs

注意:redis官网发生了非常大的变化,所以具体地址以官网实际情况为准。

核心重点做几个总结:

1、Redis事务可以通过Watch机制进一步保证在某个事务执行前,某一个key不被修改。

注意:UNWATCH取消监听,只在当前客户端有效。比如下图。只有在左侧客户端步骤3之前执行UNWATCH才能让事务执行成功。在右侧客户端执行UNWATCH是不生效的。

2、Redis事务失败如何回滚

Redis中的事务回滚,不是回滚数据,而是回滚操作。

1》如果事务是在EXEC执行前失败(比如事务中的指令敲错了,或者指令的参数不对),那么整个事务的操作都不会执行。

2》如果事务是在EXEC执行之后失败(比如指令操作的key类型不对),那么事务中的其他操作都会正常执行,不受影响。

3、事务执行过程中出现失败了怎么办

1》只要客户端执行了EXEC指令,那么就算之后客户端的连接断开了,事务就会一直进行下去。

2》事务有可能造成数据不一致。当EXEC指令执行后,Redis会先将事务中的所有操作都先记录到AOF文件中,然后再执行具体的操作。这时有一种可能,Redis保存了AOF记录后,事务的操作在执行过程中,服务就出现了非正常宕机(服务崩溃了,或者执行进程被kill -9了)。这就会造成AOF中记录的操作,与数据不符合。如果Redis发现这种情况,那么在下次服务启动时,就会出现错误,无法正常启动。这时,就要使用redis-check-aof工具修复AOF文件,将这些不完整的事务操作记录移除掉。这样下次服务就可以正常启动了。

4、事务机制优缺点,什么时候用事务

是否使用Redis事务,取决于业务场景和适用场景,目前看一般公司用到Rdis事务的场景不多。

3、Pipeline

1、什么是管道

redis-cli指令天天用,你有没有试过用redis-cli --help指令看看这个客户端指令有哪些用法?其中就有两个不起眼的东东

  --pipe             Transfer raw Redis protocol from stdin to server.
  --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.
                     no reply is received within <n> seconds.
                     Default timeout: 30. Use 0 to wait forever.

2、使用案例

在Linux上编辑一个文件 command.txt。文件中可以包含一系列的指令

set count 1
incr count
incr count
incr count

然后在客户端执行redis-cli指令时,就可以直接执行这个文件中的指令。

[root@192-168-65-214 ~]# cat command.txt | redis-cli -a 123qweasd --pipe
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 4
[root@192-168-65-214 ~]# redis-cli -a 123qweasd
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> get count
"4"

3、有什么用

结论:如果你有大批量的数据需要快速写入到Redis中,这种方式可以一定程度提高执行效率

具体参考官网:Redis pipelining | Docs

核心作用:优化RTT(round-trip time)

RTT是什么鬼?

当客户端执行一个指令,数据包需要通过网络从Client传到Server,然后再从Server返回到Client。这个中间的时间消耗,就称为RTT(Rount Trip Time)。

可以看到,如果客户端的指令非常频繁,那么RTT消耗就会非常可观。有没有办法优化RTT呢?Redis提供了pipeline机制。其思路也比较简单明了。就是将客户端的多个指令打包,一起往服务端推送。

这种实现方式,就是pipeline。例如官网就给出了一个案例:

[root@192-168-65-214 ~]# printf "AUTH 123qweasd\r\nPING\r\nPING\r\nPING\r\n" | nc localhost 6379
+OK
+PONG
+PONG
+PONG

有没有尝试理解Redis客户端干了些什么事情?后面还会继续分析。

4、pipeline注意点

redis的原生复合指令和事务,都是原子性的。但是pipeline不具备原子性。pipeline只是将多条命令发送到服务端,最终还是可能会被其他客户端的指令加塞的,虽然这种概率通常比较小。所以在pipeline中通常不建议进行复杂的数据操作。同时,这也表明,执行复合指令和事务,会阻塞其他命令执行,而执行pipeline不会。

pipeline的执行需要客户端和服务端同时完成,pipeline在执行过程中,会阻塞当前客户端。在pipeline中不建议拼装过多的指令。因为指令过多,会使客户端阻塞时间太长,同时服务端需要回复这个很繁忙的客户端,占用很多内存。

总体来说,pipeline机制适合做一些在非热点时段进行的数据调整任务。

4、lua脚本

Redis的事务和Pipelinie机制,对于Redis的指令原子性问题,都有一定的帮助,但是,从之前的分析可以看到,这两个机制对于指令原子性问题都有水土不服的地方。并且,他们都只是对Redis现有指令进行拼凑,无法添加更多自定义的复杂逻辑。因此,企业中用到更多的是lua脚本。同时,也是Redis7版本着重调整的一个功能。

1、什么是lua?为什么Redis支持lua?

lua是一种小巧的脚本语言,他拥有很多高级语言的特性。比如参数类型、作用域、函数等。lua的语法非常简单,熟悉Java后基本上可以零门槛上手lua。

如果你对lua原生的语法感兴趣,推荐一个参考网站:LuatOS 文档 这个网站可以直接在线调试lua语法,简单轻松无压力。

注:Redis 7.x版本支持的lua语言是5.1版本。而这个网站支持的是5.3版本。

lua语言最大的特点是他的线程模型是单线程的模式。这使得lua天生就非常适合一些单线程模型的中间件。比如Redis,Nginx等都非常适合接入lua语言进行功能定制。所以,在Redis中执行一段lua脚本,天然就是原子性的。

2、Redis中如何执行lua?

Redis中对lua语言的API介绍参考官网:Redis Lua API reference | Docs

Redis中对lua支持从2.6.0版本就已经开始了。具体参考指令可以使用 help eval指令查看

127.0.0.1:6379> help eval

  EVAL script numkeys [key [key ...]] [arg [arg ...]]
  summary: Executes a server-side Lua script.
  since: 2.6.0
  group: scripting
  • script参数是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一 个Lua函数。
  • numkeys参数用于指定键名参数的个数。键名参数 key [key ...] 从EVAL的第三个参数开始算 起,表示在脚本中所用到的那些Redis键(key),这些键名参数可以在 Lua中通过全局变量KEYS数组,用1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • 在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在Lua中通过全局变量ARGV数组访问, 访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

例如

127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

在lua脚本中,可以使用redis.call函数来调用Redis的命令。

127.0.0.1:6379> set stock_1 1
OK
-- 调整1号商品的库存。如果库存小于10,就设置为10
127.0.0.1:6379> eval "local initcount = redis.call('get', KEYS[1]) local a = tonumber(initcount) local b = tonumber(ARGV[1]) if a >= b then redis.call('set', KEYS[1], a) return 1 end redis.call('set', KEYS[1], b) return 0 " 1 "stock_1" 10
(integer) 0
127.0.0.1:6379> get stock_1
"10"

注:注意其中keys和args是如何传参的。

3、使用lua注意点

1》不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令。相比之下,管道pipeline不会阻塞redis。

Redis中有一个配置参数来控制Lua脚本的最长控制时间。默认5秒钟。当lua脚本执行时间超过了这个时长,Redis会对其他操作返回一个BUSY错误,而不会一直阻塞。

################ NON-DETERMINISTIC LONG BLOCKING COMMANDS #####################

# Maximum time in milliseconds for EVAL scripts, functions and in some cases
# modules' commands before Redis can start processing or rejecting other clients.
#
# If the maximum execution time is reached Redis will start to reply to most
# commands with a BUSY error.
#
# In this state Redis will only allow a handful of commands to be executed.
# For instance, SCRIPT KILL, FUNCTION KILL, SHUTDOWN NOSAVE and possibly some
# module specific 'allow-busy' commands.
#
# SCRIPT KILL and FUNCTION KILL will only be able to stop a script that did not
# yet call any write commands, so SHUTDOWN NOSAVE may be the only way to stop
# the server in the case a write command was already issued by the script when
# the user doesn't want to wait for the natural termination of the script.
#
# The default is 5 seconds. It is possible to set it to 0 or a negative value
# to disable this mechanism (uninterrupted execution). Note that in the past
# this config had a different name, which is now an alias, so both of these do
# the same:
# lua-time-limit 5000
# busy-reply-threshold 5000

2》尽量使用只读脚本

只读脚本是Redis7中新增的一种脚本执行方法,表示那些不修改Redis数据集的只读脚本。需要在脚本上加上一个只读的标志,并通过指令EVAL_RO触发。在只读脚本中不允许执行任何修改数据集的操作,并且可以随时使用SCRIPT_KILL指令停止。

使用只读脚本的好处一方面在于可以限制某些用户的操作。另一方面,这些只读脚本通常都可以转移到备份节点执行,从而减轻Redis的压力。

3》热点脚本可以缓存到服务端

redis> SCRIPT LOAD "return 'Immabe a cached script'"
"c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f"
redis> EVALSHA c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f 0
"Immabe a cached script"

5、Redis Function

1、什么是Function

如果你觉得开发lua脚本有困难,那么在Redis7之后,提供了另外一种让程序员解脱的方法-Redis Function。

Redis Function允许将一些功能声明成一个统一的函数,提前加载到Redis服务端(可以由熟悉Redis的管理员加载)。客户端可以直接调用这些函数,而不需要再去开发函数的具体实现。

Redis Function更大的好处在于在Function中可以嵌套调用其他Function,从而更有利于代码复用。相比之下,lua脚本就无法进行复用。

2、Function案例

例如,可以在服务器上新增一个mylib.lua文件。在文件中定义函数。

#!lua name=mylib

local function my_hset(keys, args)
  local hash = keys[1]
  local time = redis.call('TIME')[1]
  return redis.call('HSET', hash, '_last_modified_', time, unpack(args))
end

redis.register_function('my_hset', my_hset)

注意,脚本第一行是指定函数的命名空间,不是注释,不能少!!!

然后,就可以使用Redis客户端,将这个函数加载到Redis中。

[root@192-168-65-214 myredis]# cat mylib.lua | redis-cli -a 123qweasd -x FUNCTION LOAD REPLACE
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"mylib"

这样,在Redis中,其他客户端就可以直接调用这个函数。函数的调用以及传参方式,跟lua脚本是一样的。

127.0.0.1:6379> FUNCTION LIST
1) 1) "library_name"
   2) "mylib"
   3) "engine"
   4) "LUA"
   5) "functions"
   6) 1) 1) "name"
         2) "my_hset"
         3) "description"
         4) (nil)
         5) "flags"
         6) (empty array)
127.0.0.1:6379> FCALL my_hset 1 myhash myfield "some value" another_field "another value"
(integer) 3
127.0.0.1:6379> HGETALL myhash
1) "_last_modified_"
2) "1717748001"
3) "myfield"
4) "some value"
5) "another_field"
6) "another value"

3、Function注意点

1》Function同样也可以进行只读调用。

2》如果在集群中使用Function,目前版本需要在各个节点都手动加载一次。Redis不会在集群中进行Function同步

3》Function是要在服务端缓存的,所以不建议使用太多太大的Function。

4》Function和Script一样,也有一系列的管理指令。使用指令 help @scripting 自行了解。

6、Redis指令原子性总结

以上介绍的各种机制,其实都是Redis改变指令执行顺序的方式。在这几种工具中,Lua脚本通常会是项目中用得最多的方式。在很多追求极致性能的高并发场景,Lua脚本都会担任很重要的角色。但是其他的各种方式你也需要有了解,这样面临真实业务场景,你才有更多的方案可以选择。

四、Redis中的Bigkey问题

Bigkey指那些占用空间非常大的key。比如一个list中包含200W个元素,或者一个string里放一篇文章。基于Redis的单线程为主的核心工作机制,这些Bigkey非常容易造成Redis的服务阻塞。因此在实际项目中,一定需要特殊关照。

在Redis客户端指令中,提供了两个扩展参数,可以帮助快速发现这些BigKey

[root@192-168-65-214 myredis]# redis-cli --help
...
 --bigkeys          Sample Redis keys looking for keys with many elements (complexity).
 --memkeys          Sample Redis keys looking for keys consuming a lot of memory.

关于BigKey的处理,在后面课程中继续深入介绍。

五、Redis线程模型总结

Redis的线程模型整体还是多线程的,只是后台执行指令的核心线程是单线程的。整个线程模型可以理解为还是以单线程为主。基于这种单线程为主的线程模型,不同客户端的各种指令都需要依次排队执行。

Redis这种以单线程为主的线程模型,相比其他中间件,还是非常简单的。这使得Redis处理线程并发问题,要简单高效很多。甚至在很多复杂业务场景下,Redis都是用来进行线程并发控制的很好的工具。但是,这并不意味着Redis就没有线程并发的问题。这时候选择合理的指令执行方式,就非常重要了。

另外,Redis这种比较简单的线程模型其实本身是不利于发挥多线程的并发优势的。而且Redis的应用场景又通常与高性能深度绑定在一起,所以,在使用Redis的时候,还是要时刻思考Redis的这些指令执行方式,这样才能最大限度发挥Redis高性能的优势。

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

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

相关文章

机器学习实战:泰坦尼克号乘客生存率预测(数据处理+特征工程+建模预测)

项目描述 任务&#xff1a;根据训练集数据中的数据预测泰坦尼克号上哪些乘客能生存下来 数据源&#xff1a;csv文件&#xff08;train.csv&#xff09; 目标变量&#xff1a;Survived&#xff08;0-1变量&#xff09; 数据集预览&#xff1a; 1、英文描述&#xff1a; 2、…

人工智能之数学基础:欧式距离及在人工智能领域中的应用

本文重点 欧式距离,也称为欧几里得距离,是数学中用于衡量多维空间中两点之间绝对距离的一种基本方法。这一概念最早由古希腊数学家欧几里得提出,并以其名字命名。欧式距离的计算基于勾股定理,即在一个直角三角形中,斜边的平方等于两直角边的平方和。在多维空间中,欧式距…

logminer挖掘日志归档查找问题

--根据发生问题时间点查找归档文件 select first_time,NAME from gv$archived_log where first_time>2016-03-15 17:00:00 and first_time<2016-03-15 21:00:00; 2016-03-15 17:23:55 ARCH/jxdb/archivelog/2016_03_15/thread_1_seq_41588.4060.906577337 2016-03-15 17:…

洛谷 P1747 好奇怪的游戏 C语言 bfs

题目&#xff1a; https://www.luogu.com.cn/problem/P1747#submit 题目描述 爱与愁大神坐在公交车上无聊&#xff0c;于是玩起了手机。一款奇怪的游戏进入了爱与愁大神的眼帘&#xff1a;***&#xff08;游戏名被打上了马赛克&#xff09;。这个游戏类似象棋&#xff0c;但…

【c++篇】:解读Set和Map的封装原理--编程中的数据结构优化秘籍

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;c篇–CSDN博客 文章目录 前言一.set和map的初步封装1.树的节点封装修改2.Find()查找函数3.红…

字符型注入‘)闭合

前言 进行sql注入的时候&#xff0c;不要忘记闭合&#xff0c;先闭合再去获取数据 步骤 判断是字符型注入 用order by获取不了显位&#xff0c;select也一样 是因为它是’)闭合&#xff0c;闭合之后&#xff0c;就可以获取数据了 最后就是一样的步骤

电脑启动需要经历哪些过程?

传统BIOS启动流程 1. BIOS BIOS 启动&#xff0c;BIOS程序是烧进主板自带的ROM里的&#xff0c;所以无硬盘也可以启动。BIOS先进行自检&#xff0c;检查内存、显卡、磁盘等关键设备是否存在功能异常&#xff0c;会有蜂鸣器汇报错误&#xff0c;无错误自检飞快结束。 硬件自检…

PYNQ 框架 - OV5640驱动 + Linux 驱动分析

目录 1. 简介 1.1 博文要点 1.2 V4L2 2. 极简 Char 驱动 2.1 源码 2.2 Makefile 2.3 加载驱动 2.4 设备文件 2.5 测试驱动程序 2.6 卸载驱动程序 2.7 自动创建设备文件 2.8 日志等级 3. 极简 V4L2 驱动 3.1 源码 3.2 Makefile 3.3 设备节点类型 3.4 测试 V4L2…

微信小程序Webview与H5通信

背景 近期有个微信小程序需要用到web-view嵌套H5的场景&#xff0c;该应用场景需要小程序中频繁传递数据到H5进行渲染&#xff0c;且需要保证页面不刷新。 由于微信小程序与H5之间的通信限制比较大&#xff0c;显然无法满足于我的业务场景 探索 由于微信小程序与webview的环境是…

【maven-4】IDEA 配置本地 Maven 及如何使用 Maven 创建 Java 工程

IntelliJ IDEA&#xff08;以下简称 IDEA&#xff09;是一款功能强大的集成开发环境&#xff0c;广泛应用于 Java 开发。下面将详细介绍如何在 IDEA 中配置本地 Maven&#xff0c;并创建一个 Maven Java 工程&#xff0c;快速上手并高效使用 Maven 进行 Java 开发。 1. Maven …

交通流量预测:基于交通流量数据建立模型

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【故障处理系列--移动云云盘根目录在线扩容】

移动云云盘根目录扩容 **目的&#xff1a;**测试harbor仓库服务器的根目录能否在线扩容 1、移动云控制台系统盘扩容 这里以我自己的虚拟机演示 2、查看分区的文件类型 3、安装growpart工具 rootgitlab-cli:~# apt install cloud-guest-utils -y rootgitlab-cli:~# apt ins…

不可分割的整体—系统思考的微妙法则

不可分割的整体——系统思考的微妙法则 作为企业领导者&#xff0c;我们经常需要做出决策&#xff0c;但有时候&#xff0c;我们会忽略一个事实&#xff1a;每个决策都不是孤立的&#xff0c;它背后都是一个复杂系统的一部分。 无论是市场动态、团队协作&#xff0c;还是产品…

内核模块里获取当前进程和父进程的cmdline的方法及注意事项,涉及父子进程管理,和rcu的初步介绍

一、背景 在编写内核态系统监控代码时&#xff0c;有时候为了调试的便捷性&#xff0c;不仅要拿到异常事件有关的线程id&#xff0c;进程id和父进程id&#xff0c;还需要拿到当前进程和父进程的comm和cmdline。主要有几下几个原因&#xff1a; 1&#xff09;单纯的pid或者tgi…

京东OPPO定制版 12.1.0 | 安装包只有4.7M,无短视频,适合轻度使用

底部四个标签&#xff0c;没有短视频&#xff0c;可以正常登录。安装包体积很小&#xff0c;功能基本可用&#xff0c;特别适合追求简洁界面和轻度使用的用户。 大小&#xff1a;4.7M 下载地址&#xff1a; 百度网盘&#xff1a;https://pan.baidu.com/s/1lD0o1y9X3s4hRiz-8F…

AOSP的同步问题

repo sync同步时提示出错: error: .repo/manifests/: contains uncommitted changesRepo command failed due to the following UpdateManifestError errors: contains uncommitted changes解决方法&#xff1a; 1、cd 进入.repo/manifests cd .repo/manifests2、执行如下三…

Redis【1】- 如何阅读Redis 源码

1 Redis 的简介 Redis 实际上是简称&#xff0c;全称为 Remote Dictionary Server (远程字典服务器)&#xff0c;由 Salvatore Sanfilippo 写的高性能 key-value 存储系统&#xff0c;其完全开源免费&#xff0c;遵守 BSD 协议。Redis 与其他 key-value 缓存产品&#xff08;如…

鸿蒙修饰符

文章目录 一、引言1.1 什么是修饰符1.2 修饰符在鸿蒙开发中的重要性1.3 修饰符的作用机制 二、UI装饰类修饰符2.1 Styles修饰符2.1.1 基本概念和使用场景2.1.2 使用示例2.1.3 最佳实践 2.2 Extend修饰符2.2.1 基本概念2.2.2 使用示例2.2.3 Extend vs Styles 对比2.2.4 使用建议…

nginx安装和负载均衡

1. nginx安装 &#xff08;1&#xff09;安装依赖项&#xff1a; yum -y install gcc gcc-c make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel&#xff08;2&#xff09;下载Nginx源代码&#xff1a; http://nginx.org/en/download.html https://nginx.o…

部署 Prometheus

实验环境 IP地址服务192.168.88.10Prometheus服务端, Consul, Grafana, Node-Exporter192.168.88.77MySQL, Node-Exporter192.168.88.30Nginx&#xff0c;Node-Exporter 一、Prometheus Server 端安装和相关配置 【Prometheus1.sh】 &#xff08;1&#xff09;上传 prometh…