首页 > 其他分享 >[转帖]ORA-01450 maximum key length (3215) exceeded

[转帖]ORA-01450 maximum key length (3215) exceeded

时间:2024-01-23 18:24:47浏览次数:32  
标签:3215 01450 column text some maximum 索引 创建 table

一、 问题背景

给一个业务表online建索引时遇到了ORA-01450 maximum key length (3215) exceeded报错,看字面意思是字段太长了,检查表字段类型发现基本都是nvarchar2(2000),有些字段(例如unit)明显是不需要这么长的,表的设计有问题,联系开发按实际需求改短后能正常创建。

奇怪的是表的id字段类型也是nvarchar2(2000),但上面是有索引的,好奇为啥这个字段就能建上,以及为啥maximum key length是3215。

 

二、 报错分析

根据网上文章,9i之后每个index key最大只能为block size的80%。理论上8k的块可以创建最大长度为8096*80%约为6400左右长度的index。但是,online创建(包括rebuild)的过程中会生成一个中间的IOT表,用来记录创建过程中的变化。IOT表的限制比较严格,导致8k的block size最大长度只能有3215。当然普通创建的索引也是有限制的:ORA-01450: maximum key length (6398) exceeded。

按上面的建测试表和索引,发现的确online创建报错,而普通创建可以成功。因为nvarchar2(2000)字段最大可能长度是4000,创建索引时并不会看实际字段长度,直接按的最大长度。

建联合索引会报错,因为nvarchar2(2000)+nvarchar2(2000)字段最大可能长度是8000

再测试varchar2(2000)类型,发现普通创建和online都能成功,因为varchar2(2000)字段最大可能长度是2000

 

关于索引key最大长度,在文档 ID 136158.1中给出了不同BLOCK SIZE的限制,你会发现8K BLOCK SIZE的maximum key length 文档中写的是3218而不是我们遇到的3215。这可能是由于文档是针对8i的版本,在新版本中这个值变成了3215。

ORA-01450 maximum key length (758) exceeded  ->(2K Block)
ORA-01450 maximum key length (1578) exceeded ->(4K block)
ORA-01450 maximum key length (3218) exceeded ->(8K Block)
ORA-01450 maximum key length (6498) exceeded ->(16K Block)

报错中限制的KEY SIZE包含:索引的长度+存储索引长度的空间(2字节) + ROWID (6字节)+存储ROWID长度占用空间 (1字节),所以真正能够存放的列数据的长度只有3218-2-6-1=3209,新版本应该是3215-2-6-1=3206。

  • 这里的3209并不是实际的数据的长度,而是定义的列的长度。就像前面的例子,定义NVARCHAR2(2000),即使里面只存放了一个字符,创建索引时也会报这个错。ORACLE担心以后里面存放的数据万一超过了,索引那边没办法交代,所以干脆从源头上掐死。
  • 那能不能先定义一个小列,创建完索引后再把这个列的值改大?不行,你会遇到报错:ORA-01404: ALTER COLUMN will make an index too large,告诉你加大列长度的命令可能会导致索引太大
  • 这里面还有一个陷阱,就是你索引创建好了,一直使用也没问题,但是当你ONLINE REBUILD的时候却发现他的KEY SIZE超过限制了,导致索引只能不ONLINE的REBUILD,这对于24*7的系统而且必须REBUILD的情况比较痛苦。

 

常用的数据类型的KEY长度计算如下:

日期类型的长度是7;字符类型就是字段定义时候的长度;数字类型是22(数字类型的长度=精度/2+1),如果是负数,那么长度要再加1;如果是函数索引,那就要按照函数索引的返回值来进行计算。

 

为什么ONLINE创建只能使用不到BLOCK SIZE一半的空间?

ORACLE的管理手册中指明了索引的大小不能大于BLOCK_SIZE的一半,然后这一半的空间去掉ORACLE自己的PCTFREE、INITRANS以及BLOCK HEADER等等预留空间,实际可以使用的空间比一半要小很多。

当ONLINE创建一个索引,ORACLE为这个表的变化创建一个中间表,创建好后,ORACLE用表数据的一致性拷贝去创建一个新的索引,然后再把变化的记录拷贝到新创建的索引中,最后更新数据字典,删除临时段并删除这个中间表。这个过程将会锁表两次(ROW SHARE MODE)。一次是开始创建中间表时,另一次是结束时删除中间表。

中间表是一个名字类似SYS_JOURNAL_NNNNN的IOT表,其中的NNNNN是ONLINE REBUILD的索引的OBJECT_ID。因为IOT表的限制只能使用BLOCKSIZE的40%左右,而且这个IOT表的KEY就是索引中使用的KEY并加上ROWID的值,所以只有ONLINE创建或者REBUILD索引的时候会碰到这个问题。

 

下面来做一个演示,先创建一个表:

create table test(a varchar2(10),b varchar2(11),c varchar2(12),d number(10),e varchar2(13));

然后打开跟踪并ONLINE的创建索引:

create index idx_test on test(a,b,c,d,e) online;

