TiDB 组件 GC 原理及常见问题

news2024/12/29 8:42:55

本文详细介绍了 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.coloradmin.cn/o/1580748.html

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

相关文章

【RAG实践】基于LlamaIndex和Qwen1.5搭建基于本地知识库的问答机器人

什么是 RAG LLM 会产生误导性的 “幻觉”&#xff0c;依赖的信息可能过时&#xff0c;处理特定知识时效率不高&#xff0c;缺乏专业领域的深度洞察&#xff0c;同时在推理能力上也有所欠缺。 正是在这样的背景下&#xff0c;检索增强生成技术&#xff08;Retrieval-Augmented…

Pillow教程09:图片格式(png,jpg,ico等)批量转换+批量修改图片尺寸

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

场景文本检测识别学习 day02(AlexNet论文阅读、ResNet论文精读)

怎么读论文 在第一遍阅读的时候&#xff0c;只需要看题目&#xff0c;摘要和结论&#xff0c;先看题目是不是跟我的方向有关&#xff0c;看摘要是不是用到了我感兴趣的方法&#xff0c;看结论他是怎么解决摘要中提出的问题&#xff0c;或者怎么实现摘要中的方法&#xff0c;然…

华为机试题

目录 第一章、HJ1计算字符串最后一个单词的长度&#xff0c;单词以空格隔开。1.1&#xff09;描述1.2&#xff09;解题第二章、算法题HJ2 计算某字符出现次数1.1&#xff09;题目描述1.2&#xff09;解题思路与答案第三章、算法题HJ3 明明的随机数1.1&#xff09;题目描述1.2&a…

C++——优先级队列

前言&#xff1a;这篇文章我们继续来分享一个c的容器——优先级队列。 一.理解优先级 何为优先级一说&#xff1f;实际上就是有顺序的意思。 优先级队列&#xff0c;即有顺序的队列&#xff0c;是一个无需我们自己进行排序操作&#xff0c;在数据传入时就会由容器自己排好序的…

Linux系统安装内网穿透实现固定公网地址访问本地MinIO服务

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&am…

【蓝桥杯嵌入式】定时器的PWM输出与输入捕获(测量频率与占空比)

【蓝桥杯嵌入式】定时器的PWM输出与脉冲输 入捕获&#xff08;测量频率与占空比&#xff09; PWM输出cubemx配置程序设计 输入捕获cubemx配置程序设计 真题典例分析 PWM输出 cubemx配置 PWM输出引脚配置&#xff0c;这里使用PA6和PA7引脚输出两路PWM信号&#xff0c;分别对应的…

计算机提示msvcp140.dll丢失的解决方法

在日常操作与深度应用计算机系统的过程中&#xff0c;我们难免会遭遇各类技术性问题。其中&#xff0c;一种颇为常见且可能导致应用程序无法正常启动或运行的情况便是“msvcp140.dll文件缺失”。这一现象&#xff0c;对于无论是经验丰富的IT专业人士&#xff0c;还是对计算机知…

Linux操作系统(六):文件系统组件

参考资料&#xff1a;阿秀的笔记 文件系统 1. 文件系统的基本组成2. 文件的使用3.文件如何存储3.1 目录怎么存储 4.Linux继承于Unix系统的Unix文件实现方式4.1 Linux Ext 2/3 文件系统4.2 Linux Ext 4 文件系统4.3 磁盘空闲空间的管理机制4.3.1 空闲表法4.3.2 空闲链表法4.3.3…

网易云歌曲评论抓取

网易云歌曲评论爬取 步骤1.找到一首歌曲2.按下F12键打开开发者模式,对其进行抓包3.查找获得评论数据的接口4.对获得评论数据接口进行分析5.构建加密函数方法一方法二运行结果全部代码使用Js文件只使用python新的代码小结与展望这次的任务是获取网易云音乐下面的评论,涉及的知…

安卓四大组件——Service篇

1.作用 长时间位于后台&#xff08;无界面&#xff09;完成用户指定操作 1.1两类状态 &#xff08;a&#xff09;started&#xff08;启动&#xff09;&#xff1a;当应用程序组件&#xff08;如activity&#xff09;调用startService()方法启动服务时&#xff0c;服务处于sta…

HJ1 字符串最后一个单词的长度(字符串,import java.util.HashSet;)

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别int num sc.nextInt();boolean[] in new boolean[…

设计模式学习笔记 - 设计模式与范式 -行为型:8.状态模式:游戏、工作流引擎中常用的状态机是如何实现的?

概述 本章学习状态模式。在实际的开发中&#xff0c;状态模式并不是很常用&#xff0c;但是在能够用到的场景里&#xff0c;它可以发挥很大的作用。从这一点上看&#xff0c;它有点像我们之前讲到的组合模式。 状态模式一般用来实现状态机&#xff0c;而状态机常用在游戏、工…

✌2024/4/3—力扣—整数反转

代码实现&#xff1a; int reverse(int x) {long num 0;while (x ! 0) {num num * 10 x % 10;x x / 10;}if ((int)num ! num) {return 0;}return (int)num; }

使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数

使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否为…

Houdini笔记操作技巧_集锦

个人记录下&#xff0c;谨防遗忘。同时丰富下Hou的中文搜素环境。 1、自定义启动界面 ① 设置完界面后&#xff0c;保存自定义界面&#xff08;Save Current Desktop As...&#xff09; ② Edit-->Preferences-->General UIInterface-->Startup in Desktop&#xff1…

qt打包程序打包之跨平台

本文讲的是linux系统中的程序打包&#xff0c;首先我们创建一个简单的程序&#xff0c;我的程序叫做debtest 然后设置qmake来源&#xff0c;这个直接决定了程序依赖的qt库的位置&#xff0c;如果我们需要指定qt标准库的位置&#xff0c;那么qt环境就需要在那个位置。 修改前&am…

C# 如何修改项目名称

目录 背景具体步骤1、Visual Studio中修改项目名和程序集名称以及命名空间2、修改项目文件夹名3、修改解决方案里项目的路径4、再次打开解决方案&#xff0c;问题解决步骤总结 名词解释解决方案&#xff08;Solution&#xff09;项目&#xff08;Project&#xff09;程序集&…

【操作系统】CentOS7入门级安装

下载镜像 CentOS镜像下载Download (centos.org) 我们选择第一个 X86_64 CentOS Mirrors List 版本描述X86_X64带64位的32位扩展版(一般安装这个)ARM64 (aarch64)嵌入式。适用于微端(树莓派、机械臂、机械中控)IBM Power (ppc64le)专用于IBM POWER服务器 选择一个合适的链接 …

DSL - Wire 实现-ApiHug101

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace docs/ha…