成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL 擴展字段長度報錯 Specified key was too long

數據庫 MySQL
MySQL 5.5 中引入 innodb_large_prefix 參數,5.5 與 5.6 中該參數默認關閉,5.7 中默認開啟。innodb_large_prefix 參數用于控制行格式 ?DYNAMIC or COMPRESSED 中的索引最大長度。

引言

本文主要分析一套 MySQL 分庫分表擴展字段長度時其中一個實例報錯索引超長的案例,其中失敗實例的版本是 5.7.21,而成功實例的版本都是 5.7.24。因此懷疑與版本有關,最終通過測試與分析判斷是一個 bug,官方文檔顯示在 5.7.23 中修復。

現象

首先介紹三個案例,都是字段長度擴展時報錯索引超長。

案例 1

時間:2023-09-08 21:31:02

數據庫版本:5.6.39

SQL

ALTER TABLE sign_bill_return_image_audit_result 
MODIFY COLUMN image_name VARCHAR(250) COMMENT '圖片名稱';

日志顯示 pt-osc 執行期間報錯索引長度超過 767。

EXECUTE START AT 2023-09-08 21:31:02
Error altering new table `station_manager`.`_sign_bill_return_image_audit_result_new`: DBD::mysql::db do failed: Specified key was too long; max key length is 767 bytes [for Statement "ALTER TABLE `station_manager`.`_sign_bill_return_image_audit_result_new` MODIFY COLUMN image_name VARCHAR(250) COMMENT '圖片名稱';"] at /usr/bin/pt-online-schema-change line 9194.

EXECUTE FAIL AT 2023-09-08 21:31:03

查看表結構,顯示字符集為 utf8mb4,索引類型為單列唯一索引,image_name 字段長度從 50 擴展到 250。

mysql> show create table station_manager.sign_bill_return_image_audit_result \G
*************************** 1. row ***************************
       Table: sign_bill_return_image_audit_result
