database - PREPARE 语句在几次调用后决定不使用索引,即使它更慢

标签 database postgresql go indexing go-gorm

我已经为产品的Title 创建了索引。

CREATE INDEX products_title_trgm_idx 
ON products USING gin(title gin_trgm_ops)

我尝试创建一个准备语句,当我使用相同的搜索值对其执行多个调用时,它会使用我的索引进行前 4 个查询,并且速度非常快,大约 20 毫秒。但第五次它只是随机决定不使用索引,这使得它执行 3-4 秒。为什么会发生这种情况?

PREPARE my_test AS SELECT "products"."id","products"."active","products"."title","products"."subtitle","products"."description","products"."isbn","products"."ean","products"."bznr","products"."cover_p
icture","products"."publication_date","products"."edition","products"."publisher","products"."stock","products"."delivery_time","products"."selling_price","products"."width","products"."height","products"."lengt
h","products"."weight" FROM "products" WHERE products.title ILIKE $1 LIMIT 10;

我创建了这个准备语句,然后做了

EXECUTE my_test('%Warum Frauen alles besser wissen - und trotzdem alles falsch machen%'); 

连续5次,前4次使用索引,速度超快,但第五次及之后的几次使用seq扫描,速度超慢?它为什么要这么做?我尝试禁用 seq 扫描,它会与索引一起执行并始终在 20 毫秒内执行,但我知道禁用这不是一个好主意。

最佳答案

在您的情况下,PostgreSQL 在第五次执行该语句后切换到通用计划。通常这是一个很好的举措,因为它节省了每次执行语句时规划语句的开销。它只是在你的情况下出了问题。

在前五次执行之后,PostgreSQL 会测试通用计划是否与迄今为止执行的自定义计划一样高效。你的情况似乎出了问题。因此,PostgreSQL 要么估计自定义计划太贵,要么估计通用计划太便宜。

如果没有看到两个计划的 EXPLAIN 输出,我必须猜测(从 PostgreSQL v16 开始,您可以使用 EXPLAIN (GENERIC_PLAN) 获取通用计划)。我的猜测是慢速计划使用并行顺序扫描。默认情况下,PostgreSQL 估计索引扫描相当昂贵,因为 random_page_costseq_page_cost 的四倍。如果您没有旋​​转磁盘,或者您的数据已缓存,则应减少 random_page_cost 来告知 PostgreSQL。这可能足以让 PostgreSQL 更喜欢索引扫描。其他可能的措施可能是将 max_parallel_workers_per_gather 设置为 0 来禁用并行查询。

最后,有一个开关可以完全禁用通用计划的生成:您可以将 plan_cache_mode 设置为 force_custom_plan。如果其他方法都失败了,请尝试一下。

关于database - PREPARE 语句在几次调用后决定不使用索引,即使它更慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76116100/

相关文章:

amazon-web-services - AWS RDS 代理连接

Go 泛型 : Invalid composite literal

php - 尝试使用 $wpdb 类时无法在数据库中插入数据

sql - 联合操作后如何获取记录数..?

postgresql - 在Docker容器中安装Postgis

c# - 从 postgres 取回消息

json - 在 Firebase 数据库中使用多个验证规则

database - 如何向现有表中插入列?

ruby-on-rails - 如何在 Rails 4 中通过 hstore 属性对结果进行排序?

templates - Go:在模板文件中打印 URI(echo 框架)