第7章 Redis的噩梦:阻塞

文章目录

  • 前言
  • 1 发现阻塞
  • 2.内在原因
    • 2.1API或数据结构使用不合理
      • 2.1.1如何发现慢查询
      • 2.1.2.如何发现大对象
    • 2.2 CPU饱和
    • 2.3 持久化阻塞
      • 2.3.1fork阻塞
      • 2.3.2.AOF刷盘阻塞
      • 2.3.3.HugePage写操作阻塞
  • 3 外在原因
    • 3.1CPU竞争
    • 3.2 内存交换
    • 3.3网络问题
      • 3.3.1连接拒绝


前言

Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成的。当Redis用于高并发场景时,如果出现阻塞,哪怕是很短时间,对于我们的应用来说都是噩梦。导致阻塞问题的场景大致分为内在原因和外在原因:

  • 内在原因包括:不合理地使用API或数据结构、CPU饱和、持久化阻塞等。
  • 外在原因包括:CPU竞争、内存交换、网络问题等。

1 发现阻塞

当Redis阻塞时,线上应用服务应该最先感知到,这时应用方会收到大量Redis超时异常,比如Jedis客户端会抛出JedisConnectionException异常。常见的做法是在应用方加入异常统计并通过邮件/短信/微信报警,以便及时发现通知问题。开发人员需要处理如何统计异常以及触发报警的时机。何时触发报警一般根据应用的并发量决定,如1分钟内超过10个异常触发报警。在实现异常统计时要注意,由于Redis调用API会分散在项目的多个地方,每个地方都监听异常并加入监控代码必然难以维护。这时可以借助于日志系统,如Java语言可以使用logback或log4j。当异常发生时,异常信息最终会被日志系统收集到Appender(输出目的地),默认的Appender一般是具体的日志文件,开发人员可以自定义一个Appender,用于专门统计异常和触发报警逻辑
在这里插入图片描述
以Java的logback为例,实现代码如下:

public class Redis Appender extends AppenderBase<ILoggingEvent> {
	// 使用guava的AtomicLongMap,用于并发计数
	public static final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create();
	static {
		// 自定义Appender加入到logback的rootLogger中
		LoggerContext loggerContext = (LoggerContext) 
		LoggerFactory.getILoggerFactory();
		Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
		ErrorStatisticsAppender errorStatisticsAppender = new 
		ErrorStatisticsAppender();
		errorStatisticsAppender.setContext(loggerContext);
		errorStatisticsAppender.start();
		rootLogger.addAppender(errorStatisticsAppender);
	}
	// 重写接收日志事件方法
	protected void append(ILoggingEvent event) {
		// 只监控error级别日志
		if (event.getLevel() == Level.ERROR) {
		IThrowableProxy throwableProxy = event.getThrowableProxy();
		// 确认抛出异常
			if (throwableProxy != null) {
				// 以每分钟为key,记录每分钟异常数量
				String key = DateUtil.formatDate(new Date(), "yyyyMMddHHmm");
				long errorCount = ATOMIC_LONG_MAP.incrementAndGet(key);
					if (errorCount > 10) {
						// 超过10次触发报警代码
					}
					// 清理历史计数统计,防止极端情况下内存泄露
					for (String oldKey : ATOMIC_LONG_MAP.asMap().keySet()) {
						if (!StringUtils.equals(key, oldKey)) {
						ATOMIC_LONG_MAP.remove(oldKey);
						}
					}
			}
		}
	}
}

