一、报错现象

这是一个在使用 DB2数据库过程中比较常见的错误, 报错信息如下

  1. Exception stack trace:
  2. com.ibm.db2.jcc.am.SqlException: DB2 SQL Error: SQLCODE=-805, SQLSTATE=51002, SQLERRMC=NULLID.SYSLH203 0X5359534C564C3031, DRIVER=3.66.46

二、关键知识点

先说明几个知识点:

[Packages]

DB2 中的包是一组信息,其可以控制任何静态SQL语句的编译,部分控制着任何动态SQL语句的编译 以及可以影响在其范围内发出的任何SQL请求的执行。

包信息包括编译期间使用的优化级别、执行期间是否对符合条件的游标使用阻塞以及执行期间使用的并行度等项目。

对于静态SQL语句,包还有一个和每个SQL语句相关联的section

[Section]

因为应用程序可以有许多不同的静态和动态性质的 SQL 语句,所以包也一样。DB2 UDB 将包细分为更小的单元,称为section。 section包含有关 SQL 语句本身(如果存在)以及有关在应用程序中找到 SQL 语句的上下文的信息。

section是 SQL 语句的实际可执行体现。 它包含 DB2 UDB 产生指定结果所需的逻辑和数据访问方法。 一个section由一系列运算符和任何关联的操作数组成,这些操作数概述了数据访问的执行顺序和最佳操作。

section是 SQL 语句编译的最终结果。 SQL 编译器确定满足 SQL 语句的最有效方法,并生成一个section来实现该计划。

[DB2 CLI Packages]

DB2 调用级接口 (DB2 CLI) 是 DB2 系列数据库服务器的可调用 SQL 接口。 可调用 SQL 接口是用于数据库访问的应用程序接口 (API),它使用函数调用来调用动态 SQL 语句。在创建或迁移数据库时,或者给数据库服务端打补丁时,DB2 CLI 包会自动绑定到数据库。

在 CLI 应用程序中分配的每个语句句柄将占用 CLI 包中的一个section。