Create Table: CREATE TABLE `sign_bill_return_image_audit_result` (
  `image_name` varchar(50) NOT NULL DEFAULT '' COMMENT '圖片名稱',
  UNIQUE KEY `idx_img_name` (`image_name`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=27756774 DEFAULT CHARSET=utf8mb4 COMMENT='簽單返還圖片審核結果表'
1 row in set (0.00 sec)

查看參數,顯示未開啟 innodb_large_prefix。

mysql> show variables like 'innodb_large_prefix';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| innodb_large_prefix | OFF   |
+---------------------+-------+
1 row in set (0.00 sec)

案例 2

時間:2024-08-19 14:18:31

現象:上游字段擴展,因此下游修改,但是執行報錯聯合索引超長

數據庫版本:5.7.33

SQL

alter table worker_board_quota_counting 
modify column  `business_id` varchar(1456) NOT NULL COMMENT '業務id';

日志顯示 pt-osc 執行期間報錯索引長度超過 3072。

Error altering new table `dms_offline`.`_worker_board_quota_counting_new`: DBD::mysql::db do failed: Specified key was too long; max key length is 3072 bytes [for Statement "ALTER TABLE `dms_offline`.`_worker_board_quota_counting_new` modify column `business_id` varchar(1456) NOT NULL COMMENT '業務id';"] at /usr/bin/pt-online-schema-change line 9194.

查看表結構,顯示字符集為 utf8mb4,索引類型為聯合唯一索引,business_id 字段長度從 456 擴展到 1456。

mysql> show create table dms_offline.worker_board_quota_counting \G
*************************** 1. row ***************************
       Table: worker_board_quota_counting
Create Table: CREATE TABLE `worker_board_quota_counting` (
  `business_id` varchar(456) NOT NULL COMMENT '業務id',
  UNIQUE KEY `idx_source_businessid` (`source`,`business_id`),
) ENGINE=InnoDB AUTO_INCREMENT=19747573 DEFAULT CHARSET=utf8mb4 COMMENT='人員看板計提表'
1 row in set (0.00 sec)

查看參數,顯示已開啟 innodb_large_prefix。

mysql> select @@innodb_large_prefix;
+-----------------------+
| @@innodb_large_prefix |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set (0.00 sec)

案例 3

時間:2024-01-22 23:59:12

工單類型:分庫分表

數據庫版本:5.7.21 報錯,5.7.24 不報錯

SQL

ALTER TABLE mst_sku
modify `upc_code` varchar(1000) DEFAULT NULL COMMENT '69碼';

日志顯示 pt-osc 執行期間報錯索引長度超過 767。

Error altering new table `wms3`.`__mst_sku_new`: DBD::mysql::db do failed: Index column size too large. The maximum column size is 767 bytes. [for Statement "ALTER TABLE `wms3`.`__mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL COMMENT '69碼';"] at /usr/bin/pt-online-schema-change line 9194.

查看表結構,顯示字符集為 utf8,索引類型為單列非唯一索引,upc_code 字段長度從 64 擴展到 1000,注意其中行格式為 COMPACT。

mysql> show create table `wms3`.`mst_sku` \G
*************************** 1. row ***************************
       Table: mst_sku
Create Table: CREATE TABLE `mst_sku` (
  `upc_code` varchar(64) DEFAULT NULL COMMENT '69碼',
  KEY `idx_sku_upccode` (`upc_code`),
) ENGINE=InnoDB AUTO_INCREMENT=12952734 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='商品信息表表'
1 row in set (0.00 sec)

查看參數,顯示已開啟 innodb_large_prefix。

mysql> select @@innodb_large_prefix;
+-----------------------+
| @@innodb_large_prefix |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set (0.00 sec)

由于分庫分表工單中只有一個實例報錯,因此查看每個實例的數據庫版本與執行結果。

ysql> select a.instance_version, t.execute_status from inception_job as t 
inner join assets_instance as a on t.mysql_ip=a.instance_ip 
where t.xbp_id =9334073;
+------------------+----------------+
| instance_version | execute_status |
+------------------+----------------+
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.24      |              4 |
| MySQL5.7.21      |              3 |
+------------------+----------------+
9 rows in set (0.00 sec)

其中:

  • execute_status = 4 表示成功,3 表示失敗;
  • 顯示有一個實例失敗,版本是 5.7.21,其他實例都是 5.7.24,都執行成功,這一點很反常。

因此盡管上面三個案例都是報錯索引超長,但是其中第三個案例中 5.7.21 報錯的現象比較反常,因此進行分析。

分析

索引最大長度

其中對于 InnoDB 存儲引擎,單列索引的最大長度是 767 字節,聯合索引的最大長度是 3072 字節。

不同版本的索引最大長度也不同。

其中:

  • 5.5 中引入 innodb_large_prefix 參數,5.5 與 5.6 中該參數默認關閉,5.7 中默認開啟。其中:

參數關閉時單列索引的最大長度為 767 字節;

參數開啟時單列索引的最大長度為 3072 字節。

  • 8.0 中移除 innodb_large_prefix 參數。

innodb_large_prefix

參考官方文檔,innodb_large_prefix 參數用于控制行格式 DYNAMIC or COMPRESSED 中的索引最大長度。

When this option is enabled, index key prefixes longer than 767 bytes (up to 3072 bytes) are allowed for InnoDB tables that use DYNAMIC or COMPRESSED row format.

同時滿足以下三個條件時允許創建 large index(索引最大長度為 3072 字節):

  • ROW_FORMAT = DYNAMIC or COMPRESSED
  • innodb_file_format = Barracuda
  • innodb_large_prefix = 1

其中有一個條件不滿足時索引最大長度為 767,且超長數據將被截斷。

innodb_large_prefix is enabled by default in MySQL 5.7. This change coincides with the default value change for。innodb_file_format, which is set to Barracuda by default in MySQL 5.7. Together, these default value changes allow larger index key prefixes to be created when using DYNAMIC or COMPRESSED row format. If either option is set to a non-default value, index key prefixes larger than 767 bytes are silently truncated.

innodb_large_prefix is deprecated; expect it to be removed in a future release. innodb_large_prefix was introduced to disable large index key prefixes for compatibility with earlier versions of InnoDB that do not support large index key prefixes.

因此對于案例 3,第一個條件不滿足,原因是行格式指定為 COMPACT,因此索引最大長度為 766 字節,那么超長時會報錯嗎?

測試

5.7.24

測試環境 5.7.24 執行報錯,與官方文檔描述一致,因此報錯是正常現象。

mysql> create table _mst_sku_new (
`upc_code` varchar(64) DEFAULT NULL COMMENT '69碼',
KEY `idx_sku_upccode` (`upc_code`)
) ENGINE=InnoDB CHARSET=utf8 ROW_FORMAT=COMPACT;
Query OK, 0 rows affected (0.02 sec)

mysql> alter table `_mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL;
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes
 
mysql> select version();
+------------+
| version()  |
+------------+
| 5.7.24-log |
+------------+
1 row in set (0.00 sec)

測試環境 5.7.33 執行成功,但是有警告,原因是 sql_mode 為空,表明 sql_mode 的優先級高于 ROW_FORMAT。

圖片圖片

線上環境 5.7.24 執行成功,原因是 sql_mode = NO_ENGINE_SUBSTITUTION,因此將報錯降級為警告。

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> alter table `_mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL;
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

mysql> set session sql_mode='NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)

mysql> alter table `_mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL;
Query OK, 0 rows affected, 1 warning (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 1

線上環境 5.7.21 執行失敗,可是同樣 sql_mode = NO_ENGINE_SUBSTITUTION,原因是什么呢?

5.7.21

線上環境 5.7.21 與 5.7.24 配置相同但是報錯。

mysql> create table _mst_sku_new (
`upc_code` varchar(64) DEFAULT NULL COMMENT '69碼',
KEY `idx_sku_upccode` (`upc_code`)
) ENGINE=InnoDB CHARSET=utf8 ROW_FORMAT=COMPACT;
Query OK, 0 rows affected (0.01 sec)

mysql> select @@sql_mode;
+------------------------+
| @@sql_mode             |
+------------------------+
| NO_ENGINE_SUBSTITUTION |
+------------------------+
1 row in set (0.00 sec)

mysql> alter table `_mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL;
ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.

mysql> alter table _mst_sku_new ROW_FORMAT=dynamic;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table `_mst_sku_new` MODIFY COLUMN `upc_code` varchar(1000) DEFAULT NULL;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

其中:

  • 報錯不同,Index column size too large. The maximum column size is 767 bytes;
  • ROW_FORMAT = COMPACT 報錯,理論上不報錯,不合理,原因是 sql_mode = NO_ENGINE_SUBSTITUTION;
  • ROW_FORMAT = DYNAMIC 不報錯,理論上不報錯,合理;

因此懷疑 5.7.21 中存在 bug,比如判斷是否支持 create larger index 時沒有判斷 sql_mode。

5.7.23

查看 release notes,顯示 5.7.23 中修復了一個 bug,bug 的現象是對于 COMPACT 或 REDUNDANT:

  • 嚴格模式下不報錯
  • 非嚴格模式下不告警

For attempts to increase the length of a VARCHAR column of an InnoDB table using ALTER TABLE with the INPLACE algorithm, the attempt failed if the column was indexed.

If an index size exceeded the InnoDB limit of 767 bytes for COMPACT or REDUNDANT row format, CREATE TABLE and ALTER TABLE did not report an error (in strict SQL mode) or a warning (in nonstrict mode). (Bug #26848813)

對應 commit 為 MySQL Commit 913071c,下面表格中展示修復后的行格式與索引長度,以及嚴格模式與非嚴格模式下返回報錯還是告警,其中 IL 表示 Index Limit。

Row Format

INDEX LIMIT

STRICT MODE (>IL)

NON-STRICT MODE (>IL)

Compact/Redundant (Non Unique Index)

767 bytes

Error

Index truncation (767) and warning

Compact/Redundant (Unique/Primary Index)

767 bytes

Error

Error

Dynamic/Compressed (Non Unique Index)

3072 bytes

Error

Index truncation (3072) and warning

Dynamic/Compressed (Unique/Primary Index)

3072 bytes

Error

Error

其中當索引超長時,返回報錯還是告警由索引類型與 sql_mode 共同決定:

  • 唯一索引,對于嚴格模式與非嚴格模式,均返回報錯;
  • 非唯一索引,對于嚴格模式,返回報錯,對于非嚴格模式,返回警告,并將索引值截斷為前綴索引。

因此,判斷該現象對應該 bug,表現為 5.7.21 非嚴格模式中,非唯一索引超長后返回報錯,而不是警告。

debug

debug 數據庫版本為 5.7.33,測試索引超長返回警告的堆棧見下圖。

其中有以下兩個函數:

  • mysql_prepare_create_table
  • push_warning_printf

commit 中顯示修改 ha_innobase::max_supported_key_part_length 函數。

因此給以上三個函數設置斷點。

測試顯示行記錄為 COMPACT 時,返回索引最大長度為 767。

圖片圖片

在判斷索引長度超長(1000 * 3 = 3000 > 767)后,判斷返回報錯還是警告。

圖片圖片

其中:

  • 如果是唯一索引,返回報錯;
  • 如果是非唯一索引,繼續判斷 sq_mode,如果是嚴格模式,返回報錯,否則返回警告,并且將索引長度自動截斷實現字節對齊。變量 key_part_length 從 767 改為 765 字節,對應 utf8 字符集 255 字符。

相關代碼如下所示。

// 如果不是唯一索引,也就是二級非唯一索引,根據 sql_mode 判斷是否返回報錯
 if (key->type == KEYTYPE_MULTIPLE)
 {
   /* not a critical problem */
    // 警告
   push_warning_printf(thd, Sql_condition::SL_WARNING,
                              ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY),
                              key_part_length);
          /* Align key length to multibyte char boundary */
          // 索引長度自動截斷,比如 767 // 3 = 255
          // 將 key_part_length 減少到最接近的整數倍數,使得它不超過當前字符集中最多的多字節字符長度
          key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
          /*
            If SQL_MODE is STRICT, then report error, else report warning
            and continue execution.
          */
          // 對于嚴格模式,將警告升級為錯誤
          if (thd->is_error())
            DBUG_RETURN(true);
 }

其中枚舉類型變量 keytype 的定義如下所示,沒有區分單列索引與聯合索引,因此判斷 KEYTYPE_MULTIPLE 表示非唯一索引。

enum keytype {
  KEYTYPE_PRIMARY,
  KEYTYPE_UNIQUE,
  KEYTYPE_MULTIPLE,
  KEYTYPE_FULLTEXT,
  KEYTYPE_SPATIAL,
  KEYTYPE_FOREIGN
};

而在 5.7.21 中,返回的索引最大長度等于 3072,大于當前字段的長度 3000,因此判斷結果是索引不超長。

圖片圖片

而在創建索引的時候還會二次檢查判斷索引長度是否超長。

/* Even though we've defined max_supported_key_part_length, we
 still do our own checking using field_lengths to be absolutely
 sure we don't create too long indexes. */

 error = convert_error_code_to_mysql(
  row_create_index_for_mysql(index, trx, field_lengths, handler),
  flags, NULL);

其中:

  • create_index 函數中調用 row_create_index_for_mysql 函數創建索引;
  • row_create_index_for_mysql 函數中檢查索引的長度與行格式對應的索引最大長度,其中通過宏 DICT_MAX_FIELD_LEN_BY_FORMAT 獲取索引長度;
/* Column or prefix length exceeds maximum column length */
  if (len > (ulint) DICT_MAX_FIELD_LEN_BY_FORMAT(table)) {
   err = DB_TOO_BIG_INDEX_COL;

   dict_mem_index_free(index);
   goto error_handling;
  }
 }
  • DICT_MAX_FIELD_LEN_BY_FORMAT 宏中根據行格式返回索引最大長度,COMPACT 對應 767;
/** Find out maximum indexed column length by its table format.
For ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT, the maximum
field length is REC_ANTELOPE_MAX_INDEX_COL_LEN - 1 (767). For
Barracuda row formats COMPRESSED and DYNAMIC, the length could
be REC_VERSION_56_MAX_INDEX_COL_LEN (3072) bytes */
#define DICT_MAX_FIELD_LEN_BY_FORMAT(table)    \
  ((dict_table_get_format(table) < UNIV_FORMAT_B)  \
   ? (REC_ANTELOPE_MAX_INDEX_COL_LEN - 1)  \
   : REC_VERSION_56_MAX_INDEX_COL_LEN)
  • 由于 3000 > 767,因此判斷索引超長,最終返回報錯;
  • 但是為什么非嚴格模式下沒有將報錯降級為警告的原因暫時沒查到。

處理

時間:2024-11-24 02:00:27

10個月以后,這套該分庫分表給其他字段擴展長度時再次觸發該問題,因此決定進行處理,具體是將數據庫從 5.7.21 升級到 5.7.24。

而在升級后發現兩個現象:

  • 索引中字符長度自動調整為 255,正常現象;
  • 不小心又踩坑了,先升級的主庫,執行 DDL 后導致從庫復制中斷,異常現象。

如下所示,對比執行失敗與執行成功時的索引長度。

# 失敗后
KEY `idx_dispatch_no` (`dispatch_no`)

# 成功后
KEY `idx_dispatch_no` (`dispatch_no`(255)),

官方文檔顯示,從 5.7.17 版本開始:

  • 對于非唯一索引,如果是非嚴格模式,索引超長后返回警告,并自動截斷到支持的索引最大長度;
  • 對于唯一索引,索引超長后直接報錯,不會發生截斷,原因是截斷后可能導致唯一性約束失效。

As of MySQL 5.7.17, if a specified index prefix exceeds the maximum column data type size, CREATE INDEX handles the index as follows:

For a nonunique index, either an error occurs (if strict SQL mode is enabled), or the index length is reduced to lie within the maximum column data type size and a warning is produced (if strict SQL mode is not enabled).

For a unique index, an error occurs regardless of SQL mode because reducing the index length might enable insertion of nonunique entries that do not meet the specified uniqueness requirement.

如下所示,進行測試。

其中:

  • 嚴格模式,非唯一索引,索引超長后報錯;
  • 非嚴格模式,非唯一索引,索引超長后警告,并自動截斷;
  • 非嚴格模式,唯一索引,索引超長后報錯。

主庫升級后使用 pt-osc 執行 DDL 導致從庫復制中斷,原因是從庫未升級。

重試時發生異常,日志顯示執行暫停。

2024-11-25T11:35:07 Copying approximately 764 rows...
Replica MSS-2hbqmzhk2m is stopped. Waiting. 
Killed

查看復制,顯示復制中斷,原因是從庫執行 DDL 報錯,pt-osc 延遲檢測期間發現復制中斷后執行暫停。

mysql> SHOW SLAVE STATUS \G
*************************** 1. row ***************************
               Last_SQL_Errno: 1709
               Last_SQL_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction '965c7418-175f-11ee-b6d3-fa163eae0649:12102' at master log mysql-bin.146487, end_log_pos 8137214. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
     Last_SQL_Error_Timestamp: 241125 11:35:07
            Executed_Gtid_Set: 965c7418-175f-11ee-b6d3-fa163eae0649:1-12101
                Auto_Position: 1
1 row in set (0.00 sec)

mysql> select * from performance_schema.replication_applier_status_by_worker  limit 1 \G
*************************** 1. row ***************************
         CHANNEL_NAME: 
            WORKER_ID: 1
            THREAD_ID: NULL
        SERVICE_STATE: OFF
LAST_SEEN_TRANSACTION: 965c7418-175f-11ee-b6d3-fa163eae0649:12102
    LAST_ERROR_NUMBER: 1709
   LAST_ERROR_MESSAGE: Worker 1 failed executing transaction '965c7418-175f-11ee-b6d3-fa163eae0649:12102' at master log mysql-bin.146487, end_log_pos 8137214; Error 'Index column size too large. The maximum column size is 767 bytes.' on query. Default database: 'wms3'. Query: 'ALTER TABLE `wms3`.`_task_group_new` MODIFY COLUMN dispatch_no varchar(500) NULL COMMENT '派車單號''
 LAST_ERROR_TIMESTAMP: 2024-11-25 11:35:07
1 row in set (0.01 sec)

知識點

ROW_FORMAT

innodb_default_row_format 參數用于控制默認行格式,取值與版本有關:

  • 5.0.3 版本之前,僅支持一種行格式 REDUNDANT;
  • 5.0.3 - 5.7.8,默認行格式為 COMPACT;
  • 從 5.7.9 版本開始,默認行格式為 DYNAMIC,包括 8.0。

行格式 COMPACT 與 DYNAMIC 的主要區別是行溢出(一個列中存儲的數據大于等于8098個字節)數據的保存方式不同,其中:

  • COMPACT,在記錄的真實數據處存儲字段真實數據的前 768 個字節,剩余數據保存在其他頁中,并在真實數據中保存溢出頁地址;
  • DYNAMIC,把所有的字節都存儲到其他頁面中,只在記錄的真實數據處存儲其他頁面的地址。

圖片圖片

因此在數據庫升級過程中也需要關注行格式。

故障分析 | ERROR 1709: Index column size too large 引發的思考 文章中分享了一個案例,現象是數據庫重啟后有張表無法訪問,SELECT、DML 和 DDL 執行均報錯 ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.。

復現流程如下所示:

  • 數據庫從 5.6.21 原地升級到 8.0.21,升級之前創建的一個表未指定行格式,因此使用默認行格式 COMPACT;
  • 升級后添加字段并創建索引,索引超長但是沒有報錯,也沒有警告;
  • 數據庫重啟前,表可以正常訪問;
  • 數據庫重啟后,表無法訪問,報錯索引超長。

最終定位到也是一個 bug,具體表現為非顯式定義的 redundant 行格式表允許創建的索引列大小超 767 bytes,并在 8.0.22 版本中修復。

因此建議在數據庫升級前檢查隱式創建行格式為 compact/redundant 的表,并顯式指定。

相關案例

下面引申一個話題,SQL 工單中遇到過 goinception 語法校驗通過,但是執行時報錯行超長的現象,因此分別測試 goinception 是否可以識別字段超長與行超長。

已知:

  • 對于VARCHAR(M)類型的列最多可以占用65535個字節。其中的M代表該類型最多存儲的字符數量;
  • MySQL對一條記錄占用的最大存儲空間是有限制的,除了BLOB或者TEXT類型的列之外,其他所有的列(不包括隱藏列和記錄頭信息)占用的字節長度加起來不能超過65535個字節。

字段超長

SQL

create table,155355 * 3 > 65535,因此字段長度超長。

create table ttt(
  id int primary key auto_increment comment 'id', 
  a varchar(155355) default '' comment 'a'
) comment 'ttt';

goinception 返回報錯字段超長,建議使用大字段替換 varchar。

Column length too big for column 'a' (max = 21845); use BLOB or TEXT instead. 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.

add column;

alter table t1 add column aa varchar(155355) default '' comment 'a';

goinception;

Column length too big for column 'aa' (max = 21845); use BLOB or TEXT instead.

modify column;

alter table t1 modify column a varchar(155355) default '' comment 'a';

goinception;

Column length too big for column 'a' (max = 21845); use BLOB or TEXT instead.

因此,測試顯示 goinception 可以驗證字段超長,包括建表與改表時,那么是否可以驗證行超長?

行超長

create table,15535 * 3 * 2 = 93210 > 65535,因此雖然單個字段不超長,但是行超長。

create table ttt(
  id int primary key auto_increment comment 'id', 
  a varchar(15535) default '' comment 'a',
  b varchar(15535) default '' comment 'b'
) comment 'ttt';

goinception 返回校驗通過,當然實際執行會失敗。

{
    "id": 1, 
    "stage": "CHECKED", 
    "errlevel": 0, 
    "stagestatus": "Audit Completed", 
    "errormessage": "", 
    "sql": "USE `cctest`", 
    "affected_rows": 0, 
    "sequence": "0_0_00000000", 
    "backup_dbname": "", 
    "execute_time": "0", 
    "sqlsha1": "", 
    "backup_time": "0", 
    "actual_affected_rows": ""
}

因此結論是 goinception 可以發現單字段超長,但是無法發現多字段導致的行超長。

因此,SQL 工單中自定義行超長校驗,調用接口返回報錯。

{
    "code": 16, 
    "message": "SQLCheckMaxRowSizeError", 
    "error": "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. 庫:cctest,表:ttt,行大小為:93214,超過最大行大小65535字節,請修改字段長度或類型"
}

原因是代碼中自行實現行超長檢測,并自定義異常類。

class SQLCheckMaxRowSizeError(BaseError):  
    def __init__(self, db_name="", table="", row_size=""):  
        BaseError.__init__(  
            self, code=SQL_Check_MAX_ROW_SIZE_Error, message="SQLCheckMaxRowSizeError",  
            error="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."                  " 庫:%s,表:%s,行大小為:%s,超過最大行大小65535字節,請修改字段長度或類型" % (db_name, table, row_size)  
        )

結論

MySQL 5.5 中引入 innodb_large_prefix 參數,5.5 與 5.6 中該參數默認關閉,5.7 中默認開啟。

innodb_large_prefix 參數用于控制行格式  DYNAMIC or COMPRESSED 中的索引最大長度。

5.7 中同時滿足以下三個條件時索引最大長度為 3072 字節:

  • ROW_FORMAT = DYNAMIC or COMPRESSED
  • innodb_file_format = Barracuda
  • innodb_large_prefix = 1

其中有一個條件不滿足時索引最大長度等于 767 字節。因此對于行格式 COMPACT,索引最大長度為 767 字節。

當索引超長時,返回報錯還是告警由索引類型與 sql_mode 共同決定:

  • 唯一索引,對于嚴格模式與非嚴格模式,均返回報錯,注意不允許截斷,否則可能導致索引失效;
  • 非唯一索引,對于嚴格模式,返回報錯,對于非嚴格模式,返回警告,并將索引值截斷為前綴索引。

而本文中 5.7.21 版本中的現象與上述描述不符,非嚴格模式中,非唯一索引超長后返回報錯,而不是警告。因此判斷該現象是 bug。

分析代碼后發現,有兩次索引長度檢查,但是索引最大長度的判斷條件不一致:

  • 第一次,索引最大長度由 innodb_large_prefix 決定,參數開啟時返回 3072;
  • 第二次,索引最大長度由行格式決定,COMPACT 對應 767。

因此在 5.7.21 中當 COMPACT 開啟 innodb_large_prefix 時,將導致第一次檢查通過,第二次檢查報錯,但是具體為什么沒有將報錯降級為警告的原因暫未查到。

而在 8.0 中移除了 innodb_large_prefix 參數,索引最大長度統一由行格式決定,這樣也就避免了該問題。

回過頭來分析最初的三個案例,其中:

  • 案例 1,5.6.39,隱式 COMPACT,單列唯一索引報錯超長 767,報錯正常;
  • 案例 2,5.7.33,隱式 COMPACT,聯合唯一索引報錯超長 3072,報錯正常;
  • 案例 3,5.7.21,顯式 COMPACT,單列非唯一索引報錯超長 767,報錯不正常,正常應該是警告。

注意都是非嚴格模式。

責任編輯:武曉燕 來源: 丹柿小院
相關推薦

2009-12-11 14:16:13

PHP獲取字段長度

2010-11-22 11:55:23

MySQL字段

2010-11-01 14:30:47

db2擴充表空間

2010-10-08 14:59:00

MySql字段

2024-01-07 20:05:33

2014-04-15 11:22:24

2024-04-15 10:30:22

MySQL存儲引擎

2023-12-25 14:47:14

2023-11-13 10:55:09

MySQL數據庫

2023-04-10 08:28:35

CharVarchar

2024-05-31 09:31:00

2010-04-23 16:18:36

Oracle存取

2023-02-07 09:01:30

字符串類型MySQL

2010-09-25 10:48:59

SQL字段類型長度

2016-09-20 23:44:43

2024-03-14 08:11:45

模型RoPELlama

2022-12-05 14:05:26

MySQL最大取值存儲

2010-08-16 13:25:41

DB2數據庫操作

2019-06-18 15:20:01

MySQL連接錯誤數據庫

2024-07-15 08:32:34

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品一二三区 | 国产精品久久久久久亚洲调教 | 荷兰欧美一级毛片 | 欧美专区在线 | 毛片视频网址 | 国产粉嫩尤物极品99综合精品 | 欧美日韩高清一区二区三区 | 中文字幕电影在线观看 | www.成人免费视频 | 在线观看不卡av | 99久久亚洲 | 亚欧午夜| 视频一区中文字幕 | 国产亚洲一区二区三区在线观看 | 国产精品国产精品国产专区不卡 | 人人做人人澡人人爽欧美 | 亚洲性人人天天夜夜摸 | 免费v片在线观看 | 黄色一级毛片免费看 | 久久国产精品视频免费看 | 久久国产精品-国产精品 | 精品国产乱码一区二区三 | 全免费a级毛片免费看视频免 | 精品成人佐山爱一区二区 | 国产精品久久久久影院色老大 | 亚洲一区中文字幕 | 久久久.com | 欧美精品久久久久久久久老牛影院 | 亚洲国产精品久久久久婷婷老年 | 欧美男人的天堂 | 不卡在线视频 | 亚州影院 | 亚洲国产成人精品女人 | 九九导航 | 亚洲精品www| a级在线免费 | 在线观看亚洲专区 | 亚洲一二三视频 | 午夜资源 | 日本在线你懂的 | 日本不卡免费新一二三区 |