开发提示:
借助日志系统统计异常的前提是,需要项目必须使用日志API进行异常统一输出,比如所有的异常都通过logger.error打印,这应该作为开发规范推广。其他编程语言也可以采用类似的日志系统实现异常统计报警。
应用方加入异常监控之后还存在一个问题,如果应用操作的是多个Redis节点(比如使用Redis集群),如何决定是哪一个节点超时还是所有的节点都有超时呢?这是线上很常见的需求,但绝大多数的客户端类库并没有在异常信息中打印ip和port信息,导致无法快速定位是哪个Redis节点超时。不过修改Redis客户端成本很低,比如Jedis只需要修改Connection类下的connect、sendCommand、readProtocolWithCheckingBroken方法专门捕获连接,发送命令,协议读取事件的异常。由于客户端类库都会保存ip和port信息,当异常发生时很容易打印出对应节点的ip和port,辅助我们快速定位问题节点。
除了在应用方加入统计报警逻辑之外,还可以借助Redis监控系统发现阻塞问题,当监控系统检测到Redis运行期的一些关键指标出现不正常时会触发报警。Redis相关的监控系统开源的方案有很多,一些公司内部也会自己开发监控系统。一个可靠的Redis监控系统首先需要做到对关键指标全方位监控和异常识别,辅助开发运维人员发现定位问题。如果Redis服务没有引入监控系统作辅助支撑,对于线上的服务是非常不负责任和危险的。这里推荐笔者团队开源的CacheCloud系统,它内部的统计监控模块能够很好地辅助工程师发现定位问题。
监控系统所监控的关键指标有很多,如命令耗时、慢查询、持久化阻塞、连接拒绝、CPU/内存/网络/磁盘使用过载等。当出现阻塞时如果相关人员不能深刻理解这些关键指标的含义和背后的原理,会严重影响解决问题的速度。

2.内在原因

Redis自身原因主要围绕以下几个方面排查:

  • API或数据结构使用不合理。
  • CPU饱和的问题。
  • 持久化相关的阻塞。

2.1API或数据结构使用不合理

高并发的场景应该尽量避免在大对象上执行算法复杂度超过O(n)的命令。这是典型的不合理使用API和数据结构。如对一个包含上万个元素的hash结构执行hgetall操作。数据量比较大且命令算法复杂度是O(n),这条命令执行速度必然很慢。

2.1.1如何发现慢查询

Redis原生提供慢查询统计功能,slowlog len 获取慢查询的条数,执行slowlog get{n}命令可以获取最近的n条慢查询命令。慢查询本身只记录了命令执行时间,不包括数据网络传输时间和命令排队时间,因此客户端发生阻塞异常后,可能不是当前命令缓慢,而是在等待其他命令执行。需要重点比对异常和慢查询发生的时间点,确认是否有慢查询造成的命令阻塞排队。
发现慢查询后,开发人员需要作出及时调整。可以按照以下两个方向去调整:
1)修改为低算法度的命令,如hgetall改为hmget等,禁用keys、sort等命令。
2)调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。大对象拆分过程需要视具体的业务决定,如用户好友集合存储在Redis中,有些热点用户会关注大量好友,这时可以按时间或其他维度拆分到多个集合中。

2.1.2.如何发现大对象

Redis本身提供发现大对象的工具,对应命令:redis-cli-h{ip}-p{port}bigkeys。内部原理采用分段进行scan操作,把历史扫描过的最大对象统计出来便于分析优化,运行效果如下:

root@iZ2zec3etasicvrmg7svj0Z:/usr/local/bin# redis-cli -a password --bigkeys
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest hash   found so far '"user1"' with 2 fields
[00.00%] Biggest string found so far '"user:99:ratio"' with 3 bytes
[00.00%] Biggest list   found so far '"listkeyl"' with 6 items
[00.00%] Biggest string found so far '"k1"' with 11 bytes
[00.00%] Biggest zset   found so far '"cities:locations"' with 3 members
[00.00%] Biggest string found so far '"mybitmap"' with 13 bytes
[09.80%] Biggest string found so far '"pro"' with 39 bytes
[09.80%] Biggest zset   found so far '"topn"' with 5 members
[09.80%] Biggest set    found so far '"sk1"' with 3 members
[19.61%] Biggest hash   found so far '"uset:ztt"' with 3 fields
[29.41%] Biggest set    found so far '"myset"' with 21 members
[39.22%] Biggest string found so far '"users"' with 1261 bytes
[50.00%] Biggest hash   found so far '"user"' with 4 fields
[60.78%] Biggest zset   found so far '"myzset"' with 6 members
[60.78%] Biggest hash   found so far '"myhash"' with 6 fields
[70.59%] Biggest zset   found so far '"user:ranking:1_inter_2"' with 7 members
[81.37%] Biggest string found so far '"2016_05_01:unique:ids"' with 12304 bytes

