TiDB 组件 GC 原理及常见问题

本文详细介绍了 TiDB 的 Garbage Collection(GC)机制及其在 TiDB 组件中的实现原理和常见问题排查方法。 TiDB 底层使用单机存储引擎 RocksDB,并通过 MVCC 机制,基于 RocksDB 实现了分布式存储引擎 TiKV,以支持高可用分布式事务。 GC 过程旨在清理旧数据,减少其对性能的影响,主要包括四个步骤: 计算 GC safepoint、解析锁(Resolve locks)、连续范围数据删除(Delete ranges)和同步 GC safepoint 至集群其他组件。 文章还讲述了如何定位 GC leader、监控 GC 状态、以及处理 GC 过程中遇到的常见问题。

TiDB 底层使用的是单机存储引擎 RocksDB, 为了实现分布式事务接口,TiDB 又采用 MVCC 机制,基于 RocksDB 实现了高可用分布式存储引擎 TiKV。 也就是当新写入(增删改)的数据覆盖到旧数据时,旧数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。 当这些历史版本堆积越来越多时,就会引出一系列问题,最常见的便是读写变慢。 TiDB 为了降低历史版本对性能的影响,会定期发起 Garbage Collection( https://docs-archive.pingcap.com/zh/tidb/v7.2/garbage-collection-overview GC)清理不再需要的旧数据。

在 上一篇文章 中,我们介绍了 MVCC 版本堆积相关原理及排查手段,当我们发现 MVCC 版本堆积已经对当前集群读写产生了性能影响时,则需要检查当前集群 GC 的状态及相关参数是否需要进行调整。

本文我们将重点介绍 TiDB 组件中 GC 的相关原理及常见排查手段。由于篇幅原因,TiKV 侧的 GC 相关内容我们将在另一篇文章中独立介绍。

GC leader

通过对 TiDB 分布式事务( https://tidb.net/blog/7730ed79 )实现的了解,我们知道 TiDB 集群具体的数据存储在 TiKV 上,集群的元数据信息存在 PD 上,TiDB 要做数据旧版本的回收,则需要有个类似 GC worker 的角色从 PD 拿到元数据信息然后对 TiKV 中的数据做垃圾回收工作。这个角色目前我们放在 TiDB 中,一个就够,所以我们借助 PD 维护选举出一个 GC leader 的角色,来统一协调整个集群的 GC 工作。

GC leader 是 TiDB 中负责推动集群 GC 工作的一个协程(goroutinue), 一个 TiDB 集群中,同一时刻有且只有一个 TiDB 上会有这个 GC leader 角色。

常见排查指导

通常如果怀疑系统 GC 状态可能存在异常,我们可以从 gc leader 所报的日志中查看当前 GC 的详细状态。

  • 如何查找 GC leader 在哪个 tidb 上?
mysql> select variable_name,variable_value from mysql.tidb where variable_name = "tikv_gc_leader_desc"\G;
*************************** 1. row ***************************variable_name: tikv_gc_leader_desc
variable_value: host:172-16-120-219, pid:3628952, start at 2024-01-17 16:34:58.022047311 +0800 CST m=+9.910349289
1 row in set (0.00 sec)
  • 找到 gc leader 对应的 tidb 实例,在日志中 grep "gc_worker" 关键字即可看到当前 GC 的整体运行状态。关于日志中字段的具体含义,我们可以在后面的章节会详细展开介绍。
tidb@172-16-120-219:~/shirly/tiup$ ps aux | grep 3628952
tidb     3592616  0.0  0.0   8160  2456 pts/2    S+   15:12   0:00 grep --color=auto 3628952
tidb     3628952  8.2  0.2 10914336 1065168 ?    Ssl  Jan17 4158:25 bin/tidb-server -P 4005 --status=10080 --host=0.0.0.0 --advertise-address=127.0.0.1 --store=tikv --initialize-insecure --path=127.0.0.1:2379,127.0.0.1:2381,127.0.0.1:2383 --log-slow-query=/DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb_slow_query.log --config=conf/tidb.toml --log-file=/DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb.log
tidb@172-16-120-219:~/shirly/tiup$ grep "gc_worker" /DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb.log  | head
[2024/02/02 19:29:59.062 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446541678411803] [concurrency=3]
[2024/02/02 19:29:59.291 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446541678411803] [regions=1236]
[2024/02/02 19:30:59.065 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446557407051824] [concurrency=3]
[2024/02/02 19:30:59.283 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446557407051824] [regions=1236]
[2024/02/02 19:31:59.060 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446573135691804] [concurrency=3]
[2024/02/02 19:31:59.297 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446573135691804] [regions=1236]
[2024/02/02 19:32:59.072 +08:00] [INFO] [gc_worker.go:347] ["[gc worker] starts the whole job"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [concurrency=3]
[2024/02/02 19:32:59.074 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [try-resolve-locks-ts=447446588864331831] [concurrency=3]
[2024/02/02 19:32:59.305 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [try-resolve-locks-ts=447446588864331831] [regions=1236]
[2024/02/02 19:33:59.057 +08:00] [INFO] [gc_worker.go:307] ["[gc worker] there's already a gc job running, skipped"] ["leaderTick on"=6345a3cad480026]

TiDB GC 整体流程及常见问题

TiDB GC 整体流程主要分四个步骤,本章我们将逐一展开介绍。

目前我们 TiDB 侧的 GC 流程主要分为四个步骤:

  • 计算 GC safepoint, 即 TiDB GC 时需要知道具体删除哪个时间点之前的旧版本。
  • 清理 GC safepoint 之前事务留下的锁,即旧版本数据在被清理前,需要明确残留锁所在事务的状态。
  • Delete-ranges 连续范围数据删除,即对于 truncate table 等这类在 TiKV 中连续保存的数据,直接在此阶段进行物理删除以优化性能。
  • 将最新 safepoint 同步到集群其他组件(TiKV)

TiDB 中 GC worker 以上行为的发生频率我们可以在 grafana 监控中 tikv-details->GC->TiDB GC worker actions 看到。

下面我们逐一介绍每个步骤的原理、相关监控、配置及常见问题。

Step 1 计算 GC safepoint

根据 文章 对数据写入的简单介绍,我们知道当对同一个 key 进行多次增删改后,会在 raftstore 层留下所有的历史版本,随着这些版本的堆积,整体的读写性能将受到影响。TiDB 会定期触发 GC 工作对这些历史版本进行回收,那每次 GC 具体回收哪些旧版本数据呢?TiDB 在每次发生 GC 时,都会根据当前配置参数计算出一个 safepoint , 来决定具体回收哪些旧版本数据。

GC safepoint 的定义

  • Safepoint 是一个时间戳,对应一个具体的物理时间。
  • TiDB GC 会保证 ts >safepoint 的所有快照数据的安全性。

如上图,当前 key 一共有四个版本,

  • 如果 gc safepoint 是 5:00, 则 GC 后只会保留 key_4:00 这条数据。
  • 如果 gc safepoint 是 2:30, 则 GC 后会保留 key_4:00,key_3:00,key_02:00 这三条数据。以确保读 2:30 这一时刻的快照时,能读到 key_02:00 这条数据。

另外,当前系统的 gc safepoint 我们也可以在系统表 mysql.tidb 中看到:

GC safepoint 的计算过程及常见问题

了解了 GC safepoint, 我们知道其对集群数据的安全性非常重要,算错了 safepoint, 就可能将还需要的旧版本数据提前永久删除掉。所以在计算 safepoint 的时候,我们考虑到了多种情况。

以下是当前 gc worker 计算 safepoint 的主要过程:

1. 根据 GC lifetime 配置计算

GC lifetime ( https://docs.pingcap.com/tidb/stable/system-variables#tidb_gc_life_time-new-in-v50 )的定义:这个变量用于指定每次 GC 时需要保留的数据时限,默认为 10 分钟,即一般情况下,只保证十分钟以内的数据快照安全性即可。

GC safepoint = Current time - GC lifetime (10min by default)
  • 常见问题

当我们调整 gc lifetime 时,比如调大 gc lifetime 时,在 lifetime 符合要求之前会跳过几次 GC,相关 gc_worker 的日志如下

tidb_172.26.55.107_4000.log:[2024/01/16 09:06:52.689 +08:00] [Info] [gc_worker.go:1613] ["[gc worker] sent safe point to PD"] [uuid=6342b1728dc000d] ["safe point"=447035326078648378]
tidb_172.26.55.107_4000.log:[2024/01/16 09:15:11.099 +08:00] [Info] [gc_worker.go:731] ["[gc worker] there's another service in the cluster requires an earlier safe point. gc will continue with the earlier one"] [uuid=6342b1728dc000d] [ourSafePoint=447035555467755520] [minSafePoint=447035326078648378]

对于这种情况符合预期,无绪介入。

2. 检查长时间运行且未提交的事务

检查当前集群所有 session 里面中,是否存在未提交的事务,且该事务 begin 时间早于上一步算出来的 gc safepoint

GC safepoint = min(GC safepoint,min_start_ts among all sessions)

也就是说,gc safepoint 不应晚于当前正在执行中的事务的开始时间。

为了降低长时间运行的未提交事务对 GC 的影响,我们在 v6.1.0 中引入了参数 tidb_gc_max_wait_time( https://docs.pingcap.com/tidb/dev/system-variables#tidb_gc_max_wait_time-new-in-v610 ) (默认 24 小时),也就是当某个事务执行时间超过 24 小时后,该事务不会再卡住 gc safepoint 的推进。

  • 常见问题

GC 在这一步卡住,可以从 gc_worker 日志中看到具体卡住的事务详情:

[gc_worker.go:359] ["[gc worker] gc safepoint blocked by a running session"] [uuid=609099af5940005] [globalMinStartTS=437144990507073574] [safePoint=2022/11/04 23:29:59.969 +08:00]

Workaround: 检查 processlist 定位卡住的那个事务,咨询业务侧是否可以对该事务进行清理:

select * from INFORMATION_SCHEMA.CLUSTER_PROCESSLIST where TIMESTAMPDIFF(minute, now(), concat("2021-", substring_index(txnstart, "(", 1) )) < -10;

3. 检查当前工具需要保留的快照版本

在实际业务集群中,用户可能使用了 CDC/BR 等备份工具,这些备份工具可能需要更早的一个快照进行备份,也就意味着这部分数据不能被 GC 掉。

GC safepoint = min(GC safepoint, min_service_gc_safe_points)
  • 常见问题

如果 GC 被这部分卡住了,一般可以通过 gc_worker 的日志类似如下:

[2022/02/24 17:18:01.444 +05:30] [INFO] [gc_worker.go:411] ["[gc worker] gc safepoint blocked by a running session"] [uuid=5f56cf29bac008e] [globalMinStartTS=431397745740742701] [safePoint=431398894023213056]
[2022/02/24 17:18:01.450 +05:30] [INFO] [gc_worker.go:581] ["[gc worker] there's another service in the cluster requires an earlier safe point. gc will continue with the earlier one"] [uuid=5f56cf29bac008e] [ourSafePoint=431397745740742701] [minSafePoint=431337131664474119] 

在确认了是这一步卡住的后,就可以通过 PD 查看一下具体是哪个服务卡住了 GC 并介入处理了。

// 通过 pd-ctl 查看 service_gc_safe_points 下面的服务需要的最旧快照对应的 safepoint
tidb@172-16-120-219:~/shirly/tiup$  tiup ctl:v7.5.0 pd  -u http://127.0.0.1:2379  service-gc-safepoint
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 service-gc-safepoint
{"service_gc_safe_points": [{"service_id": "gc_worker","expired_at": 9223372036854775807,"safe_point": 447873652897873920 // 这个是在当前这一步上传的 gc safepoint.},{"service_id": "ticdc-default-157...","expired_at": 9223372036854645313,"safe_point": 447873653919043430}],"gc_safe_point": 447873652897873920 // 这个是 TiDB GC 最后一步上传的 safepoint, 用于通知 tikv 用。
}
// 计算每个 service safepoint 实际对应的时间。
tidb@172-16-120-219:~/shirly/tiup$ tiup ctl:v7.5.0 pd  -u http://127.0.0.1:2379  tso 447873652897873920
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 tso 447873652897873920
system:  2024-02-21 15:59:59.055 +0800 CST
logic:   0
tidb@172-16-120-219:~/shirly/tiup$ tiup ctl:v7.5.0 pd  -u http://127.0.0.1:2379  tso 447873653919043430
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 tso 447873653919043430
system:  2024-02-21 16:00:02.95 +0800 CST
logic:   118630

Step 2 : Resolve locks

为什么要清理锁

在有了 gc safepoint 之后,意味着在本轮 GC 中,我们在保证所有 tso >= safepoint 版本的快照安全性的基础上,可以开始删除旧版本了, 那么在删除旧版本之前,如果遇到了锁怎么办呢?根据我们之前对分布式事务的理解,用户在 commit 一个事务之后,TiKV 内部还是有可能留下锁的,而这些锁的提交状态则是存在 primary-key 上,试想以下情况:

  • 事务 1:
  • 事务 ID 即 start_ts 是 t1, commit_ts 为 t2。
  • 更新 A,B,C 三个 key, 客户端 commit 且返回成功。
  • 当前分布式事务 primary_key 是 A , 也就是事务的状态存在 A 上 。
  • B,C 上有当前 t1 所在事务的残留 lock 。
  • 事务 2:
  • 事务 ID 即 start_ts 为 t3, commit_ts 为 t4。(t1<t2<t3<t4)。
  • 更新 A, D 两个 key, 客户端 commit 且返回成功。
  • A,**D** 新版本写入成功,无残留锁。

现在假设我们算出来的 GC safepoint 是 t4 , 也就是保证能读到 t4 这一时刻的快照数据一致即可。对于 A , 当前 t4 可见的数据为 A_t4=>t3,A_t3=>12, 也就是 A 在 t4 这个时刻快照读到的数据是 12。

对于比这个版本更旧的版本 A_t2=>t1 我们认为在当前 gc safepoint 下是可以删除的。那我们可以直接删除 A_t2=>t1 这个版本吗?

假设我们直接删除,删除之后,如果用户要读 t4 这个快照里面 B 的值,发现 B 上有个指向 (A,t1) 的这个 lock, 我们开始从 A 上确认事务 t1 的状态,但是在 TiKV 中找不到 (A,t1) 这个事务,也就无法确认其状态。

以上,就是我们为什么要在真正清理旧版本数据之前,要先对 gc safepoint 之前启动的事务所在残留锁进行清理的原因,这个过程我们定义为 Resolve locks。

Resolve locks 具体过程

知道了 Resolve locks 的原因后,我们很容易就理解,resolve locks 这一步简单理解,就是对 tikv 中的 Lock CF 进行扫描,并清理掉 lock.ts <= gc safepoint 的锁即可。在实际操作中,我们操作步骤如下:

  1. 将请求发给每个 region 的 leader 获取到 lock
  2. 根据 lock 状态,逐个 resolve lock:
  3. 向 PD 定位当前 lock 里面 primary-key 所在的 region 信息
  4. 向对应的 TiKV 发送获取当前 (primary-key,事务 ID ) 对应的事务状态
  5. 根据 (primary-key,事务 ID) 对应的状态:
  • 事务已提交,向 tikv 提交 lock。
  • 事务已 rollback, 向 tikv 发送回滚该 lock 的消息。

相关配置

既然是按照 region 查找锁并进行清理,这个过程完全可以是并发的,针对这一步,目前 TiDB 提供了以下参数:

  • tidb_gc_concurrency( https://docs.pingcap.com/tidb/v6.5/system-variables#tidb_gc_concurrency-new-in-v50 )Resolve locks 步骤中的线程数,默认 -1 表示使用 TiKV 实例的个数作为并发数。一般情况下不用去调整这个值。

相关监控

在 grafana 上 tikv-details/gc/resolveLocks Progress 面板中,可以看到这一步骤以 region 为单位的执行进度。

常见问题

这一步是否可能卡住 GC?

一般的 resolveLocks 不应该卡住 GC,当这一步骤出现问题时,数据可能存在一致性问题,如果数据比较重要的话,应立即联系官方协助恢复。同样的,这一步骤出现问题需要通过 gc worker 的日志进行判断,可以参考 GC leader 章节的方式定位相关日志。

历史上 v5.1.3 有个 bug( https://github.com/pingcap/tidb/issues/26359 )会导致数据一致性的问题而导致 Resolve Locks 失败。错误日志类似长这样:

[gc_worker.go:621]["[gc worker] resolve locks returns an error"]..

Resolve locks 对性能的影响

对于比较空闲的集群,GC 期间 resolve locks 是会对 TiKV 产生性能影响的,主要原因为 resolve lock 需要对集群中所有 region 读取 lock 信息而导致静默 region 被唤醒,从而导致 raftstore/TiKV CPU 出现抖动。具体影响如下:

  1. 不 hibernate 但 GC。一个 tikv 维护 1w 个 region 需要消耗 15% cpu 的开销。
  2. 不 hibernate 不 GC。一个 tikv 维护 1w 个 region 需要消耗 10% cpu 的开销。
  3. hibernate 不 GC。一个 tikv 维护 1w 个 region 需要消耗 1% cpu 的开销。
  4. hibernate 定期 GC。一个 tikv 维护 1w 个 region 需要消耗 1% cpu 的开销外加 GC 期间达到 10-15% 的开销。

所以 4 相比 1 于就会有类似的尖刺现象,因为平常的 baseline 更低

Step 3 : Delete Ranges

在上一步骤中,我们已经将锁清理完毕,也就是所有 gc safepoint 之前的事务状态已经明确。所以,从这一步开始,我们可以真正地开始清理数据了。在正常情况下,因为我们底层用的是 rocksdb, 在我们明确一个版本可以清理以后,会直接调用 rocksdb 的 delete 接口去清理该 key. 但我们知道,rocksdb 本身使用的是 LSM 架构,也就是说它也有 mvcc, 它的一次删除,最终也是转化成了一次写入。那数据什么时候会真正清理呢?就需要等我们 rocksdb 的。compaction 操作来清理了,这个过程就十分漫长了,后续我们还会继续谈论这个话题。

现在从 TiDB 视角看,有一些数据清理操作,如:

  • Drop table/index
  • Truncate table
  • Drop partition
  • ...

以上操作在 GC 时需要将连续的大片数据进行删除。对于这部分数据,我们认为在过了 GC safepoint 之后,可以直接清理,不需要跟普通的 GC 一样对历史版本一边读一边删除的过程。Delete Ranges 就是我们针对这种连续的大块删除的优化。在这一步骤中,我们会直接对数据进行物理回收,空间会立刻被释放出来,极大地减少这一块 GC 对系统读写的压力。

具体步骤如下:

  • 执行 SQL 阶段:这些符合 delete-range 要求的 SQL 会在执行 时,记录在系统表 mysql.gc_delete_range,并标记其删除的时间 ts:

  • GC 阶段:根据上表中删除时间 ts 与当前 gc safepoint 进行比对,对于符合删除条件的数据,调用 tikv 的 unsafeDestroyRange 接口对所有 tikv 发送。TiKV 在收到这个请求后,会直接绕过 raft 对本地的 rocksdb 进行 destroyRange 操作。同时,做完以后,我们会将结果记录在系统表 gc_delete_range_done 里面:

常见问题

这一步也很少出问题,但是不排除短期内需要删除大量数据时,这一步执行比较慢而导致 GC 看似被卡住的情况。对于这种情况不需要做太多介入,耐心等待其完成即可。

Step 4 : Sync gc safepoint

最后,我们会将 GC safepoint 存储到 PD 上,以通知 tikv 进行 GC。TiKV 定期会从 PD 上拿 gc safepoint, 如果发生了变更,则会拿当前的 gc safepoint 开始 tikv 本地的 GC 工作。

相关监控

我们可以从 grafana 上的 tikv-details->GC->TiKV auto GC safepoint 观察 gc safepoint 推进是否符合预期:

常见问题

PD 一共提供了两个接口来存 gc_safepoint, 分别为:

  • UpdateServiceGCSafepoint:在 计算 GC safepoint 那一步调用,这步调用成功,意味着TiDB侧 GC 正式开始
  • UpdateGCSafepoint 在最后这一步“ save gc safepoint toPD ”执行,这一步调用成功,意味着TiDB侧 GC 正式结束。

一般情况下这两个接口的 safepoint 应该是保持一致的,当不一致时,一般是

updateGCSafepoint< UpdateServiceGCSafepoint

也就是 GC 在 TiDB 这一侧某一步卡住了。生产情况下有些卡住是符合预期的,如下面这种情况:

  • 在工具卡住 gc safepoint 之后,两接口的 gc safepoint 出现不一致。如果此时又正好调大了 gc lifetime, 那么在下一次 gc 成功执行完成之前,这两个接口对应的值会一直不一致。

具体影响:无。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络——37认证

认证 目标&#xff1a;Bob需要Alice证明他的身份 Protocol ap1.0&#xff1a;Alice说"A am Alice" 可能出现的问题&#xff1a; 在网络上Bob看不到Alice&#xff0c;因此Trudy可以简单的声称他是Alice 认证&#xff1a;重新尝试 Protocol ap2.0&#xff1a;Alice…

阿里云4核8G服务器ECS通用算力型u1实例优惠价格

阿里云4核8G服务器优惠价格955元一年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R) Platinum处理器&#xff0c;阿里云活动链接 aliyunfuwuq…

批量导入svg文件作为图标使用(vue3)vite-plugin-svg-icons插件的具体应用

目录 需求svg使用简述插件使用简述实现安装插件1、配置vite.config.ts2、src/main.ts引入注册脚本3、写个icon组件4、使用组件 需求 在vue3项目中&#xff0c;需要批量导入某个文件夹内数量不确定的svg文件用来作为图标&#xff0c;开发完成后能够通过增减文件夹内的svg文件&a…

一文解析智慧城市,人工智能技术将成“智”理主要手段

长期以来&#xff0c;有关智慧城市的讨论主要围绕在技术进步方面&#xff0c;如自动化、人工智能、数据的公开以及将更多的传感器嵌入城市以使其更加智能化。实际上&#xff0c;智慧城市是一个关于未来的设想&#xff0c;其重要原因在于城市中存在各种基础设施、政治、地理、财…

测试框架pytest学习与实践

pytest是一个专业的测试框架&#xff0c;可以帮助我们对python项目进行测试&#xff0c;提高测试的效率。 pytest官网手册&#xff1a;pytest: helps you write better programs — pytest documentation 中文手册&#xff1a;Pytest 教程 入门学习 安装pytest pip install…

代码随想录算法训练营第二十五天| 216.组合总和III、17.电话号码的字母组合

系列文章目录 目录 系列文章目录216.组合总和III17.电话号码的字母组合回溯法 216.组合总和III 本题k相当于树的深度&#xff0c;9&#xff08;因为整个集合就是9个数&#xff09;就是树的宽度。 剪枝&#xff1a;①for循环的范围剪枝&#xff0c;i < 9 - (k - path.size()…

Mac资源库的东西可以删除吗?mac资源库在哪里打开 cleanmymacx是什么 cleanmymac免费下载

在使用Mac电脑的过程中&#xff0c;用户可能会遇到存储空间不足的问题。一种解决方法是清理不必要的文件&#xff0c;其中资源库&#xff08;Library&#xff09;文件夹是一个常被提及但又让人迷惑的目标。Mac资源库的东西可以删除吗&#xff1f;本文旨在解释Mac资源库的作用、…

卫星遥感影像如何选择合适的分辨率

​ 卫星遥感影像的分辨率是影响其应用效果的关键因素之一。分辨率越高&#xff0c;所获取的图像细节越丰富&#xff0c;能够更准确地反映地物的特征和变化。因此&#xff0c;在选择卫星遥感影像时&#xff0c;需要根据实际需求和数据可获取性来选择合适的分辨率。 一、分辨…

Python向带有SSL/TSL认证服务器发送网络请求小实践(附并发http请求实现asyncio+aiohttp)

1. 写在前面 最近工作中遇到这样的一个场景&#xff1a;给客户发送文件的时候&#xff0c;为保证整个过程中&#xff0c;文件不会被篡改&#xff0c;需要在发送文件之间&#xff0c; 对发送的文件进行签名&#xff0c; 而整个签名系统是另外一个团队做的&#xff0c; 提供了一…

AI大语言模型GPT —— R 生态环境领域数据统计分析

自2022年GPT&#xff08;Generative Pre-trained Transformer&#xff09;大语言模型的发布以来&#xff0c;它以其卓越的自然语言处理能力和广泛的应用潜力&#xff0c;在学术界和工业界掀起了一场革命。在短短一年多的时间里&#xff0c;GPT已经在多个领域展现出其独特的价值…

数据挖掘入门项目二手交易车价格预测之建模调参

文章目录 目标步骤1. 调整数据类型&#xff0c;减少数据在内存中占用的空间2. 使用线性回归来简单建模3. 五折交叉验证4. 模拟真实业务情况5. 绘制学习率曲线与验证曲线6. 嵌入式特征选择6. 非线性模型7. 模型调参&#xff08;1&#xff09; 贪心调参&#xff08;2&#xff09;…

C++从入门到精通——初步认识面向对象及类的引入

初步认识面向对象及类的引入 前言一、面向过程和面向对象初步认识C语言C 二、类的引入C的类名代表什么示例 C与C语言的struct的比较成员函数访问权限继承默认构造函数默认成员初始化结构体大小 总结 前言 面向过程注重任务的流程和控制&#xff0c;适合简单任务和流程固定的场…

电商技术揭秘八:搜索引擎中的SEO内部链接建设与外部推广策略

文章目录 引言一、 内部链接结构优化1.1 清晰的导航链接1. 简洁明了的菜单项2. 逻辑性的布局3. 避免深层次的目录结构4. 使用文本链接5. 突出当前位置6. 移动设备兼容性 1.2 面包屑导航1. 显示当前页面位置2. 可点击的链接3. 简洁性4. 适当的分隔符5. 响应式设计6. 避免重复主页…

c# wpf XmlDataProvider 简单试验

1.概要 2.代码 <Window x:Class"WpfApp2.Window12"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/blend…

Debian12 使用 nginx 与 php8.2 使用 Nextcloud

最近将小服务器升级了下系统&#xff0c;使用了 debian12 的版本&#xff0c;正好试试 nginx 和 php-fpm 这种方式运行 Nextcloud 这个私有云的配置。 一、基本系统及应用安装 系统&#xff1a;debian12 x86_64 位版本最小安装&#xff0c;安装后可根据自己需求安装一些工具&…

《图解Vue3.0》- 调试

如何对vue3项目进行调试 调试是开发过程中必备的一项技能&#xff0c;掌握了这项技能&#xff0c;可以很好的定义bug所在。一般在开发vue3项目时&#xff0c;有三种方式。 代码中添加debugger;使用浏览器调试&#xff1a;sourcemap需启用vs code 调试&#xff1a;先开启node服…

python标准数据类型--集合常用方法

在Python中&#xff0c;集合&#xff08;Set&#xff09;是一种无序且不重复的数据结构&#xff0c;它是由一个无序的、不重复的元素组成的。Python中的集合与数学中的集合概念相似&#xff0c;并且支持一系列常用的方法。本篇博客将深入介绍Python集合的常用方法&#xff0c;帮…

《QT实用小工具·十五》多种样式的开关控件

1、概述 源码放在文章末尾 目前实现了三种样式的开关控件按钮&#xff0c;如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef IMAGESWITCH_H #define IMAGESWITCH_H/*** 图片开关控件 * 1. 自带三种开关按钮样式。* 2. 可自定义开关图片。*/#include <QWid…

小米汽车su7全色系展示源码

源码简介 小米汽车全色系展示源码&#xff0c;小米汽车su7全色系展示源码 安装教程 纯HTML&#xff0c;直接将压缩包上传网站目录解压即可 首页截图 源码下载 小米汽车su7全色系展示源码-小8源码屋源码简介 小米汽车全色系展示源码&#xff0c;小米汽车su7全色系展示源码 …

(二)小案例银行家应用程序-创建DOM元素

● 上图的数据很明显是从我们账户数组中拿到了&#xff0c;我们刚刚学习了forEach&#xff0c;所以我们使用forEach来创建我们的DOM元素&#xff1b; const displayMovements function (movements) {movements.forEach((mov, i) > {const type mov > 0 ? deposit : w…