关闭跟踪并查看TRACE文件,可以发现如下语句:

  1. create table "SYS"."SYS_JOURNAL_74346" (C0 VARCHAR2(10), C1 VARCHAR2(11), C2 VARCHAR2(12), C3 NUMBER(10,0), C4
  2. VARCHAR2(13), opcode char(1), partno number, rid rowid, primary key( C0, C1, C2, C3, C4 , rid )) organization
  3. index TABLESPACE "SYSTEM"

其中前面的C0,C1等列就是索引的KEY值,索引由几列组成,临时IOT表也会对应创建,后面三列是不变的,根据字面意思推测应该是操作的代码(增加、删除、更新) 、分区号(分区索引用到)、ROWID。而主键是由所有的KEY值和ROWID列组成,这也正好跟前面的长篇大论相吻合。至于IOT为啥只能用一半,有些说是为了B*TREE的分裂,有些说是ORACLE老版本的小问题,结果为了兼容一直没改。

 

四、解决方法

查询文档,这个问题其实有挺多绕过的方法,整理学习一下。

1. 改短字段后创建

对于表设计明显不合理的情况,这是比较合理的方法。

查看字段实际最大长度

  1. select max ( length ( text_column ) ) mx_char_length,
  2. max ( lengthb ( text_column ) ) mx_byte_length
  3. from some_table;

改短字段长度

alter table some_table modify text_column nvarchar2(100);

如果确实不能改短,下面有一些workaround,但它们都有各自的限制,需要根据实际选择。

 

2. 不使用online创建

对于前面的例子,nvarchar2(2000)的字段可以用这个方法绕过最大长度限制。但也就像之前写的,这个长度没办法创建联合索引,以后也不能使用online rebuild,对于7*24小时的系统,如果是大表可能难以接受。

 

3. 使用更大的block size存储索引

将索引存放在单独的表空间,并将表空间block size设为16k或32k,当然也需要先设置好DB_nK_CACHE_SIZE 参数。这种方法打破了DB的标准化,可能会使运维管理更加复杂。

  1. alter system set DB_32K_CACHE_SIZE=256M;
  2. create tablespace tblsp_32k_blocks datafile 'tblsp_32k_blocks' size 1m blocksize 32768;
  3. create index text_index on some_table(text_column) tablespace tblsp_32k_blocks;

 

4. 创建基于STANDARD_HASH函数的索引

standard_hash函数会返回一个固定长度的值,在等值查询时能用到该索引。

  1. create index text_index on some_table(standard_hash(text_column));
  2. select * from some_table where text_column = 'this';
  3. ----------------------------------------------------------
  4. | Id | Operation | Name |
  5. ----------------------------------------------------------
  6. | 0 | SELECT STATEMENT | |
  7. |* 1 | TABLE ACCESS BY INDEX ROWID BATCHED| SOME_TABLE |
  8. |* 2 | INDEX RANGE SCAN | TEXT_INDEX |
  9. ----------------------------------------------------------

但范围查询和like查询都用不到

  1. select * from some_table where text_column >= 't' and text_column<'u';
  2. ----------------------------------------
  3. | Id | Operation | Name |
  4. ----------------------------------------
  5. | 0 | SELECT STATEMENT | |
  6. |* 1 | TABLE ACCESS FULL| SOME_TABLE |
  7. ----------------------------------------
  8. select * from some_table where text_column like 'this%';
  9. ----------------------------------------
  10. | Id | Operation | Name |
  11. ----------------------------------------
  12. | 0 | SELECT STATEMENT | |
  13. |* 1 | TABLE ACCESS FULL| SOME_TABLE |
  14. ----------------------------------------

 

5. 创建基于substr函数的索引

对于范围查询,可以使用基于substr函数的索引(like依然用不到),但是它可能会导致查询效率较低。

  1. create index text_substr_index on some_table(substr(text_column,1,10));
  2. select * from some_table where text_column = 'this';
  3. -----------------------------------------------------------------                     
  4. | Id  | Operation                           | Name              |                     
  5. -----------------------------------------------------------------                     
  6. |   0 | SELECT STATEMENT                    |                   |                     
  7. |*  1 |  TABLE ACCESS BY INDEX ROWID BATCHED| SOME_TABLE        |                     
  8. |*  2 |   INDEX RANGE SCAN                  | TEXT_SUBSTR_INDEX |                     
  9. -----------------------------------------------------------------                     
  10.                                                                                         
  11. select * from some_table where text_column >= 't' and text_column < 'u';
  12. -----------------------------------------------------------------          
  13. | Id  | Operation                           | Name              |          
  14. -----------------------------------------------------------------          
  15. |   0 | SELECT STATEMENT                    |                   |          
  16. |   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| SOME_TABLE        |          
  17. |   2 |   INDEX RANGE SCAN                  | TEXT_SUBSTR_INDEX |          
  18. -----------------------------------------------------------------
  19.  
  20. select * from some_table where text_column like 'this%';
  21. ----------------------------------------                   
  22. | Id  | Operation         | Name       |                   
  23. ----------------------------------------                   
  24. |   0 | SELECT STATEMENT  |            |                   
  25. |   1 |  TABLE ACCESS FULL| SOME_TABLE |                   
  26. ----------------------------------------

 