-------- summary -------

Sampled 102 keys in the keyspace!
Total key length in bytes is 1027 (avg len 10.07)

Biggest   list found '"listkeyl"' has 6 items
Biggest   hash found '"myhash"' has 6 fields
Biggest string found '"2016_05_01:unique:ids"' has 12304 bytes
Biggest    set found '"myset"' has 21 members
Biggest   zset found '"user:ranking:1_inter_2"' has 7 members

14 lists with 50 items (13.73% of keys, avg size 3.57)
7 hashs with 23 fields (06.86% of keys, avg size 3.29)
57 strings with 13963 bytes (55.88% of keys, avg size 244.96)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
12 sets with 51 members (11.76% of keys, avg size 4.25)
12 zsets with 44 members (11.76% of keys, avg size 3.67)

根据结果汇总信息能非常方便地获取到大对象的键,以及不同类型数据结构的使用情况。

2.2 CPU饱和

单线程的Redis处理命令时只能使用一个CPU。而CPU饱和是指Redis把单核CPU使用率跑到接近100%。使用top命令很容易识别出对应Redis进程的CPU使用率。CPU饱和是非常危险的,将导致Redis无法处理更多的命令,严重影响吞吐量和应用方的稳定性。对于这种情况,首先判断当前Redis的并发量是否达到极限,建议使用统计命令redis-cli-h{ip}-p{port}–stat获取当前Redis使用情况,该命令每秒输出一行统计信息,运行效果如下:

# redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
3789785 3.20G 507 0 8867955607 (+0) 555894
3789813 3.20G 507 0 8867959511 (+63904) 555894
3789822 3.20G 507 0 8867961602 (+62091) 555894
3789831 3.20G 507 0 8867965049 (+63447) 555894
3789842 3.20G 507 0 8867969520 (+62675) 555894
3789845 3.20G 507 0 8867971943 (+62423) 555894

以上输出是一个接近饱和的Redis实例的统计信息,它每秒平均处理6万+的请求。对于这种情况,垂直层面的命令优化很难达到效果,这时就需要做集群化水平扩展来分摊OPS压力。如果只有几百或几千OPS的Redis实例就接近CPU饱和是很不正常的,有可能使用了高算法复杂度的命令。还有一种
情况是过度的内存优化,这种情况有些隐蔽,需要我们根据info commandstats统计信息分析出命令不合理开销时间,例如下面的耗时统计:

cmdstat_hset:calls=198757512,usec=27021957243,usec_per_call=135.95

查看这个统计可以发现一个问题,hset命令算法复杂度只有O(1)但平均耗时却达到135微秒,显然不合理,正常情况耗时应该在10微秒以下。这是因为上面的Redis实例为了追求低内存使用量,过度放宽ziplist使用条件(修改了hash-max-ziplist-entries和hash-max-ziplist-value配置)。进程内的
hash对象平均存储着上万个元素,而针对ziplist的操作算法复杂度在O(n)到O(n2)之间。虽然采用ziplist编码后hash结构内存占用会变小,但是操作变得更慢且更消耗CPU。ziplist压缩编码是Redis用来平衡空间和效率的优化手段,不可过度使用。

2.3 持久化阻塞

对于开启了持久化功能的Redis节点,需要排查是否是持久化导致的阻塞。持久化引起主线程阻塞的操作主要有:fork阻塞、AOF刷盘阻塞、HugePage写操作阻塞。

2.3.1fork阻塞

fork操作发生在RDB和AOF重写时,Redis主线程调用fork操作产生共享内存的子进程,由子进程完成持久化文件重写工作。如果fork操作本身耗时过长,必然会导致主线程的阻塞。
可以执行info stats命令获取到latest_fork_usec指标,表示Redis最近一次fork操作耗时,如果耗时很大,比如超过1秒,则需要做出优化调整,如避免使用过大的内存实例和规避fork缓慢的操作系统等。

2.3.2.AOF刷盘阻塞

