MySQL查看字段长度

发布时间: 2023-11-21 11:46 阅读: 文章来源:1MUMB2177PS
一文中讲过了关于 MySQL 在 Server 层和 InnoDB 层的限制,但是限制的算法没有详细展开,这里进行补充说明,先回顾一下 MySQL 的两个限制:

1. MySQL Server 层 的限制为单条记录的大小不超过 65535 字节;

2. InnoDB 层 不能超过 innodb_page_size 大小的一半(实际上还要小一点,因为要扣除一些页中元数据信息), 以默认的 16K 设置为例,其限制为 8126。

另:以下计算方式均已 MySQL 5.7.27 进行说明。

二、Server 层限制的计算方法

2.1 计算过程

一般说来,如果是 MySQL Server 层做了限制,则返回如下报错:

Row size too large. The maximum row size for the used table type,not counting BLOBs, is 65535.This includes storage overhead, check the manual.You have to change some columns to TEXT or BLOBs

所以可以根据这个报错来定位到计算方式,其关键函数路径如下:

mysql_prepare_create_table/* sql/sql_table.cc:3474 */--> mysql_create_frm/* sql/unireg.cc:107 */--> pack_header/* sql/unireg.cc:733 */

在 pack_header 中存在这部分代码:

if (reclength > (ulong) file->max_record_length()) /* 右值为 65535 */{my_error(ER_TOO_BIG_ROWSIZE, /* 这个就是上面的报错信息对应的状态值 */MYF(0),static_cast(file->max_record_length()));DBUG_RETURN(1);}

重点在于 reclength 的值是否 大于 65535。因此了解一下 reclength 的计算过程:

/* sql/unireg.cc:843 */if (field->offset + data_offset + length > reclength)reclength= field->offset + data_offset + length;/*field->offset 的计算方式sql/sql_table.cc:3816*/while ((sql_field=it++)) { /* 省略部分 */sql_field->offset= record_offset;/* 当前记录的数据偏移量 */if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)auto_increment++;if (sql_field->stored_in_db)record_offset+= sql_field->pack_length; /* 累加当前字段的pack_length */}/*data_offset 的计算方式sql/unireg.cc:139*/data_offset= (create_info->null_bits + 7) / 8;/*length 的计算方式sql/unireg.cc:833*/length=field->pack_length; /* 这个pack_length就是和字段类型相关,以varchar为例*//* sql/sql_table.cc:3082 */sql_field->sql_type=MYSQL_TYPE_VAR_STRING;sql_field->pack_length= calc_pack_length(sql_field->sql_type,(uint) sql_field->length);关于 `calc_pack_length` 的计算方式如下:size_t calc_pack_length(enum_field_types type, size_t length){switch (type) {case MYSQL_TYPE_VAR_STRING:case MYSQL_TYPE_STRING:case MYSQL_TYPE_DECIMAL: return (length);case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2));case MYSQL_TYPE_YEAR:case MYSQL_TYPE_TINY: return 1;case MYSQL_TYPE_SHORT : return 2;case MYSQL_TYPE_INT24:case MYSQL_TYPE_NEWDATE: return 3;case MYSQL_TYPE_TIME: return 3;case MYSQL_TYPE_TIME2:return length > MAX_TIME_WIDTH ?my_time_binary_length(length - MAX_TIME_WIDTH - 1) : 3;case MYSQL_TYPE_TIMESTAMP: return 4;case MYSQL_TYPE_TIMESTAMP2:return length > MAX_DATETIME_WIDTH ?my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;case MYSQL_TYPE_DATE:case MYSQL_TYPE_LONG: return 4;case MYSQL_TYPE_FLOAT : return sizeof(float);case MYSQL_TYPE_DOUBLE: return sizeof(double);case MYSQL_TYPE_DATETIME: return 8;case MYSQL_TYPE_DATETIME2:return length > MAX_DATETIME_WIDTH ?my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;case MYSQL_TYPE_LONGLONG: return 8;/* Don‘t crash if no longlong */case MYSQL_TYPE_NULL: return 0;case MYSQL_TYPE_TINY_BLOB:return 1+portable_sizeof_char_ptr;case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr;case MYSQL_TYPE_MEDIUM_BLOB:return 3+portable_sizeof_char_ptr;case MYSQL_TYPE_LONG_BLOB:return 4+portable_sizeof_char_ptr;case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr;case MYSQL_TYPE_JSON: return 4+portable_sizeof_char_ptr;case MYSQL_TYPE_SET:case MYSQL_TYPE_ENUM:case MYSQL_TYPE_NEWDECIMAL:abort(); return 0;// This shouldn‘t happencase MYSQL_TYPE_BIT: return length / 8;default:return 0;}}

2.2 小结

根据上面计算方式的梳理,在 MySQL Server 层计算长度的公式,可以写成这样的伪代码:

data_offset = (number_null_field + 7) / 8; /*number_null_field表示允许为null的字段数*/total_length = 0;for (int i = 0; i < n_fileds; i++) {total_length += calc_pack_length(field_type, length)}/*field_type:数据类型length:数据的字节数注意varchar(100) 的字节数,在utf8mb4环境下,字节数为400*/total_length += data_offset;

通过上述计算,需要满足 total_length 8126).Changing some columns to TEXT or BLOB may help.In current row format, BLOB prefix of 0 bytes is stored inline.

通过对报错信息的搜索,其判断方式如下:

/* dict0dict.cc:2515 *//* Check the size limit on leaf pages. */if (rec_max_size >= page_rec_max) {ib::error_or_warn(strict) 40 ? 40 : field.length;}rec_max_size += field_max_size + field_ext_max_size;}

通过上述计算,需要满足 rec_max_size < 8126,即可通过 InnoDB 层的检查。

四、总结

1. 必须在 MySQL Server 层和 InnoDB 层同时满足上述条件,才能建表成功;2. 如果出现上述报错情况,大部分是因为 varchar 等设置过大,建议可以将一些字段逐步缩小,或者用 text 进行代替;

•••展开全文