资讯动态

游标也是Enumerable:postgresql_cursor 链式调用 map、lazy 与 Rails partial 渲染实战

发布时间:2026/8/23 12:14:32 来源:尧图企业网站定制
游标也是Enumerablepostgresql_cursor 链式调用 map、lazy 与 Rails partial 渲染实战【免费下载链接】postgresql_cursorActiveRecord PostgreSQL Adapter extension for using a cursor to return a large result set项目地址: https://gitcode.com/gh_mirrors/po/postgresql_cursorpostgresql_cursor 是一个面向 Rails 的 ActiveRecord PostgreSQL 游标扩展 gem它让数据库按块默认 1000 行返回大数据量结果集避免把几十万甚至上百万行一次性加载进内存。而它最容易被低估的一面是——游标对象本身就是一个 Ruby Enumerable你可以像操作普通数组一样对它链式调用map、lazy、inject甚至直接把它交给 Rails 的 partial 渲染。本文用最短的篇幅带你吃透这套游标 链式调用的玩法。一、为什么大数据量查询需要 PostgreSQL 游标先弄清楚它解决了什么问题。当你执行Product.where(id0).each { |p| ... }时数据库会把全部匹配行一次性返回给 ActiveRecord每一行都被实例化成对象后堆成一个大数组——数据量一大内存直接膨胀严重的会 OOM 崩溃。Rails 自带的find_each/find_in_batches虽然也是分块处理但有明显短板只能按主键顺序遍历无法指定任意排序主键必须是数值类型每取一块数据查询会重新执行一遍。而 PostgreSQL 的游标Cursor由数据库端保持结果集位置程序每次只FETCH一小块行比如 1000 行处理完再取下一块直到取完。排序随便写复杂查询只执行一次内存占用始终平稳。postgresql_cursor 就是把这个机制无缝接入了 ActiveRecord。二、安装 postgresql_cursor一行 Gemfile 即可在项目的Gemfile中加入一行即可使用全部游标能力gem postgresql_cursor该 gem 依赖activerecord 6.0需要 PostgreSQL 数据库支持依赖关系可参考 postgresql_cursor.gemspec。如果你想先把源码拉到本地研究执行git clone https://gitcode.com/gh_mirrors/po/postgresql_cursor装好后任意 Model 的查询链上就多了each_row、each_instance、each_row_by_sql等一系列方法由 lib/postgresql_cursor/active_record/sql_cursor.rb 和 lib/postgresql_cursor/active_record/relation/cursor_iterators.rb 注入。三、核心机制游标为什么天生是 Enumerable秘密在核心文件 lib/postgresql_cursor/cursor.rb 的第 26 行——Cursor类直接include Enumerablemodule PostgreSQLCursor class Cursor include Enumerable # 游标即集合 # ... end end再配合一个精巧的设计each_row/each_instance等方法如果不传 block返回的就是游标对象本身见 lib/postgresql_cursor/active_record/relation/cursor_iterators.rb 中return cursor的写法。这意味着你可以把游标传着走先创建、再赋值给变量、在任意时机迭代还能自由拼接 Enumerable 全家桶——map、select、reduce、any?、first、count……数据流本身依然是从数据库按块拉取只是操作接口完全变成了集合风格。四、实战一对游标链式调用 map最直观的用法是查询 → 游标 → 映射一气呵成官方 README 中的示例非常经典# 每行返回 Hash字符串键值速度最快 Product.each_row.map { |r| r[id].to_i } # [1, 2, 3, ...] # 每行返回 ActiveRecord 实例 Product.each_instance.map { |r| r.id }.each { |id| p id }对比一下不用游标时的写法链式调用让批量取字段这件事变得极其顺手。官方测试 test/test_postgresql_cursor.rb 里的test_enumerables也验证了any?这类 Enumerable 方法可以直接用在游标上。⚠️ 注意map最终会把结果收集成一个数组。如果只是想取某一列的值小数据量完全够用但数据量极大时map本身就吃内存此时请看下一节的lazy方案。五、实战二lazy 惰性游标把内存压到最低游标取数本来就是惰性的一次只装 1000 行而再链上一个lazy可以让整条处理链都保持惰性# 遍历全表累加库存任何时刻内存里只有一块数据 Product.each_instance.lazy.inject(0) { |sum, p| sum p.quantity } # 499500lazy.inject不会先把所有行物化而是边取边算处理完一块就释放一块——这正是游标 lazy组合的威力。同理lazy.map { ... }.first、lazy.take(5)这类操作也能在只取少量结果时提前终止避免无谓拉取。六、实战三把游标直接交给 Rails partial 渲染很多人不知道游标还能直接当作渲染集合丢进 viewrender partial: product, collection: Product.each_instance render partial: product, collection: Product.each_rowRails 渲染集合时会先调用size而游标专门处理了这一点——lib/postgresql_cursor/cursor.rb 第 74-78 行的注释写得很直白ActiveRecord 渲染集合时会调用 #size这里定义并返回一个占位值返回 -1于是渲染流程顺利走通partial 会按渲染引擎的节奏逐行消费游标。对于把大结果集分页展示给前端这类场景这是少见的优雅。七、新手必看的实用技巧与常见坑小结果集别用游标游标有额外的 DECLARE/FETCH/CLOSE 开销几百行的查询直接用select/pluck更快。调block_size每次从数据库取多少行由block_size控制默认 1000。配合lockFOR UPDATE逐行更新时官方建议block_size 10避免长时间锁住大批行引发死锁。Hash vs Instanceeach_row返回字符串值的 Hash不做类型转换处理量最大时最快需要时间、数值等 Ruby 类型时改用each_instance惰性转换只转你读到的属性。用select裁列Product.select(:id, :name).each_instance { ... }只取需要的列网络和内存双赢。while:/until:提前退出所有游标方法都支持选项 hash例如each_row(until: :done)block 返回值命中即停止拉取。手动fetch需包在事务里如果你自己持有游标对象逐行cursor.fetch要放在Product.transaction do ... end内或用with_hold: true让游标跨事务存活。pluck_rows/pluck_instances想要plucked 数组又要游标的分块取数时它们是 pluck 的游标版替代。八、结语源码值得十分钟postgresql_cursor 的核心就三个文件十分钟能读透lib/postgresql_cursor/cursor.rb游标核心Enumerable混入、open/fetch/close、block_size、while/until控制lib/postgresql_cursor/active_record/relation/cursor_iterators.rbRelation 上的each_row、each_instance、批处理与pluck_rowslib/postgresql_cursor/active_record/sql_cursor.rb类级原始 SQL 入口如each_row_by_sql。想动手实验可以跑起 test-app/app.rb 里的迷你示例应用先rake setup建库再rake console。一句话总结把游标当集合用map 管方便lazy 管内存partial 管展示——这就是 postgresql_cursor 处理大数据量的完整姿势。【免费下载链接】postgresql_cursorActiveRecord PostgreSQL Adapter extension for using a cursor to return a large result set项目地址: https://gitcode.com/gh_mirrors/po/postgresql_cursor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价