当我们开启AOF持久化功能时,文件刷盘的方式一般采用每秒一次,后台线程每秒对AOF文件做fsync操作。当硬盘压力过大时,fsync操作需要等待,直到写入完成。如果主线程发现距离上一次的fsync成功超过2秒,为了数据安全性它会阻塞直到后台线程执行fsync操作完成。这种阻塞行为主要是硬盘压力引起,可以查看Redis日志识别出这种情况,当发生这种阻塞行为时,会打印如下日志:

Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF
buffer without waiting for fsync to complete, this may slow down Redis.

也可以查看info persistence统计中的aof_delayed_fsync指标,每次发生fdatasync阻塞主线程时会累加。
运维提示:硬盘压力可能是Redis进程引起的,也可能是其他进程引起的,可以使用iotop查看具体是哪个进程消耗过多的硬盘资源。

2.3.3.HugePage写操作阻塞

子进程在执行重写期间利用Linux写时复制技术降低内存开销,因此只有写操作时Redis才复制要修改的内存页。对于开启Transparent HugePages的操作系统,每次写命令引起的复制内存页单位由4K变为2MB,放大了512倍,会拖慢写操作的执行时间,导致大量写操作慢查询。例如简单的incr命令也会出现在慢查询中。

3 外在原因

外在原因围绕以下三个方面进行排查:

  • CPU竞争
  • 内存交换
  • 网络问题

3.1CPU竞争

CPU竞争问题如下:

  • 进程竞争:Redis是典型的CPU密集型应用,不建议和其他多核CPU密集型服务部署在一起。当其他进程过度消耗CPU时,将严重影响Redis吞吐量。可以通过top、sar等命令定位到CPU消耗的时间点和具体进程,这个问题比较容易发现,需要调整服务之间部署结构。
  • 绑定CPU:部署Redis时为了充分利用多核CPU,通常一台机器部署多个实例。常见的一种优化是把Redis进程绑定到CPU上,用于降低CPU频繁上下文切换的开销。这个优化技巧正常情况下没有问题,但是存在例外情况,如下图所示
    在这里插入图片描述
    当Redis父进程创建子进程进行RDB/AOF重写时,如果做了CPU绑定,会与父进程共享使用一个CPU。子进程重写时对单核CPU使用率通常在90%以上,父进程与子进程将产生激烈CPU竞争,极大影响Redis稳定性。因此对于开启了持久化或参与复制的主节点不建议绑定CPU。

3.2 内存交换

内存交换(swap)对于Redis来说是非常致命的,Redis保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的Redis性能急剧下降。识别Redis内存交换的检查方法如下:
1)查询Redis进程号:

# redis-cli -p 6383 -a password info server | grep process_id
process_id:4476

2)根据进程号查询内存交换信息:

# cat /proc/4476/smaps | grep Swap
Swap: 0 kB
Swap: 0 kB
Swap: 4 kB
Swap: 0 kB
Swap: 0 kB
.....

如果交换量都是0KB或者个别的是4KB,则是正常现象,说明Redis进程内存没有被交换。预防内存交换的方法有:

  • 保证机器充足的可用内存。
  • 确保所有Redis实例设置最大可用内存(maxmemory),防止极端情况下Redis内存不可控的增长。
  • 降低系统使用swap优先级,如echo10>/proc/sys/vm/swappiness,具体细节见12.1节“Linux配置优化”。

3.3网络问题

网络问题经常是引起Redis阻塞的问题点。常见的网络问题主要有:连接拒绝、网络延迟、网卡软中断等。

3.3.1连接拒绝

当出现网络闪断或者连接数溢出时,客户端会出现无法连接Redis的情况。我们需要区分这三种情况:网络闪断、Redis连接拒绝、连接溢出。
第一种情况:网络闪断:一般发生在网络割接或者带宽耗尽的情况,对于网络闪断的识别比较困难,常见的做法可以通过sar-n DEV查看本机历史流量是否正常,或者借助外部系统监控工具(如Ganglia)进行识别。具体问题定位需要更上层的运维支持,对于重要的Redis服务需要充分考虑部署架构的优化,尽量避免客户端与Redis之间异地跨机房调用。
第二种情况:Redis连接拒绝:Redis通过maxclients参数控制客户端最大连接数,默认10000。当Redis连接数大于maxclients时会拒绝新的连接进入,info stats的rejected_connections统计指标记录所有被拒绝连接的数量:

