达梦报错超长记录如何分析

背景说明

    在达梦数据库上,有时候在做数据库移植或应用开发测试过程中会遇到“记录超长”的报错,例如:
在这里插入图片描述

这个报错其实根据字面意思我们可能大概知道是数据的长度超长了,对达梦数据库比较熟悉的使用者其实是知道该如何处理的,一般来说有以下两种方法:

    1. 重新初始化实例,设置更大的页大小
    1. 对报错的表开启超长记录

下面我们通过举例来讨论这个记录超长的具体原因并进行分析,分析过程中我们要提到以下几个概念:

    1. 页大小(page_size)
    1. 超长记录
    1. 行内数据与行外数据
    1. 配置参数LOB_MAX_INROW_LEN

页大小

    页大小是初始化数据库时的参数,会影响字符长度(如char/varchar)的实际最大长度和每条记录的最大长度。例如:
第一,当页大小为8K时,对于字符类型实际最大长度为3900字节(不同页大小影响字符类型实际的最大长度是不一样的,见下图所示)
在这里插入图片描述

第二,行内记录长度最大长度不超过页大小的一半,这个是大前提。
了解这两个前提对于理解以下内容比较关键,为了方便说明问题,我们使用默认的页大小page_size=8K
举个例子:

create table test_singlecol( col varchar(8000));
insert into test_singlecol values(repeat('a',4000)); --报错,因为超过3900了
insert into test_singlecol values(repeat('a',3880)); --报错,因为超过3900了
insert into test_singlecol values(repeat('a',3879)); --正常插入,还有20个自己被内置的存储占用,例如伪列rowid,trxid还有null标记等

插入4000报错可以理解,因为超过8k页的约3900的限制了,那为什么3880页报错了?其实我们知道达梦数据库中会有两个伪列,ROWID和TRXID,分别来看一下这两个伪列的长度:

select lengthb(rowid),lengthb(trxid),* from dual;

在这里插入图片描述

这里我们可以大概算一下,除此之外,varchar类型由于是变长类型,且实际数据长度大于128,还需要2个字节表示长度,所以3880+20+2=3902>3900报错了。

超长记录

   假如我们就是需要插入超过限制的大小,那么我们可以开启超长记录,那么就可以解除这个限制,因为超长的记录以blob的形式存在行外了。
超长记录存储功能是指当 DM 行存储的记录长度超过页大小一半时,是否尝试将过长的变长字符串转换为行外 BLOB 存储。

ALTER TABLE test_singlecol ENABLE USING LONG ROW;
insert into test_singlecol values(repeat('a',3880)); --正常插入
insert into test_singlecol values(repeat('a',4000)); --正常插入

在基表中,当没有指定 USING LONG ROW 存储选项时,插入 VARCHAR 数据类型的实际最大存储长度由数据库页面大小决定,具体最大长度算法如下表所示,如果指定了 USING LONG ROW 存储选项,则插入 VARCHAR 数据类型的长度不受数据库页面大小限制。VARCHAR 类型在表达式计算中的长度上限不受页面大小限制,为 32767;

行内数据与行外数据

   对于像TEXT,CLOB之类的大字段类型而言,当实际数据长度较小时就与其他字段紧凑的放在一起,称之为行内数据,如果实际数据长度较长时,实际数据就单独放在某些数据页中,而与其他字段一起存储的只是大字段头。

drop table if exists test_text;

create table test_text(col text);
insert into test_text values(repeat('a',901));
commit;
select dump(col) from test_text;

在这里插入图片描述

可以看到行内行外标记为行外,而行内长度为47,这个47也就是大字段头的长度
因为我们知道记录一旦超长被记录到行外lob中,则行内字段记录的是这个数据的大字段头信息,是固定长度47。

对比如下两种插入:
对于varchar类型表定义:

call create_table(39,'varchar(4000)');
call insert_table(39,4000);-- -2665: 记录超长
alter table  "SYSDBA"."TEST_39COL" enable using long row;
call insert_table(39,4000);--正常插入
对于text类型表定义:
drop table if exists TEST_39COL;
call create_table(39,'text');
call insert_table(39,50);-- 正常插入,行内
call insert_table(39,100);-- 正常插入,行外
call insert_table(39,4000);-- 正常插入,行外
select dump(c1),dump(c2),dump(c3),dump(c4) from TEST_39COL;

在这里插入图片描述

对比可以看出对于varchar类型定义的表,超长记录不会自动存储到行外,除非开启超长记录。而lob类型定义的表,会根据实际数据长度自动决定存储在行内还是行外,当然这里对于lob类型的数据,是否存在行内由参数LOB_MAX_INROW_LEN来控制。

LOB类型行内存储最大长度

   LOB_MAX_INROW_LEN默认为900,决定了大字段类型能放进行行内存储的最大长度(但并不绝对,还与“每条记录总长度不超过页大小一般”有关),与上述讨论的第三点息息相关,超过该值则作为行外存储,没超过则作为行内存储。

select * from v$dm_ini where para_name like 'LOB%';

在这里插入图片描述

create table test_lob(col text);
insert into test_lob values(repeat('a',901));--行外
insert into test_lob values(repeat('a',900));--行内
commit;
select dump(col) from test_lob;

在这里插入图片描述

可以看到,改参数值可以控制是否存在行内的边界长度。
那回到上述第三点讨论中,这个插入的varchar长度是100,并没有超过LOB_MAX_INROW_LEN默认值,为什么也是存在行外呢?这里我们可以大致计算一下,如果这条记录存在行内的话39*100+20=3920,也超过了3900所以只能存到行外去了。

总结:

    综上所述,达梦数据库中报错“记录超长”与上述因素都有关系,其中页大小是影响最大的一个因素,如果业务场景中涉及的表都会比较“宽”,那么建议直接将页大小设置大一些,可以避免一些记录超长的问题,但是相对来说也会更加浪费空间一些,这里需要dba做一些权衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值