默认的:

  • DB2 CLI包在NULLID集合中创建
  • 为每个隔离级别(4 个隔离级别)和游标保持性 (2种) 创建了三个小包和三个大包。 (3*4*2 + 3*4*2=共48包
  • 每个小包允许每个连接最多 65 个语句句柄,每个大包每个连接最多允许 385 个语句,其中大包和小包各有2个句柄是提供给update/delete语句和execute immediate语句(同一个连接中这两种句柄可以被多次复用),所以每个连接中其他所有语句可以使用的句柄数初始默认为(3 * 63) + (3 * 383) = 1338 个。

CLI包的命名方式:SYSS[H|N]xyy 和 SYSL[H|N]xyy

  • 'S'代表小包,'L'代表大包
  • 'H' 代表 WITH HOLD,'N' 代表 NOT WITH HOLD
  • 'x'是隔离级别:0=NC, 1=UR, 2=CS, 3=RS, 4=RR
  • 'yy' 是包迭代 00 到 FF

默认下面这些包会在数据库中预先创建好:

db2 "select pkgname from syscat.packages where pkgSchema='NULLID' and pkgname like '%SYS%'"

三、解决办法

3.1. Case A

如果提示的缺少的package是上面提到的预先创建好的package其中之一,则说明那个package因为某些未知原因在数据库中丢失了。

可以重新bind一次来创建包

  1. cd ~db2inst1/sqllib/bnd
  2. db2 "bind @db2cli.lst blocking all sqlerror continue grant public "

3.2. Case B

如果提示缺少的包的号码大于上面提到的预先创建好的任何package号码,则说明当前存在的包不够用了,程序在申请新的package。

比如应用报805错误说找不到NULLID.SYSLH203这个包,则说明应用已经使用了SYSSH200, SYSSH201, SYSSH202 (3个小包) 和 SYSLH200, 201, 202 (3个大包) 并且正在寻找下一个大包。

首先需要知道,单次应用连接中可使用的CLI Package的句柄数量是有上限的,所以一般有2种情况会导致这种场景:

  • 应用程序代码中存在未正常释放已经不需要使用的语句句柄。
  • 如果程序不存在上述句柄未释放的情况,则可能是发生报错的时间点应用承载了过高的并发压力,而当前单次连接的语句句柄上限满足不了业务需求了

对于代码层的原因,需要排查代码来解决问题根本原因。比较常见的出现问题的语句为prepareStatement, DECLARE CURSORS, 或者嵌入式SQL(静态SQL)等,每一个独立的这种语句都会占用一个句柄,使用完毕后需要调用Statement.close()方法释放句柄。

对于第二种情况,则需要手动增加CLI 包的数量

  1. cd ~db2inst1/sqllib/bnd
  2. db2 "bind @db2cli.lst blocking all sqlerror continue grant public CLIPKG X"

这里的"X"是指可以创建的大包的数量,可以指定的范围为3-30。

四、实验

4.1. 错误复现

这里有一个Java Demo,用来复现SQL0805N错误。

其中通过调用prepareStatement语句但不正常释放来模拟句柄数耗尽。

  1. import java.sql.*;
  2. import java.io.*;
  3. import java.math.*;
  4. import java.util.*;
  5. public class db2805_1
  6. {
  7. public static void main(String[] args)
  8. {
  9. try{
  10. Class.forName("com.ibm.db2.jcc.DB2Driver").getDeclaredConstructor().newInstance();
  11. Connection conn= DriverManager.getConnection("jdbc:db2://10.211.55.10:60000/sample","db2inst1","db2inst1");
  12. if (conn==null)
  13. {
  14. System.out.println("Can not connect to database");
  15. }
  16. PreparedStatement pstmt = null;
  17. ResultSet rs=null;
  18. String sql = "select count(*) from emp";
  19. int i=1;
  20. while(i<10000){
  21. pstmt = conn.prepareStatement(sql); /*每次调用占一个section*/
  22. pstmt.execute();
  23. System.out.println("i="+i);
  24. i++;
  25. }
  26. } catch(Exception e){
  27. e.printStackTrace();
  28. }
  29. }
  30. }

数据库端的包的情况如下(默认):

  1. -- 通过SQL检查
  2. $ db2 connect to sample
  3. Database Connection Information
  4. Database server = DB2/LINUXX8664 10.1.4
  5. SQL authorization ID = DB2INST1
  6. Local database alias = SAMPLE
  7. $ db2 "select substr(PKGNAME,1,20),TOTAL_SECT,CREATE_TIME,ISOLATION,BOUNDBYTYPE,PKG_CREATE_TIME,LASTUSED from syscat.packages where PKGNAME like 'SYSSH20%' or PKGNAME like 'SYSLH20%'"
  8. 1 TOTAL_SECT CREATE_TIME ISOLATION BOUNDBYTYPE PKG_CREATE_TIME LASTUSED
  9. -------------------- ---------- -------------------------- --------- ----------- -------------------------- ----------
  10. SYSLH202 385 2021-06-18-18.18.44.883818 CS S 2021-06-18-18.18.44.883818 01/01/0001
  11. SYSLH201 385 2021-06-18-18.18.44.874181 CS S 2021-06-18-18.18.44.874181 01/01/0001
  12. SYSLH200 385 2021-06-18-18.18.44.865003 CS S 2021-06-18-18.18.44.865003 01/01/0001
  13. SYSSH202 65 2021-06-18-18.18.44.818740 CS S 2021-06-18-18.18.44.818740 01/01/0001
  14. SYSSH201 65 2021-06-18-18.18.44.816721 CS S 2021-06-18-18.18.44.816721 01/01/0001
  15. SYSSH200 65 2021-06-18-18.18.44.814545 CS S 2021-06-18-18.18.44.814545 01/01/0001
  16. 6 record(s) selected.
  17. -- 也可以这样检查
  18. $ db2 connect to sample
  19. Database Connection Information
  20. Database server = DB2/LINUXX8664 10.1.4
  21. SQL authorization ID = DB2INST1
  22. Local database alias = SAMPLE
  23. [db2inst1@db01] [~]
  24. $ db2 "list packages for all"|grep -i NULLID|egrep -i 'SYSSH2|SYSLH2'
  25. SYSLH200 NULLID SYSIBM 385 Y 3 CS B
  26. SYSLH201 NULLID SYSIBM 385 Y 3 CS B
  27. SYSLH202 NULLID SYSIBM 385 Y 3 CS B
  28. SYSSH200 NULLID SYSIBM 65 Y 3 CS B
  29. SYSSH201 NULLID SYSIBM 65 Y 3 CS B
  30. SYSSH202 NULLID SYSIBM 65 Y 3 CS B

执行程序报错:

可知,在申请第1339个section时失败,这里和上面说明的单次连接1338个句柄上限一致。

4.2. 错误修复

这里给Demo代码添加正常释放语句句柄的逻辑,如下

  1. ...
  2. while(i<10000){
  3. pstmt = conn.prepareStatement(sql);
  4. pstmt.execute();
  5. pstmt.close(); /*释放句柄*/
  6. System.out.println("i="+i);
  7. i++;
  8. }
  9. ...

再次执行成功

4.3. APP占用section数的监控

在开启了实例级别的监控开关后,可以通过采集应用的snapshot来获取其对于section的占用情况,例如:

  1. Section number = 35
  2. Application creator = NULLID
  3. Package name = SYSLH206

则应用已经占用的句柄数可以由上面的信息计算出来。因为APP现在已经用到SYSLH206包的35个section,则其已经使用过了SYSSH200, SYSSH201, SYSSH202 (3个小包) 和 SYSLH200, 201, 202, 203, 204, 205(6个大包) 的所有section,具体数目为:3*64 + 6*384 + 35 = 2531

正常释放句柄的APP

这里我们来观察下正常的APP在获取CLI包section时的情况,demo程序为

  1. while(i<10000){
  2. pstmt = conn.prepareStatement(sql);
  3. pstmt.execute();
  4. pstmt.close(); /*释放句柄*/
  5. System.out.println("i="+i);
  6. i++;
  7. }

可知:句柄每次不需要使用后都正常释放的APP,由于这里只存在prepareStatement的调用和释放,所以使用的section是固定的,为第一个小包SYSSH200的1个section。

未正常释放句柄的APP

这里为了方便观察,给demo程序后面加了一层模拟休眠的SQL,从而模拟程序处于未提交状态,另外prepareStatement语句每次循环使用完后并未释放句柄

  1. String sql = "SELECT * FROM sysibm.systables where fid=?";
  2. String sql1 = "call dbms_alert.sleep(1000000)"; /*休眠*/
  3. int i=1;
  4. while(i<1338){
  5. pstmt = conn.prepareStatement(sql);
  6. pstmt.setInt(1,5);
  7. pstmt.execute();
  8. System.out.println("i="+i);
  9. i++;
  10. }
  11. pstmt = conn.prepareStatement(sql1);
  12. pstmt.execute();

执行程序,可以看到成功执行了1338个语句(含一个sleep语句调用)

再采集DB2 snapshot观察下section的占用

可以计算下:3*63 + 3*383 = 1338

可知:未正常释放句柄时,单次连接中句柄的占用是逐渐递增的,直到达到上限为止

4.4. 句柄未释放是否影响其他并发连接

以上一小节agentid=562的应用为对比,再执行另一段未正常释放句柄的程序,来观察section的未释放是否不会影响其他并发的连接

显而易见,是无影响的。

五、思考总结

5.1. 最开始的思考误区

一开始以为DB2 CLI包是一组由多个应用连接共享的资源,每个连接对于section的申请按照先到先得的原则,占用句柄不释放的异常应用程序最终会消耗光总的section从而产生805报错。

此种思考结论,不能解释应用人员提出来的:出现报错后再次重试可以继续执行而未出现报错,以及别的一些应用访问数据库正常的现象。

5.2. DB2内存结构

这里主要说明下DB2代理私有内存。

每个DB2 代理进程都有自己的私有内存工作区域,以执行任务。代理进程将代表应用程序使用内存来优化、构建和执行访问计划、执行排序、记录游标信息,收集统计信息等。

5.3. SQL语句工作流程

可知,SQL的执行是依赖于最终的编译结果section的获取。

对于CLI 包的调用,也应该是遵循这个过程,通过JDBC调用DB2 CLI接口时,程序中包含的PrepareStatement、Execute Immediate等语句都需要申请section,最终从CLI Package中获取section并加载到自身代理的私有内存中。但是,在同一个应用连接中,CLI Package所包含的section个数是有上限的,如果存在已占用的语句句柄在执行完并未正常释放时,最终将导致达到上限而报错。

并且,不同的应用连接在数据库连接层的连接代理负责自己那一部分的包和section的获取和加载到私有内存,即代理间是独立的非共享的,所以不存在最开始提到的那个思考误区。

六、参考文章

https://www.ibm.com/support/pages/75-ways-demystify-db2-9-tech-tip-db2-cli-packages-demystified

https://www.ibm.com/support/pages/sql0805n-package-nullidsyslh21e-was-not-found

https://www.ibm.com/support/pages/how-many-concurrently-running-statements-allowed-db2-java-application-and-how-increase-it

DB2 SQL0805N解决和思考的更多相关文章

  1. ajax的content-download时间过慢问题的解决与思考

    其次,查看出现延迟问题的业务页面和不出现延迟的业务页面对这一组件的调用区别. 通过对比,也没有发现两个组件有何不同.(故这一奥秘,有兴趣的同学可以联系我一起讨论.....我可以把源码发给你) 经过多次 ...

  2. 关于DB2 SQL0805N找不到程序包的错误解决办法

    DB2在执行SQL语句的时候会使用内部定义的包(package)来保持不同级别的游标的稳定性, 包的名字就是“ULLID.SYSLH2XX“. DB2 里面默认的时候会创建3个这样的包即SYSLH20 ...

  3. db2 SQL6036N解决办法

    问题背景: 数据库在进行大量的运算和数据处理的过程中,IO.CPU等资源消耗非常高的时候,强制停止数据库.db2stop force 结果数据库命令迟迟没有响应.这个时候对数据库进行其他操作均无响应, ...

  4. DB2死锁解决办法

    db2 命令行,1.用管理员用户登录:db2 connect to 你的数据库名 user 用户名 using 密码 2.db2 "get snapshot for locks on 数据库 ...

  5. db2疑难解决

    http://www-01.ibm.com/support/knowledgecenter/?lang=zh#!/SSEPGG_9.5.0/com.ibm.db2.luw.messages.sql.d ...

  6. 转载:遇到BITMAP CONVERSION TO ROWIDS 后解决与思考

    今天遇到一个案例,有点价值写下来,以后多看看 SQL: select t.order_id, t.spec_name, t.staff_code, t.staff_code as xxbStaffCo ...

  7. 解决一道leetcode算法题的曲折过程及引发的思考

    写在前面 本题实际解题过程是 从 40秒 --> 24秒 -->1.5秒 --> 715ms --> 320ms --> 48ms --> 36ms --> ...

  8. 关于ajax的content-download时间过慢问题的解决方案与思考

    前言:   做前端架构很久很久了,经常到我这里都是些棘手的问题,之前没有养成很好的记录问题的习惯,以后会努力成文,积累. 于是今天就有个这篇文章.关于ajax的content-download时间过慢 ...

  9. css文字环绕图片--遇到的问题及解决方法

    一.前言 需要实现一个文字环绕图片的效果,心想so easy嘛. 1)代码部分 <style> .img-left { border: 3px solid #005588; width:3 ...

随机推荐

  1. Linux下线程pid和tid

    #include <stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/sy ...

  2. SE_Work0_回顾与展望

    项目 内容 课程:北航-2020-春-软件工程 博客园班级博客 要求:阅读推荐博客并回答问题 热身作业阅读部分要求 我在这个课程的目标是 提升团队管理及合作能力,开发一项满意的工程项目 这个作业在哪个 ...

  3. MyBatis进阶--接口代理方式实现Dao 和动态SQL

    MyBatis接口代理方式实现Dao层 接口代理方式-实现规则 传统方式实现Dao层,我们既要写接口.还要写实现类.而MyBatis框架可以帮助我们省略写Dao层接口实现类的步骤.程序员只需要编写接口 ...

  4. (五)Jira Api对接:修改任务状态

    项目迭代结束后我们需要把sprint下面的story.task任务状态修改到结束状态,如果手动修改会花费不少时间,本文就介绍如何通过jira api自动修改任务状态,提高工作效率. 一.查看任务工作流 ...

  5. istio sidecar流量处理机制及配置

    sidecar 介绍 在istio的流量管理等功能,都需要通过下发的配置应用到应用运行环境执行后生效,负责执行配置规则的组件在service mesh中承载应用代理的实体被称为side-car Ist ...

  6. 攻防世界(八)web2

    攻防世界系列:web2 1.代码审计  知识补充: strrev(string):反转字符串 strlen(string):字符串长度 substr(string,start,length):截取字符 ...

  7. sed -n "29496,29516p" service.log:从29496行开始检索,到29516行结束

    在工作中常用的Linux命令  javalinux 发布于 2019-07-24   约 11 分钟 前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://gith ...

  8. keepalived的脑裂问题与解决

    Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工 ...

  9. Play-book格式写法

    Play-Book playbook的组成 play 角色(主机或者主机组) task 任务,演戏的动作 总结:playbook是有多个play组成,一个play有多个task:剧本由一个或者多个演员 ...

  10. Azure Synapse Link for Dataverse

    MyBuild - Scale, analyze and serve Microsoft Dynamics 365 application data with Azure 本周的微软Bulid大会上发 ...