# redis-cli -p 6384 info Stats | grep rejected_connections
rejected_connections:0

Redis使用多路复用IO模型可支撑大量连接,但是不代表可以无限连接。客户端访问Redis时尽量采用NIO长连接或者连接池的方式。
开发提示:当Redis用于大量分布式节点访问且生命周期比较短的场景时,如比较典型的在Map/Reduce中使用Redis。因为客户端服务存在频繁启动和销毁的情况且默认Redis不会主动关闭长时间闲置连接或检查关闭无效的TCP连接,因此会导致Redis连接数快速消耗且无法释放的问题。这种场景下建议设置tcp-keepalive和timeout参数让Redis主动检查和关闭无效连接。
第三种情况:连接溢出。这是指操作系统或者Redis客户端在连接时的问题。这个问题的原因比较多,下面就分别介绍两种原因:进程限制、backlog队列溢出。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Altium】AD-PCB界面抬头显示设置

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 PCB设计界面中抬头显示设置的方法 2、 问题场景 PCB设计界面中左上角有一个抬头显示悬浮窗口&#xff0c;这个窗口可以显示坐标&#xff0c;选中PCB中某个对象后还能显示它的具体信息。有用户不喜欢这个窗口&#x…

漆包线行业生产管理革新:万界星空科技MES系统解决方案

一、引言 在科技日新月异的今天&#xff0c;万界星空科技凭借其在智能制造领域的深厚积累&#xff0c;为漆包线行业量身打造了一套先进的生产管理执行系统&#xff08;MES&#xff09;解决方案。随着市场竞争的加剧&#xff0c;漆包线作为电气设备的核心材料&#xff0c;其生产…

Android手机删除的照片怎么找回来?3个详细教程,有效恢复

在这个充满科技魅力的时代&#xff0c;手机它不仅是我们的通讯工具&#xff0c;更是我们的娱乐中心、信息库和摄影工作室。然而&#xff0c;有时候&#xff0c;我们在享受科技带来的便利的同时&#xff0c;也会遭遇一些小小的困扰。 比如&#xff0c;你可能会在一次手抖中删除…

AI需求强劲推动韩国六月芯片出口创历史新高

据路透社7月1日报道&#xff0c;韩国产业通商资源部最新数据显示&#xff0c;韩国出口连续第九个月增长&#xff0c;六月份海外对芯片的持续且增强的需求将芯片出口额推至历史高位。这一出口增长势头同时带动韩国制造业采购经理指数&#xff08;PMI&#xff09;攀升至两年多以来…

《Linux开发笔记》C语言编译

C语言编译过程 编译过程主要分为四步&#xff1a;预处理、编译、汇编、链接 预处理&#xff1a;主要用于查找头文件、展开宏 编译&#xff1a;把.i文件编译成.s文件 汇编&#xff1a;把.s文件汇编为.o文件 链接&#xff1a;把多个.o文件链接成一个app 以上四个步骤主要由3个命…

java 程序、进程 、线程,cpu,并行、并发、启动线程两种方式

1、重写 Thread 父类方法 后创建实例调用 start 方法 2、将创建自实现 Runable 接口后的实例 作为参数传递给 Thread 的构造方法 两个条件同时存在&#xff0c;那个生效&#xff1f; new Thread(/* condition 1 */threadTest2) {Override/* condition 2 */public void run() {T…

【计算机毕业设计】基于Springboot的大学生就业招聘系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

绝区零 Mac 下载安装详细教程(MacOS IPA 砸壳包 playCover 完美运行)

绝区零 7.4 号开始公测&#xff0c;但刚刚就可以开始下载了&#xff0c;我也是第一时间就迫不及待的安装到了我的 Mac 电脑上&#xff0c;感兴趣的朋友可以跟我一起安装试试 我这里是通过 playCover 的形式在 Mac 上安装运行的&#xff0c;根据之前原神的经验所以这次还是同样…

CTFHUB-SSRF-Redis协议

本题需要用到&#xff1a; 在线编码网址&#xff1a;https://icyberchef.com/ gopherus工具&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/139440201 开启题目&#xff0c;页面空白 和上一个题FastCGI协议一样&#xff0c;还是使用gopherus攻击redis ./gopheru…

