Go语言pgxcursor库:PostgreSQL大数据流式处理与内存优化实践

news2026/5/2 22:45:43
1. 项目概述为什么需要游标迭代器在 Go 语言生态中处理 PostgreSQL 数据库时pgx库无疑是当前最主流、性能最出色的选择之一。然而当你的应用需要处理海量数据查询时一个常见的问题就会浮出水面内存。想象一下你有一个用户表里面有上千万条记录你需要遍历所有用户进行某种批量处理比如数据迁移、报表生成、或发送通知。如果直接用pgx的Query方法数据库会尝试将整个结果集一次性发送到你的 Go 应用内存中。这可能导致应用内存瞬间飙升甚至触发 OOM内存溢出而被系统杀死。这就是pgx-contrib/pgxcursor这个库要解决的核心痛点。它不是一个全新的数据库驱动而是一个构建在pgx v5之上的、精巧的“游标迭代器”。它的设计哲学非常明确用数据库游标Cursor的能力实现流式、低内存消耗的数据遍历同时保持与原生pgx.Rows接口的完全兼容。这意味着你几乎不需要改变现有的数据扫描逻辑比如你正在用的pgx.RowToStructByName就能让应用获得处理大数据集的能力。我自己在几个数据密集型项目中都遇到过类似问题。最初的做法是手动写分页查询LIMIT/OFFSET但这种方式在深度分页时性能急剧下降而且代码里充斥着繁琐的页码计算逻辑。后来尝试在事务中手动声明和使用 SQL 游标代码又变得冗长且容易出错尤其是游标的声明、获取和关闭逻辑稍有不慎就会导致连接或事务泄漏。pgxcursor的出现相当于把这些底层复杂性全部封装了起来提供了一个既高效又优雅的解决方案。它特别适合那些需要稳定、高效地流式处理大量数据库记录的后台任务、ETL 管道或数据分析服务。2. 核心设计解析它是如何工作的要理解pgxcursor的价值得先搞清楚它背后依赖的两个关键技术PostgreSQL 的服务器端游标以及pgx v5的扩展接口。2.1 基石PostgreSQL 的服务器端游标游标Cursor在数据库里是一个经典概念你可以把它理解为一个指向查询结果集的指针。与一次性获取所有结果不同声明一个游标后你可以分批地比如一次100条从服务器“拉取”数据。在 PostgreSQL 中这通常通过以下 SQL 命令序列完成BEGIN; -- 开启事务 DECLARE my_cursor CURSOR FOR SELECT * FROM large_table; -- 声明游标 FETCH 100 FROM my_cursor; -- 获取前100行 FETCH 100 FROM my_cursor; -- 获取下一个100行 ... CLOSE my_cursor; -- 关闭游标 COMMIT; -- 提交事务关键点在于结果集始终存储在数据库服务器端客户端只是按需获取。这带来了两个核心好处极低的内存压力客户端内存占用只与单次获取的批次大小有关与总结果集大小无关。稳定的服务端资源查询在声明游标时就已经执行并确定了结果集后续的FETCH操作成本很低。pgxcursor在底层自动为你执行了DECLARE、FETCH和CLOSE这些命令。2.2 桥梁pgx v5 的 Querier 接口与 Rows 封装pgx v5设计得非常灵活它定义了一个核心的pgx.Querier接口。无论是连接 (pgx.Conn)、连接池 (pgxpool.Pool) 还是事务 (pgx.Tx)都实现了这个接口。pgxcursor.Querier结构体内部封装了一个pgx.Querier这意味着它可以适配上述任何一种数据库上下文。它的魔法在于Query方法。当你调用它时它并不会立即执行普通的查询。而是在当前连接或事务中自动声明一个持有你 SQL 查询的游标。根据你设置的Capacity执行FETCH N来获取第一批数据。返回一个实现了pgx.Rows接口的自定义对象内部我们叫它cursorRows。此后当你调用rows.Next()时如果内存中的批次数据还没消费完就直接返回如果消费完了它会自动发起下一次FETCH直到游标耗尽。最后在rows.Close()被调用时它会自动关闭游标并清理资源。这种设计实现了“开箱即用”的体验你使用它的方式和使用原生的pgx.Rows一模一样但背后已经是完全不同的、更节省资源的流式机制了。注意由于游标必须在一个数据库事务或至少一个持久的连接上下文中生存pgxcursor在内部可能会隐式管理事务。如果你传入的是一个连接池 (pgxpool.Pool)它会为了游标操作而从池中获取并独占一个连接直到迭代结束。理解这一点对性能调优很重要。3. 从入门到精通完整使用指南了解了原理我们来看看如何在实际项目中使用它。整个过程非常直观。3.1 基础安装与初始化首先通过 go get 添加依赖go get github.com/pgx-contrib/pgxcursor接下来是初始化。pgxcursor非常灵活可以与连接、连接池或现有事务一起工作。package main import ( context fmt log github.com/jackc/pgx/v5 github.com/jackc/pgx/v5/pgxpool github.com/pgx-contrib/pgxcursor ) // 定义一个与数据库表映射的结构体 type Product struct { ID int db:id Name string db:name Price int db:price } func main() { ctx : context.Background() // 示例1使用连接池最常见场景 pool, err : pgxpool.New(ctx, postgres://user:passwordlocalhost:5432/mydb) if err ! nil { log.Fatal(err) } defer pool.Close() // 创建 pgxcursor 的 Querier设置批次容量为 500 querier : pgxcursor.Querier{ Querier: pool, // 传入连接池 Capacity: 500, } // 示例2使用单个连接适用于需要长连接控制的场景 conn, err : pgx.Connect(ctx, connString) if err ! nil { log.Fatal(err) } defer conn.Close(ctx) connQuerier : pgxcursor.Querier{ Querier: conn, Capacity: 200, } // 示例3在现有事务中使用保证数据一致性视图 tx, err : pool.Begin(ctx) if err ! nil { log.Fatal(err) } defer tx.Rollback(ctx) // 安全回滚 txQuerier : pgxcursor.Querier{ Querier: tx, Capacity: 1000, } // ... 使用 txQuerier 进行查询 // tx.Commit(ctx) }3.2 执行查询与遍历结果初始化Querier后使用方式就和标准库database/sql或pgx原生查询几乎无异。func processLargeDataset(querier *pgxcursor.Querier) error { ctx : context.Background() // 1. 执行查询得到 rows rows, err : querier.Query(ctx, SELECT id, name, price FROM products WHERE category_id $1 ORDER BY created_at DESC , 42) // 支持参数化查询和 pgx 一样 if err ! nil { return fmt.Errorf(query failed: %w, err) } defer rows.Close() // 切记 defer close确保游标被正确清理 var processedCount int // 2. 遍历 rows for rows.Next() { var p Product // 使用 pgx 强大的行扫描工具与 pgxcursor 完美兼容 err : pgx.RowToStructByName(p, rows) if err ! nil { return fmt.Errorf(scan row failed: %w, err) } // 这里是你的业务逻辑例如 // - 计算统计信息 // - 转换数据格式 // - 写入另一个系统或文件 fmt.Printf(Processing product %d: %s\n, p.ID, p.Name) processedCount // 模拟一些处理耗时 // time.Sleep(10 * time.Millisecond) } // 3. 检查遍历过程中是否有错误如网络中断 if err : rows.Err(); err ! nil { return fmt.Errorf(iteration error: %w, err) } log.Printf(Successfully processed %d products, processedCount) return nil }这里有一个非常重要的实操心得务必使用defer rows.Close()。对于普通的pgx.Rows关闭主要是为了归还连接。但对于pgxcursorClose()方法还肩负着向数据库发送CLOSE cursor和可能的事务清理指令。如果忘记关闭可能会导致服务器端的游标资源一直无法释放在长时间运行的应用中逐渐耗尽数据库资源。3.3 Capacity 策略深度调优Capacity是pgxcursor.Querier中最重要的调优参数它直接决定了性能特征。文档中的表格给出了概要这里我们深入解读一下。Capacity值内部 SQL 行为适用场景与影响分析0(默认)每次rows.Next()都执行FETCH NEXT低内存模式。每读取一行都需要一次与数据库的网络往返。吞吐量极低只适用于极慢速的消费场景或测试。在实际生产环境中应避免使用此默认值。N(例如 100)每次内存批次耗尽时执行FETCH N平衡模式。这是推荐的使用方式。N值在内存消耗和网络往返次数之间取得平衡。批次越大吞吐量越高但单次FETCH延迟可能略增内存占用为N * row_size。N(例如 5000)同上但N值很大高吞吐模式。适用于网络延迟较高如跨地域数据库、或客户端处理速度非常快的场景。目标是最大化每次网络通信的效率用稍高的内存换取更少的总体延迟。如何选择你的Capacity这里没有一个万能公式但可以遵循以下步骤估算行大小粗略估算你查询结果中单行数据在 Go 内存中的大小可以通过查询一小部分数据并用unsafe.Sizeof或 profiling 工具估算。设定内存预算决定你愿意为这个流式查询分配多少内存。例如你希望单个查询最多占用 10MB。计算 NN 内存预算 / 单行大小。比如行大小约 2KB预算 10MB那么N ≈ 5000。实测与调整在模拟真实负载下进行测试。观察数据库的pg_stat_statements中FETCH的次数和应用的吞吐量。你可以尝试将N调整为 1000、5000、10000 进行对比。一个实用的技巧是将Capacity设置为与你的“处理批次”相匹配。例如你每处理 1000 条记录就写入一次文件或提交一次事务那么将Capacity设为 1000 或它的整数倍通常是最有效的。// 示例根据数据特征动态设置 Capacity func getOptimalCapacity(rowEstimatedSize int) int { memoryBudget : 20 * 1024 * 1024 // 20 MB capacity : memoryBudget / rowEstimatedSize // 确保一个合理的范围 if capacity 100 { return 100 } if capacity 10000 { return 10000 } return capacity } querier : pgxcursor.Querier{ Querier: pool, Capacity: getOptimalCapacity(2048), // 假设每行约2KB }4. 高级场景与性能陷阱规避掌握了基本用法后我们来看看一些更复杂的场景和必须避开的“坑”。4.1 与事务Transaction的协同工作游标的生命周期严格依赖于一个数据库事务。pgxcursor帮你隐藏了大部分细节但理解其行为至关重要。场景A你传入一个*pgxpool.Pool这是最常见的情况。pgxcursor会在内部执行以下操作从连接池获取一个专用连接。在该连接上开启一个READ ONLY、DEFERRABLE的事务为了最小化锁竞争和性能最优。在该事务中声明并使用游标。当rows.Close()被调用时关闭游标并提交事务最后将连接归还给池。这意味着在整个迭代过程中你看到的数据视图是一致性快照从游标声明的那一刻起就固定了不受其他并发事务修改的影响。这对于生成一致性报表非常有用。场景B你传入一个已存在的pgx.Tx这时pgxcursor会使用你提供的事务。你必须自己管理这个事务的提交或回滚。游标将在该事务的隔离级别下运行。这给了你最大的控制权例如你可以将游标读取和后续的写入操作放在同一个事务中保证原子性。// 示例在显式事务中使用 pgxcursor 进行读取然后更新 err pgx.BeginFunc(ctx, pool, func(tx pgx.Tx) error { querier : pgxcursor.Querier{Querier: tx, Capacity: 1000} rows, err : querier.Query(ctx, SELECT id, status FROM orders WHERE statusPENDING) if err ! nil { return err } defer rows.Close() var ids []int64 for rows.Next() { var id int64 var status string if err : rows.Scan(id, status); err ! nil { return err } ids append(ids, id) } if rows.Err() ! nil { return rows.Err() } // 基于读取到的 ids在同一个事务中执行更新 _, err tx.Exec(ctx, UPDATE orders SET statusPROCESSED WHERE id ANY($1), ids) return err })重要警告长时间持有事务也就是长时间迭代游标会阻止 PostgreSQL 清理旧的元组行版本可能导致表膨胀影响数据库整体性能。对于超大型数据集的遍历需要评估时间窗口或考虑使用REPEATABLE READ隔离级别以外的其他方案。4.2 连接池Pool下的资源管理当使用pgxpool.Pool时pgxcursor会从池中“借走”一个连接直到迭代结束。这带来两个潜在问题连接占用如果Capacity设置很小或者客户端处理很慢这个连接会被长时间占用。在高并发下可能导致连接池中的连接被耗尽其他请求需要等待。监控你的数据库连接数 (max_connections) 和应用连接池的等待统计至关重要。超时控制务必为查询上下文 (context.Context) 设置合理的超时。如果迭代卡住比如客户端逻辑死循环一个带有超时的context可以帮助中断查询释放连接。ctx, cancel : context.WithTimeout(context.Background(), 5*time.Minute) // 为整个遍历设置5分钟超时 defer cancel() rows, err : querier.Query(ctx, SELECT ...) // ... 迭代过程 // 如果超过5分钟ctx会触发Done后续的rows.Next()会返回错误连接得以释放。4.3 错误处理与资源清理的最佳实践健壮的错误处理是生产级代码的基石。func robustCursorProcessing(querier *pgxcursor.Querier) error { ctx : context.Background() rows, err : querier.Query(ctx, complexQuery) if err ! nil { // 查询本身失败语法错误、连接问题等 return fmt.Errorf(initial query failed: %w, err) } // 使用 defer 确保在任何情况下包括panic都能尝试关闭rows defer func() { closeErr : rows.Close() if closeErr ! nil { // 记录关闭错误但通常不把它作为主错误返回以免掩盖业务逻辑错误 log.Printf(WARNING: failed to close cursor rows: %v, closeErr) } }() for rows.Next() { // ... 扫描数据 ... if err : pgx.RowToStructByName(item, rows); err ! nil { // 单行扫描失败可能是类型不匹配或数据损坏 // 可以选择记录错误并跳过该行或者直接失败取决于业务要求 log.Printf(ERROR: scanning row failed, skipping: %v, err) continue } // 业务处理 if err : businessLogic(item); err ! nil { // 如果业务逻辑失败我们可能希望中止整个处理 return fmt.Errorf(business logic failed at row: %w, err) } } // 循环结束后必须检查 rows.Err() // 这个错误表示在迭代过程中发生的错误如网络中断、游标失效等。 if err : rows.Err(); err ! nil { return fmt.Errorf(cursor iteration failed: %w, err) } return nil }一个常见的陷阱是忽略rows.Err()。for rows.Next()循环可能在中间因为底层FETCH失败而退出此时rows.Err()会返回具体的错误。如果不检查你会以为所有数据都处理完了而实际上可能只处理了一部分。5. 实战对比pgxcursor vs. 传统分页为了更直观地感受pgxcursor的优势我们将其与最常见的替代方案——LIMIT/OFFSET分页进行对比。假设我们有一个events表有 1,000,000 条记录我们需要遍历所有user_id100的事件。方案一使用 LIMIT/OFFSET 分页func paginateEvents(pool *pgxpool.Pool, userID int) error { offset : 0 limit : 1000 for { rows, err : pool.Query(ctx, SELECT id, data FROM events WHERE user_id $1 ORDER BY id LIMIT $2 OFFSET $3, userID, limit, offset) if err ! nil { return err } var hasData bool for rows.Next() { hasData true // ... 处理数据 ... } rows.Close() if !hasData { break } offset limit } return nil }问题性能衰减OFFSET越高数据库需要扫描并跳过的行就越多查询会越来越慢。OFFSET 100000意味着数据库要先找到并丢弃前10万行。数据窗口漂移如果在遍历过程中有新的events插入使用ORDER BY id和固定OFFSET可能导致某些行被重复看到或完全错过取决于新行的id大小和排序。方案二使用 pgxcursorfunc streamEvents(pool *pgxpool.Pool, userID int) error { querier : pgxcursor.Querier{Querier: pool, Capacity: 1000} rows, err : querier.Query(ctx, SELECT id, data FROM events WHERE user_id $1 ORDER BY id, userID) if err ! nil { return err } defer rows.Close() for rows.Next() { // ... 处理数据 ... } return rows.Err() }优势稳定性能游标在声明时 (DECLARE) 就固定了结果集后续每次FETCH的成本基本恒定与已读取的行数无关。一致性视图在整个迭代过程中你看到的是游标声明时刻的数据快照不会受到其他事务新增数据的影响保证了处理逻辑的一致性。代码简洁无需手动管理offset变量和循环终止条件。结论对于需要顺序、完整遍历大量数据的场景pgxcursor在性能和代码简洁性上完胜传统分页。传统分页更适合随机跳转的 UI 页面展示。6. 疑难杂症与排查清单即使设计得再好在实际部署中也可能遇到问题。下面是我在项目中遇到的一些典型情况及其解决方法。6.1 错误“cursor c_xxx does not exist”这个错误通常发生在游标生命周期管理出现混乱时。原因1在同一个pgxcursor.Querier上并发调用Query。pgxcursor.Querier本身不是协程安全的。每个游标迭代应该使用独立的Querier实例或在不同的 goroutine 中使用独立的连接。解决为每个并发遍历创建新的pgxcursor.Querier{Querier: pool, ...}连接池会管理底层连接。原因2rows对象在没有调用Close()的情况下被垃圾回收然后新的查询复用了同一个底层连接该连接上可能还存在一个未关闭的游标。解决永远使用defer rows.Close()。原因3手动传入了事务 (pgx.Tx)并在游标迭代完成前就提交或回滚了该事务。解决确保事务的生命周期完全覆盖游标的迭代过程。6.2 性能问题迭代速度慢如果发现使用pgxcursor后遍历速度不如预期。检查点1Capacity设置是否过小这是最常见的原因。默认值0是性能杀手。将其增加到 1000 或更高观察性能变化。使用网络监控工具查看数据库往返次数。检查点2客户端处理是否成为瓶颈在for rows.Next()循环中的业务逻辑可能很重。尝试先只做rows.Next()和rows.Scan()到一个临时变量但不处理看看原始读取速度如何。如果读取本身很快那么瓶颈在客户端逻辑。检查点3查询本身是否有性能问题游标只是获取结果的方式查询本身的效率WHERE 条件、索引使用是基础。使用EXPLAIN ANALYZE分析你的查询语句。检查点4网络延迟是否过高如果是跨地域访问数据库单次FETCH的延迟会被放大。尝试大幅增加Capacity比如到 5000 或 10000减少网络往返次数。6.3 内存使用超出预期Capacity设置过大或者单行数据量巨大例如包含大的TEXT或JSONB字段。诊断在迭代过程中使用 Go 的runtime.ReadMemStats或pprof来观察堆内存的增长。确认增长是否与Capacity * row_size相符。调整降低Capacity值。或者考虑修改查询语句只选择必需的字段避免SELECT *。6.4 与上下文Context取消的交互Go 的context用于取消和超时。当传递给Query的context被取消超时或手动取消时pgxcursor会尝试中止底层的数据库操作。行为在迭代过程中如果ctx.Done()下一次rows.Next()的调用可能会返回false并且rows.Err()会返回一个context.Canceled或context.DeadlineExceeded错误。最佳实践始终检查rows.Err()并妥善处理上下文取消错误进行必要的资源清理和日志记录。ctx, cancel : context.WithTimeout(context.Background(), time.Minute) defer cancel() rows, _ : querier.Query(ctx, ...) defer rows.Close() for rows.Next() { // ... } if err : rows.Err(); err ! nil { if errors.Is(err, context.DeadlineExceeded) { log.Println(Cursor processing timed out, partial data processed.) // 可能进行一些中间状态保存 return err } // 处理其他错误 }6.5 事务隔离级别的影响如前所述当使用连接池时pgxcursor默认在READ ONLY DEFERRABLE事务中运行。这通常是性能最好的选择。但如果你需要读取其他事务已提交的最新数据READ COMMITTED或者你需要在迭代过程中基于读取的数据进行写入就需要手动管理事务。// 需要读取最新已提交数据并可能写入的场景 tx, err : pool.BeginTx(ctx, pgx.TxOptions{IsoLevel: pgx.ReadCommitted}) if err ! nil { log.Fatal(err) } defer tx.Rollback(ctx) // 安全回滚 querier : pgxcursor.Querier{Querier: tx, Capacity: 500} rows, err : querier.Query(ctx, SELECT ... FOR UPDATE) // 甚至可以使用 FOR UPDATE 锁定行 // ... 迭代并可能基于读取的数据执行 tx.Exec ... tx.Commit(ctx)最后我个人在将pgxcursor用于生产环境的数据迁移服务后最大的体会是它显著简化了大数据量处理的代码复杂度将开发者的注意力从繁琐的分页管理和资源清理中解放出来回归到业务逻辑本身。它的兼容性设计pgx.Rows接口使得将其集成到现有使用pgx的项目中风险极低几乎可以做到无缝替换。对于任何需要稳定、高效处理 PostgreSQL 海量数据读操作的 Go 服务它都应该被纳入首要考虑的技术选型之中。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…