“cached plan must not change result type” 这个错误的出现需要两个条件:业务使用了prepare语句 与 数据库做了DDL操作
什么是PREPARE
预备语句是服务端的对象,可以用于优化性能。在执行PREPARE语句的时候,指定的查询被解析、分析、重写。当随后发出EXECUTE语句的时候,预备语句被规划和执行。这种设计避免了重复解析、分析工作。PREPARE语句创建后在整个数据库会话期间一直存在,一旦创建成功,即便是在事务块中创建,事务回滚,PREPARE也不会删除。只能通过显式调用DEALLOCATE进行删除,另外,会话结束时PREPARE也会自动删除。
报错规避方法
当我们在数据库层面做了DDL(修改字段长度,字段类型,字段的数量)操作,且业务使用了prepare语句,那就会出现缓存中的执行计划与结果类型不匹配的情况,而cache plan的设计是要保证数据类型是固定的,不可改变的。
1、重启业务程序
prepare语句会随着连接会话的断开而自动删除,一般重大的数据库DDL操作都会伴随着业务程序的重启,所以涉及到业务重启的DDL操作并不会出现这个报错。
2、杀掉数据库会话连接
这个方法同重启业务差不多,但并不是所有的DDL操作都要重启业务程序,建议将idle状态的连接进行删除,通过交替删除的方式将连接全部断开一遍,这样相对重启业务来说,影响要小一些。
3、deallocate the prepare
deallocate 这种方式更适合于单prepare会话的情况,对于业务程序上来的多并发连接的情况并不适用,不如采取方法1和方法2,示例如下:
MogDB=# create table pret(id int,col1 varchar(8));
CREATE TABLE
MogDB=# \d+ pret
Table "public.pret"
Column | Type | Modifiers | Storage | Stats target | Description
--------+----------------------+-----------+----------+--------------+-------------
id | integer | | plain | |
col1 | character varying(8) | | extended | |
Has OIDs: no
Options: orientation=row, compression=no
MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
MogDB=# insert into pret values(1,'c1'),(2,'c2');
INSERT 0 2
MogDB=# execute p1('c1');
id | col1
----+------
1 | c1
(1 row)
--修改字段长度
MogDB=# alter table pret alter col1 set data type varchar(16);
ALTER TABLE
MogDB=# \d+ pret
Table "public.pret"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
id | integer | | plain | |
col1 | character varying(16) | | extended | |
Has OIDs: no
Options: orientation=row, compression=no
--报错出现
MogDB=# execute p1('c1');
ERROR: cached plan must not change result type
--取消prepare
MogDB=# deallocate p1;
DEALLOCATE
MogDB=# execute p1('c1');
ERROR: prepared statement "p1" does not exist
--重新prepare
MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
MogDB=# execute p1('c1');
id | col1
----+------
1 | c1
(1 row)
4、使用类型转换
我们也可以使用类型转换的方式来定义prepare语句,这种方式是临时规避方式,不建议在应用程序中使用,示例如下:
MogDB=# \d+ pret
Table "public.pret"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
id | integer | | plain | |
col1 | character varying(16) | | extended | |
Has OIDs: no
Options: orientation=row, compression=no
--预编译1
MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
--预编译2
MogDB=# prepare p2 as select id::text,col1 from pret where col1=$1;
PREPARE
MogDB=# execute p1('c1');
id | col1
----+------
1 | c1
(1 row)
MogDB=# execute p2('c1');
id | col1
----+------
1 | c1
(1 row)
--修改字段类型
MogDB=# alter table pret alter column id type varchar(8);
ALTER TABLE
MogDB=# \d+ pret
Table "public.pret"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
id | character varying(8) | | extended | |
col1 | character varying(16) | | extended | |
Has OIDs: no
Options: orientation=row, compression=no
再次执行预编译语句,查看效果,p1报错,但是p2正常
MogDB=# execute p1('c1');
ERROR: cached plan must not change result type
MogDB=# execute p2('c1');
id | col1
----+------
1 | c1
(1 row)
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