H616连接摄像头

&#xff08;1&#xff09;首先将USB摄像头连接到OrangePi开发板的USB接口 &#xff08;2&#xff09;通过lsmod命令查看是否加载了uvcvideo模块 lsmod | grep video &#xff08;3&#xff09;通过 v4l2-ctl 命令可以看到 USB 摄像头的设备节点信息为/dev/video0 sudo apt u…

数字化精益生产系统--SRM供应商关系管理

SRM供应商关系管理&#xff0c;全称为Supplier Relationship Management&#xff08;供应商关系管理&#xff09;系统&#xff0c;是一种专门用于管理采购供应链和供应商关系的软件系统。该系统通过集成各个环节的采购活动&#xff0c;帮助企业实现采购流程的自动化、标准化和优…

【数智化案例展】苏商银行——全场景数据统一极速多维即席分析底座建设

‍ 镜舟科技案例 本项目案例由镜舟科技投递并参与数据猿与上海大数据联盟联合推出的《2024中国数智化转型升级创新服务企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 在 2017 年建行之初&#xff0c;江苏苏商银行股份有限公司&#xff08;以下简称“…

【软件测试】单元测试、系统测试、集成测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试&#xff0c;如函数或一个类的方法…

解析Kotlin中的内联函数,inline、noinline、crossinline【笔记摘要】

用编译时常量的概念&#xff0c;引出本文要讲内联函数inline&#xff1a; 1.编译时常量 Java的编译时常量 Compile-time Constant 它有四个要求&#xff1a;1.这个变量需要是 final 的  2.类型只能是字符串或者基本类型  3.这个变量需要在声明的时候就赋值  4.等号右边…

西部智慧健身小程序+华为运动健康服务

1、 应用介绍 西部智慧健身小程序为用户提供一站式全流程科学健身综合服务。用户通过登录微信小程序&#xff0c;可享用健康筛查、运动风险评估、体质检测评估、运动处方推送、个人运动数据监控与评估等公益服务。 2、 体验介绍西部智慧健身小程序华为运动健康服务核心体验如…

文心一言 VS 讯飞星火 VS chatgpt (294)-- 算法导论21.4 3题

三、根据练习 21.4-2 的结论&#xff0c;对于每个结点x&#xff0c;需要多少位(bit)来存储x.rank&#xff1f;21.4-2 的内容是&#xff1a;“证明&#xff1a;每个结点的秩最多为 ⌊lgn⌋ 。”。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在问题21.4-2中&a…

YOLO-world论文阅读笔记

论文&#xff1a;https://arxiv.org/abs/2401.17270 code: https://github.com/AILab-CVC/YOLO-World 1、为什么要做这个研究&#xff08;理论走向和目前缺陷&#xff09; ? 之前的开集检测器大多比较慢&#xff0c;不利于部署。 2、他们怎么做这个研究 &#xff08;方法&…

C++编程(九)异常处理机制、模板、容器库

文章目录 一、异常处理机制&#xff08;一&#xff09;概念&#xff08;二&#xff09;语法格式&#xff08;三&#xff09;使用示例&#xff08;四&#xff09;异常处理机制库 二、模板&#xff08;一&#xff09;概念&#xff08;二&#xff09;函数模板1. 语法格式2. 函数模…

zxing-cpp+OpenCV根据字符串生成条形码

编译构建 需要使用到 CMake、Git、GCC 或 MSVC。 github 链接&#xff1a;https://github.com/zxing-cpp/zxing-cpp 编译之前请确保&#xff1a; 确保安装了 CMake 版本 3.15 或更高版本。 确保安装了与 C17 兼容的编译器(最低VS 2019 16.8 / gcc 7 / clang 5)。 编译构建…

Linear Regression线性回归

目录 前言 Housing Price Prediction Linear Regression Loss/Cost Function Learning Rate 作业 前言 在本专栏中&#xff0c;我们将学习机器学习的知识。 Housing Price Prediction 在开始线性回归的研究之前&#xff0c;我们先来考虑一个实际的问题&#xff1a;房屋…