6. 定义virtual列

虚拟列其实就是对列进行运算或者在列上使用函数,oracle在运行时才会计算该列的值。我们可以用standard_hash函数建虚拟列,然后对该列建普通索引。虚拟列相比函数索引有以下好处:

  • 优化器可获得虚拟列的统计信息
  • 能够看到索引值,更易于理解
  1. alter table some_table add text_hash varchar2(40 char) as (standard_hash(text_column));
  2. create index vc_text_hash_index on some_table (text_hash);

 

7. 使用全文索引(Oracle Text Index)

创建时需要指定indextype子句,查询时使用contains操作符

  1. create index oracle_text_index on some_table(text_column) indextype is ctxsys.context;
  2. select * from some_table where contains (text_column,'value')>0;

 

参考

https://blog.csdn.net/tnndwdl/article/details/78452967

https://blogs.oracle.com/sql/how-to-fix-ora-01450-maximum-key-length-6398-exceeded-errors

ORA-01450 and Maximum Key Length - How it is Calculated (文档 ID 136158.1)

</article>

标签:3215,01450,column,text,some,maximum,索引,创建,table
From: https://www.cnblogs.com/jinanxiaolaohu/p/17983095

相关文章

  • CF1201C - Maximum Median
    思路二分答案。对于一个mid,查询中位数要是为mid的话至少要做多少次操作,最小操作次数就是排序后从中位数开始计算max(0,mid-v[i])的和ac代码#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;consti64inf=8e18;typedefpair<int,int>pii;cons......
  • Maximum And Queries (hard version)
    题目传送门感觉这题比\(\rmF\)难啊,\(\rmF\)就是个板子,但为啥这题是蓝的,\(\rmF\)是紫的。思路首先考虑\(nq\)怎么做。发现很简单,按位贪心就行了。具体地说,从大到小枚举二进制位,判断答案中能否出现这一位,若\(i\)当前这一位没有值,那么必须被补全到这个值,否则无所谓,然......
  • ICPC2021Kunming G Find the Maximum 题解
    QuestionFindtheMaximum给出一个树,每个点有一个权值\(b_n\),求一条树上路径\(V\),要求\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\)最大,其中\(x\)是自己选择的一个树Solution先转化一下\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\),得到\[\frac{\sum_{u\inV(-x^2+b_......
  • EM(Expectation-Maximum)算法
    EM算法简介EM算法的核心分为两步E步(Expection-Step)M步(Maximization-Step)因为在最大化过程中存在两个参量,其中若知道,则知道;若知道,则知道。且两个量未存在明显的关系,但又互相依存可以采用EM算法其中主要思想为:首先随机初始化参数然后求的在参数下按照极大似然估计求得参数然后根据参......
  • CF1881F Minimum Maximum Distance 题解
    因为白点对\(f_i\)没有贡献,所以可以重构出一棵原树的子树,使得所有的叶子都为标记点且标记点数量不变(没有删去标记点)。因为没有标记被删去且结构不变,所以这棵树的答案与原树答案相同。现在,对于所有节点,到它距离最大的标记点一定在叶子上。那么问题就变为:求出树上任意一点到所有......
  • Nacos启动:[NACOS HTTP-POST] The maximum number of tolerable server reconnection e
    一、表象二、分析源码:publicHttpRestResult<String>httpPost(Stringpath,Map<String,String>headers,Map<String,String>paramValues,Stringencode,longreadTimeoutMs)throwsException{finallongendTime=System.currentTi......
  • 【刷题笔记】124. Binary Tree Maximum Path Sum
    题目Givena non-empty binarytree,findthemaximumpathsum.Forthisproblem,apathisdefinedasanysequenceofnodesfromsomestartingnodetoanynodeinthetreealongtheparent-childconnections.Thepathmustcontain atleastonenode anddoes......
  • ICPC2022Xian E Find Maximum 题解
    LinkICPC2022XianEFindMaximumQuestion定义\(f(x)\)求Solution通过打表我们可以发现\(f(x)\)表示三进制表达中有效位数与数码和之和接下来考虑如何获得最大的\(f(x)\)贪心的去考虑,假设答案为\(Ans\),\((Ans)_3\)肯定是前几位和\((R)_3\)的前几位一样,然后某一......
  • 【刷题笔记】104. Maximum Depth of Binary Tree
    题目Givenabinarytree,finditsmaximumdepth.Themaximumdepthisthenumberofnodesalongthelongestpathfromtherootnodedowntothefarthestleafnode.Note:Aleafisanodewithnochildren.Example:Givenbinarytree[3,9,20,null,null,15,7],......
  • LeetCode #1131 Maximum of Absolute Value Expression 绝对值表达式的最大值
    安装Flutter环境首先配置flutter3开发环境,照着官方教程傻瓜式安装即可。>>安装和环境配置|Flutter中文文档|Flutter中文开发者网站注意在国内网络环境下需要进行一些额外的环境配置:>>在中国网络环境下使用Flutter|Flutter中文文档|Flutter中文开发者网站Description......