> 文档中心 > MySQL源码分析-常见报错以及insert缓慢原因分析

MySQL源码分析-常见报错以及insert缓慢原因分析


 👩‍💻博客主页:大家好我是poizxc2014的博客主页

✨欢迎关注🖱点赞🎀收藏⭐留言✒
📖个人主页:poizxc2014的博客_CSDN博客-数据库,mysql,java领域博主

💻首发时间:🎞2022年05月01日🎠

🎨你做三四月的事,八九月就会有答案,一起加油吧
🔥💖🔮😘🔏🀄🎧如果觉得博主的文章还不错的话,👍请三连支持一下博主哦🤞

如果觉得博主的文章还不错的话,请三连支持一下博主哦

最后的话,在很多方面还做的不好的地方,欢迎大佬指正,一起学习哦,冲冲冲

目录

1、insert几个可能的性能瓶颈点

示例分析

 2、使用perror查看报错

 3、parse 解析

建表

执行mysql_execute_command()

mysql_insert()

write_record

ha_write_row、write_row

入引擎层

非主键的处理

插入单个索引

row_ins_clust_index_entry 和 row_ins_sec_index_entry

4、直接简单插入记录

5、插入记录on duplicate key


本文的分析是以5.6 innodb 引擎为主

1、insert几个可能的性能瓶颈点

基于insert源码分析,和MySQL事务的一般过程,可以看出insert语句执行过程中几个可能的瓶颈点,包括加锁,io和网络几个方面:

  • MDL锁, insert语句需要拿IX MDL 锁

  • 外键检查对主表加S行锁

  • insert转update操作需要的对老记录index entry的行锁

  • iops限制

  • 写binlog

  • semi-sync消息延迟

示例分析

MDL锁等待

  • 一般都是因为表上有运行时间比较长的DDL语句在运行,比如Optimize, Truncate table,Alter table等

  • 通过运行show processlist,就会看到被阻塞的语句的状态是 Waiting for table metadata lock ,如果权限足够的话,还可以看到blocker thread

  • 规避的方法是==避免在业务高峰运行DDL语句==,特别是耗时很长的对大表的DDL

外键检查等待对主表记录的S锁

  • 如果是insert的目标表有定义外键依赖,MySQL需要做参照完整性(RI)检查,会对主表的对应记录加S锁

  • 如果主表记录上正好有没有提交的修改,就会带来insert事务的锁等待

  • 下面的语句可以==当场查看正在阻塞的关系==

SELECTr.trx_id waiting_trx_id,r.trx_mysql_thread_id waiting_thread,r.trx_query waiting_query,b.trx_id blocking_trx_id,b.trx_mysql_thread_id blocking_thread,b.trx_query blocking_query,(Unix_timestamp() - Unix_timestamp(r.trx_started)) blocked_timefrom information_schema.innodb_lock_waits winner join information_schema.innodb_trx bon b.trx_id = w.blocking_trx_idinner join information_schema.innodb_trx ron r.trx_id = w.requesting_trx_id
  • 规避方法

  1. 加速释放主表的X行锁,避免长事务;

  2. 或者是通过业务而不是MySQL数据库来保证参照完整性

insert转update操作需要拿老记录index entry上的S/X锁

  • 如果新插入的记录项已经存在,但已经被标记为已删除,或者是使用了INSERT ON DUPLICATE KEY UPDATE, MySQL会将insert操作转成update操作

  • 就会对老的index 项目加上X锁(如果是cluster index对应的记录则加S锁),来确保原有记录的删除/修改事务已经提交

  • 定位方法:

  1. 查看慢日志的lock time字段来判断

  2. 如果发生了死锁,还可以查看master error log

  3. 也可类似MDL锁等待,从I_S表查看当前的阻塞关系

iops限制

后台大IO应用导致insert变慢

写binlog延迟

磁盘无空间,导致binlog写入hang住

semi-sync消息延迟

rpl_semi_sync_master_timeout参数值,semi-sync可能导致insert延迟对应的时间

 2、使用perror查看报错

perror 30 
OS error code  30:  Read-only file system
MySQL error code MY-000030: File '%s' (fileno: %d) was not closed

错误:1000 SQLSTATE:HY000(ER_HASHCHK)消息:hashchk 错误:1001 SQLSTATE:HY000(ER_NISAMCHK)消息:isamchk 错误:1002 SQLSTATE:HY000(ER_NO)消息:没有错误:1003 SQLSTATE:HY000(ER_YES)消息:错误: 1004 SQLSTATE:HY000(ER_CANT_CREATE_FILE)消息:无法创建文件'%s'(errno的:%d)的错误:1005 SQLSTATE:HY000(ER_CANT_CREATE_TABLE)消息:无法创建表'%s'(errno的:%D)错误:1006 SQLSTATE:HY000(ER_CANT_CREATE_DB)消息:无法创建数据库'%s'(errno的:%d)的错误:1007 SQLSTATE:HY000(ER_DB_CREATE_EXISTS)消息:无法创建数据库'%s';数据库中存在错误:1008 SQLSTATE:HY000(ER_DB_DROP_EXISTS)消息:无法下降datebase'%s'的数据库不存在错误:1009 SQLSTATE:HY000(ER_DB_DROP_DELETE)消息:错误删除数据库(无法删除'%s的“,并将errno数:%d)错误:1010 SQLSTATE:HY000(ER_DB_DROP_RMDIR)消息:错误删除数据库(RMDIR'%s'的,并将errno:%d)的错误:1011 SQLSTATE:HY000(ER_CANT_DELETE_FILE)消息:错误上删除'% '(errno的:%D)错误:1012 SQLSTATE:HY000(ER_CANT_FIND_SYSTEM_REC)消息:无法读取系统表中的记录错误:1013 SQLSTATE:HY000(ER_CANT_GET_STAT)消息:无法获取的状态'%S'(errno的数:%d)错误:1014 SQLSTATE:HY000(ER_CANT_GET_WD)消息:无法获得工作目录(errno的:%d)的错误:1015 SQLSTATE:HY000(ER_CANT_LOCK)消息:无法锁定文件(errno的数:%d)错误:1016 SQLSTATE:HY000(ER_CANT_OPEN_FILE)消息:无法打开文件:'%S'(errno的:%d)的错误:1017 SQLSTATE:HY000(ER_FILE_NOT_FOUND)消息:无法找到文件:'%S'(errno的: %d)的错误:1018 SQLSTATE:HY000(ER_CANT_READ_DIR)消息:无法读取'%s'的(DIR errno的:%d)的错误:1019 SQLSTATE:HY000(ER_CANT_SET_WD)消息:无法更改目录到'%s “(errno的:%d)的错误:1020 SQLSTATE:HY000(ER_CHECKREAD)消息:记录已更改自上次读表'%s' 错误:1021 SQLSTATE:HY000(ER_DISK_FULL)消息:磁盘已满(%S);等待有人以释放一些空间... 错误:1022 SQLSTATE:23000(ER_DUP_KEY)消息:可以不写;在表'%s'的重复键错误:1023 SQLSTATE:HY000(ER_ERROR_ON_CLOSE)消息:'%s'的密切错误(并将errno:%d)的错误:1024 SQLSTATE:HY000(ER_ERROR_ON_READ)消息:读取文件错误'%s'的(errno的:%D)错误:1025 SQLSTATE:HY000(ER_ERROR_ON_RENAME)消息:重命名错误'%S'“ %S'(errno的:%d)的错误:1026 SQLSTATE:HY000(ER_ERROR_ON_WRITE)消息:错误写入文件'%s'(errno的数:%d)错误:1027 SQLSTATE:HY000(ER_FILE_USED)消息:'%S'已被锁定反对改变错误:1028 SQLSTATE:HY000(ER_FILSORT_ABORT)消息:排序中止错误:1029 SQLSTATE:HY000(ER_FORM_NOT_FOUND)消息:视图'%s'的不存在'%s'的错误:1030 SQLSTATE:HY000(ER_GET_ERRNO)消息:得到存储引擎错误%d '%s'的错误:1031 SQLSTATE:HY000(ER_ILLEGAL_HA)消息:表的存储引擎没有此选项错误:1032 SQLSTATE:HY000(ER_KEY_NOT_FOUND)消息:无法找到记录'%s'的错误:2052 SQLSTATE:HY000(ER_NOT_FORM_FILE)消息:不正确的信息文件:'%s'的错误:1034 SQLSTATE:HY000(ER_NOT_KEYFILE)消息:表'%s'的不正确的密钥文件尝试修复错误:1035 SQLSTATE:HY000(ER_OLD_KEYFILE)消息:表'%s'的旧密钥文件;修复它!错误:1036 SQLSTATE:HY000(ER_OPEN_AS_READONLY)消息:表'%s' 读取错误:1037 SQLSTATE:HY001( ER_OUTOFMEMORY )消息:内存不足,重新启动服务器并再次尝试(需要%d字节)错误:1038 SQLSTATE:HY001(ER_OUT_OF_SORTMEMORY)消息:排序内存不足,增加服务器的排序缓冲区大小错误:1039 SQLSTATE:HY000(ER_UNEXPECTED_EOF)消息:阅读文件'%s'(errno的数:%d)错误:1040 SQLSTATE:08004(ER_CON_COUNT_ERROR)消息:连接过多的错误:1041 SQLSTATE:HY000(ER_OUT_OF_RESOURCES)消息:内存不足时,发现意外的EOF;检查,如果 mysqld或一些其他进程使用所有可用的内存,如果没有,你可能必须使用“的ulimit'允许mysqld来使用更多的内存,也可以添加更多的交换空间错误:1042 SQLSTATE:08S01(ER_BAD_HOST_ERROR)消息:无法获取您的主机名地址错误:1043 SQLSTATE:08S01(ER_HANDSHAKE_ERROR)消息:坏握手错误:1044 SQLSTATE:42000(ER_DBACCESS_DENIED_ERROR)消息:为用户访问被拒绝['%S'@'%S'](mailto:‘%s’@'%s’)数据库'%s' 的错误:1045 SQLSTATE: 28000 (ER_ACCESS_DENIED_ERROR)消息:访问拒绝用户['%s的'@'%s'的](mailto:‘%s’@'%s’):(%s的密码)错误:1046 SQLSTATE:3D000(ER_NO_DB_ERROR)消息:没有数据库被选择的错误:1047 SQLSTATE:08S01(ER_UNKNOWN_COM_ERROR)消息 : 未知的命令错误:1048 SQLSTATE:23000(ER_BAD_NULL_ERROR)讯息列'%s'能不能是空的错误:1049 SQLSTATE:42000(ER_BAD_DB_ERROR)消息:未知的数据库'%s'的错误:1050 SQLSTATE:42S01(ER_TABLE_EXISTS_ERROR)消息:表'%s'中已经存在的错误:1051 SQLSTATE:42S02(ER_BAD_TABLE_ERROR)消息:未知的表'%s' 的错误:1052 SQLSTATE:23000(ER_NON_UNIQ_ERROR)消息:列'%s'在%s是模糊的错误:1053 SQLSTATE: 08S01 (ER_SERVER_SHUTDOWN)消息:服务器关闭进展错误:1054 SQLSTATE:42S22(ER_BAD_FIELD_ERROR)消息:未知列'%s'的'%s'的错误:1055 SQLSTATE:42000(ER_WRONG_FIELD_WITH_GROUP)消息:'%s'的不GROUP BY 错误:1056 SQLSTATE:42000(ER_WRONG_GROUP_FIELD)消息:无法在'%s'的组错误:1057 SQLSTATE:42000(ER_WRONG_SUM_SELECT)消息:声明SUM函数和在同一语句中的列错误:1058 SQLSTATE:21S01(ER_WRONG_VALUE_COUNT )消息:列计数不匹配值计数错误:1059 SQLSTATE:42000(ER_TOO_LONG_IDENT)消息:标识符名称'%s'是太长的错误:1060 SQLSTATE:42S21(ER_DUP_FIELDNAME)消息:重复的列名'%s '的错误:1061 SQLSTATE:42000(ER_DUP_KEYNAME)消息:重复键的名称'%s'的错误:1062 SQLSTATE:23000(ER_DUP_ENTRY)消息:重复条目'%s'的关键%ð 错误:1063 SQLSTATE:42000(ER_WRONG_FIELD_SPEC)消息:不正确列符列'%s' 错误:1064 SQLSTATE:42000(ER_PARSE_ERROR)消息:%s的近'%s'的行%ð 错误:1065 SQLSTATE:42000(ER_EMPTY_QUERY)消息:查询是空的错误:1066 SQLSTATE:42000 (ER_NONUNIQ_TABLE)消息:不是唯一的表/别名:'%s'的错误:1067 SQLSTATE:42000(ER_INVALID_DEFAULT)消息:无效的默认值'%s' 的错误:1068 SQLSTATE:42000(ER_MULTIPLE_PRI_KEY)消息:多个主键定义错误:1069 SQLSTATE:42000(ER_TOO_MANY_KEYS)消息:指定的键太多;%最大D键允许错误:1070 SQLSTATE:42000(ER_TOO_MANY_KEY_PARTS)消息:太多指定的关键部件,最大为%d 部分允差:1071 SQLSTATE:42000( ER_TOO_LONG_KEY )消息:指定的键过长;最大的密钥长度是%d个字节的错误:1072 SQLSTATE:42000(ER_KEY_COLUMN_DOES_NOT_EXITS)消息:键列'%s'中不存在表错误:1073 SQLSTATE:42000(ER_BLOB_USED_AS_KEY)消息 : BLOB列'%s的'不能用于与使用的表类型关键指标1074 SQLSTATE:42000(ER_TOO_BIG_FIELDLENGTH)消息:错误列长度为列'%s'大(MAX =%d)条;使用BLOB代替错误:1075 SQLSTATE:42000(ER_WRONG_AUTO_KEY)消息:不正确的表定义可以有一个自动列,必须作为一个关键定义的错误:1076 SQLSTATE:HY000(ER_READY)消息:%S:连接准备。版本:'%s'的插座:'%s'的端口:%ð 错误:1077 SQLSTATE:HY000(ER_NORMAL_SHUTDOWN)消息:%S:正常关机错误:1078 SQLSTATE:HY000(ER_GOT_SIGNAL)消息:%S:得到信号为%d 。中止错误:1079 SQLSTATE:HY000(ER_SHUTDOWN_COMPLETE)消息:%S:关机完整的错误:1080 SQLSTATE:08S01(ER_FORCING_CLOSE)消息:%S:强制关闭线程%LD用户:'%s'的错误:1081 SQLSTATE:08S01 (ER_IPSOCK_ERROR)消息:无法创建IP套接字错误:1082 SQLSTATE:42S12(ER_NO_SUCH_INDEX)消息:表'%s'有没有像在CREATE INDEX指数;重新创建表错误:1083 SQLSTATE:42000(ER_WRONG_FIELD_TERMINATORS ) 消息:字段分隔符说法是不期望是什么;,检查本手册的错误:1084 SQLSTATE:42000(ER_BLOBS_AND_NO_TERMINATED)消息:您不能使用BLOB的固定rowlength;请使用“终止领域的“错误:1085 SQLSTATE:HY000 (ER_TEXTFILE_NOT_READABLE )消息:文件'%s'必须在数据库目录或可被所有人读取错误:1086 SQLSTATE:HY000(ER_FILE_EXISTS_ERROR)消息:文件'%s'已经存在的错误:1087 SQLSTATE:HY000(ER_LOAD_INFO)消息:记录:跳过%LD删除:%LD:%ld的警告:%ld的错误:1088 SQLSTATE:HY000(ER_ALTER_INFO)消息:记录:%ld的重复:%ld的错误:1089 SQLSTATE:HY000(ER_WRONG_SUB_KEY)消息:不正确的分部分项; 所使用的关键部分不是字符串,使用长度超过的重要组成部分,或存储引擎不支持独特的子键错误:1090 SQLSTATE:42000(ER_CANT_REMOVE_ALL_FIELDS)消息:您不能删除所有列使用 ALTER表;使用DROP TABLE,而不是错误:1091 SQLSTATE:42000(ER_CANT_DROP_FIELD_OR_KEY)消息:无法删除'%s'的检查列/键存在错误:1092 SQLSTATE:HY000(ER_INSERT_INFO)消息:记录:%ld的重复:% LD警告:%ld的错误:1093 SQLSTATE:HY000(ER_UPDATE_TABLE_USED)消息:您不能在FROM子句中指定更新的目标表'%s' 的错误:1094 SQLSTATE:HY000(ER_NO_SUCH_THREAD)消息:未知的线程ID:%lu个错误:1095 SQLSTATE:HY000(ER_KILL_DENIED_ERROR)消息:你是不是线程%鲁所有者错误:1096 SQLSTATE:HY000(ER_NO_TABLES_USED)消息:没有表错误:1097 SQLSTATE:HY000(ER_TOO_BIG_SET)消息:%s的列太多的字符串集的错误:1098 SQLSTATE:HY000(ER_NO_UNIQUE_LOGFILE)消息:无法生成一个独特的日志文件名 %(1-999)的错误:1099 SQLSTATE:HY000(ER_TABLE_NOT_LOCKED_FOR_WRITE)消息:表'%s'与锁定读锁不能更新的错误:1100 SQLSTATE:HY000(ER_TABLE_NOT_LOCKED)消息:表'%s'不是用LOCK TABLES 锁定1101 SQLSTATE:42000(ER_BLOB_CANT_HAVE_DEFAULT)消息:错误将BLOB / TEXT列'%S'可'T有默认值错误:1102 SQLSTATE:42000(ER_WRONG_DB_NAME)消息:不正确的数据库名称'%s' 错误:1103 SQLSTATE:42000(ER_WRONG_TABLE_NAME)消息:不正确的表名'%s'的错误:1104 SQLSTATE:42000(ER_TOO_BIG_SELECT如果SELECT没关系错误:1105 SQLSTATE:HY000(ER_UNKNOWN_ERROR)消息:未知的错误错误:1106 SQLSTATE:42000 )消息:SELECT将审查超过MAX_JOIN_SIZE行;检查您的WHERE和使用集SQL_BIG_SELECTS = 1 =#或设置SQL_MAX_JOIN_SIZE (ER_UNKNOWN_PROCEDURE)消息:未知的过程'%s' 的错误:1107 SQLSTATE:42000(ER_WRONG_PARAMCOUNT_TO_PROCEDURE)消息:不正确的参数计数过程'%s' 的错误:1108 SQLSTATE:HY000(ER_WRONG_PARAMETERS_TO_PROCEDURE)消息:过程'%s'不正确的参数错误:1109 SQLSTATE:42S02(ER_UNKNOWN_TABLE)消息:未知的表格,在%s'%s' 的错误:1110 SQLSTATE:42000(ER_FIELD_SPECIFIED_TWICE)消息:列'%s'指定两次错误:1111 SQLSTATE:HY000(ER_INVALID_GROUP_FUNC_USE)消息:无效使用组函数错误:1112 SQLSTATE:42000(ER_UNSUPPORTED_EXTENSION)消息:表'%s'的使用不存在的扩展,在这个版本的MySQL错误:1113 SQLSTATE:42000(ER_TABLE_MUST_HAVE_COLUMNS)消息:表必须至少有 1 列错误:1114 SQLSTATE:HY000(ER_RECORD_FILE_FULL)消息:表'%s'是完整的错误:1115 SQLSTATE:42000(ER_UNKNOWN_CHARACTER_SET)消息:未知字符集:'%S' 错误:1116 SQLSTATE:HY000(ER_TOO_MANY_TABLES)消息:太多的表,在连接MySQL只能使用%d个表中的错误:1117 SQLSTATE:HY000(ER_TOO_MANY_FIELDS)消息:太多列错误:1118 SQLSTATE:42000(ER_TOO_BIG_ROWSIZE)消息:行大小太大。使用的表类型的最大行大小,不计斑点,是% ld。你必须改变一些列的文本或BLOB的错误:1119 SQLSTATE:HY000(ER_STACK_OVERRUN)消息:线程堆栈溢出:二手:%%LD栈的LD 。使用“mysqld的- O thread_stack =#'指定更大的堆栈如果需要错误:1120 SQLSTATE:42000(ER_WRONG_OUTER_JOIN)消息:发现在跨依赖的OUTER JOIN;检查您在条件错误:1121 SQLSTATE:42000(ER_NULL_COLUMN_IN_INDEX)消息:列'%s'的使用UNIQUE或指数,但不定义为NOT NULL错误:1122 SQLSTATE:HY000(ER_CANT_FIND_UDF)消息:无法加载函数'%s '错误:1123 SQLSTATE:HY000(ER_CANT_INITIALIZE_UDF)消息:可以 “ 吨初始化函数'%s';%的错误:1124 SQLSTATE:HY000(ER_UDF_NO_PATHS)消息:不允许为共享库路径错误:1125 SQLSTATE:HY000(ER_UDF_EXISTS)消息:函数'%s'已存在错误:1126 SQLSTATE: HY000(ER_CANT_OPEN_LIBRARY)消息:无法打开共享库'%S'(errno的:%d的%s的)错误:1127 SQLSTATE:HY000(ER_CANT_FIND_DL_ENTRY)消息:无法找到函数'%s'在库' 错误:1128 SQLSTATE:HY000(ER_FUNCTION_NOT_DEFINED)消息:函数'%s'没有定义的错误:1129 SQLSTATE:HY000(ER_HOST_IS_BLOCKED)消息:'%s'的主机,是因为很多连接错误封锁,疏通'mysqladmin的嵌入式主机的错误 : 1130 SQLSTATE:HY000(ER_HOST_NOT_PRIVILEGED)消息:主机'%s'中是不允许连接到这个MySQL服务器的错误:1131 SQLSTATE:42000(ER_PASSWORD_ANONYMOUS_USER)消息:您正在使用MySQL作为匿名用户和匿名用户的,不得擅自更改密码错误:1132 SQLSTATE:42000(ER_PASSWORD_NOT_ALLOWED)消息:您必须有权限才能更新在MySQL数据库中的表能够改变他人的密码错误:1133 SQLSTATE:42000(ER_PASSWORD_NO_MATCH)消息:无法找到任何匹配的行user表中的错误:1134 SQLSTATE:HY000(ER_UPDATE_INFO)消息:匹配的行:%ld的改变了:%ld的警告:%ld的错误:1135 SQLSTATE:HY000(ER_CANT_CREATE_THREAD)消息:无法创建一个新线程(errno设置为%d)如果你没有可用内存,你可以参考手册为可能的OS相关的错误错误:1136 SQLSTATE:21S01(ER_WRONG_VALUE_COUNT_ON_ROW)消息:列计数不匹配行%ld的值计数错误:1137 SQLSTATE: HY000(ER_CANT_REOPEN_TABLE)留言:不能重新打开表:'%s'的错误:1138 SQLSTATE:22004(ER_INVALID_USE_OF_NULL)消息:无效使用NULL值错误:1139 SQLSTATE:42000(ER_REGEXP_ERROR)消息:错误'%s'的regexp的错误:1140 SQLSTATE:42000(ER_MIX_OF_GROUP_FUNC_AND_FIELDS)消息:混合组列(MIN(),MAX(),COUNT(),... ...)没有组列是非法的,如果有没有GROUP BY 子句错误:1141 SQLSTATE: 42000 (ER_NONEXISTING_GRANT)消息:有没有这样的补助金为用户'%s'主机'%s'的定义的错误:1142 SQLSTATE:42000(ER_TABLEACCESS_DENIED_ERROR)消息:%s命令拒绝[用户'%s的'@'%s'](mailto:‘%s’@'%s’)的表'%s' 的错误:1143 SQLSTATE:42000(ER_COLUMNACCESS_DENIED_ERROR)消息: [用户'%s](mailto:‘%s’@'%s’)的%s命令[否认'@'%s'](mailto:‘%s’@'%s’)的列'%s'在表'%s' 的错误:1144 SQLSTATE:42000 ( ER_ILLEGAL_GRANT_FOR_TABLE)消息:非法GRANT / REVOKE命令,请参考手册,以了解哪些权限可用于错误:1145 SQLSTATE:42000(ER_GRANT_WRONG_HOST_OR_USER)消息:主机或用户参数授予太长的错误:1146 SQLSTATE:42S02( ER_NO_SUCH_TABLE )消息:表'%s.%s'的是不存在的错误:1147 SQLSTATE:42000(ER_NONEXISTING_TABLE_GRANT)消息:有没有这样的补助金,为用户'%s'表'%s'主机'%s '的定义错误:1148 SQLSTATE:42000(ER_NOT_ALLOWED_COMMAND)消息:不允许使用的命令是这个版本的MySQL 错误:1149 SQLSTATE:42000(ER_SYNTAX_ERROR)消息:您已在您的SQL语法错误,请检查你的MySQL服务器对应手册使用正确的语法的版本1150 SQLSTATE:HY000(ER_DELAYED_CANT_CHANGE_LOCK)消息:错误的延迟插入线程不能获得请求的锁表%s错误:1151 SQLSTATE:HY000(ER_TOO_MANY_DELAYED_THREADS)消息:太多延迟使用线程错误: 1152 SQLSTATE:08S01(ER_ABORTING_CONNECTION)消息:终止连接%ld的DB:'%s'的用户:'%S'(%S)错误:1153 SQLSTATE:08S01(ER_NET_PACKET_TOO_LARGE)消息:得到一个比max_allowed_packet个'字节的数据包更大错误:1154 SQLSTATE:08S01(ER_NET_READ_ERROR_FROM_PIPE)消息:从连接管得到一个读错误错误:1155 SQLSTATE:08S01(ER_NET_FCNTL_ERROR)消息:的fcntl()得到一个错误错误:1156 SQLSTATE:08S01(ER_NET_PACKETS_OUT_OF_ORDER)消息:得到的数据包出错误:1157 SQLSTATE:08S01(ER_NET_UNCOMPRESS_ERROR)消息:无法解压缩通信包错误:1158 SQLSTATE:08S01(ER_NET_READ_ERROR)消息:得到一个错误,阅读通信数据包错误:1159 SQLSTATE:08S01(ER_NET_READ_INTERRUPTED)消息 :超时阅读通信包错误:1160 SQLSTATE:08S01(ER_NET_ERROR_ON_WRITE)消息:得到书面通信的数据包错误错误:1161 SQLSTATE:08S01(ER_NET_WRITE_INTERRUPTED)消息:超时书面通信的数据包错误:1162 SQLSTATE:42000(ER_TOO_LONG_STRING)消息:结果字符串长于“max_allowed_packet个”字节错误:1163 SQLSTATE:42000(ER_TABLE_CANT_HANDLE_BLOB)消息:使用的表类型不支持BLOB / TEXT 列错误:1164 SQLSTATE:42000(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT)消息:使用的表类型不支持 AUTO_INCREMENT列错误:1165 SQLSTATE:HY000(ER_DELAYED_INSERT_TABLE_LOCKED)消息:INSERT DELAYED的,不能使用与表'%s',因为它是用LOCK TABLES锁定错误:1166 SQLSTATE:42000(ER_WRONG_COLUMN_NAME)消息:不正确的列名'%s' 错误:1167 SQLSTATE:42000(ER_WRONG_KEY_COLUMN)消息:所使用的存储引擎不能索引列'%s' 的错误:1168 SQLSTATE:HY000(ER_WRONG_MRG_TABLE)消息:在MERGE表中的所有表是不相同的定义的错误: 1169 SQLSTATE:23000 (ER_DUP_UNIQUE)留言:不能写,因为唯一约束,表'%s'的错误:1170 SQLSTATE:42000(ER_BLOB_KEY_WITHOUT_LENGTH)消息:BLOB / TEXT列'%s'的关键指标没有一个密钥长度错误: 1171 SQLSTATE:42000(ER_PRIMARY_CANT_HAVE_NULL)消息:主键的所有部分必须为NOT NULL;如果您需要在一个关键的NULL,使用独特,而不是错误:1172 SQLSTATE:42000(ER_TOO_MANY_ROWS)消息:结果超过一行的错误:1173 SQLSTATE:42000(ER_REQUIRES_PRIMARY_KEY)消息:此表类型需要一个主键错误:1174 SQLSTATE:HY000(ER_NO_RAID_COMPILED)消息:这版本的MySQL不支持RAID的编译错误:1175 SQLSTATE:HY000(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE)消息:正在使用安全更新模式,你试过没有一个WHERE使用一个键列更新表中的错误:1176 SQLSTATE:HY000(ER_KEY_DOES_NOT_EXITS)消息:键'%s'不存在于表'%s ' 错误:1177 SQLSTATE :42000(ER_CHECK_NO_SUCH_TABLE)消息:无法打开表错误:1178 SQLSTATE:42000(ER_CHECK_NOT_IMPLEMENTED)消息:表的存储引擎不支持%s的错误:1179 SQLSTATE:25000(ER_CANT_DO_THIS_DURING_AN_TRANSACTION)消息:你是不是允许在交易执行命令错误:1180 SQLSTATE:HY000(ER_ERROR_DURING_COMMIT)消息:在提交错误%d 错误:1181 SQLSTATE:HY000(ER_ERROR_DURING_ROLLBACK)消息:在回滚错误%d 错误:1182 SQLSTATE:HY000(ER_ERROR_DURING_FLUSH_LOGS)消息:得到了错误,在FLUSH_LOGS%d个错误:1183 SQLSTATE:HY000(ER_ERROR_DURING_CHECKPOINT)消息:在检查点期间得到了错误%d错误:1184 SQLSTATE:08S01(ER_NEW_ABORTING_CONNECTION)消息:终止连接%劳工处DB:'%s'的用户 :“ %s'的主机:'%s'的(%S)错误:1185 SQLSTATE:HY000(ER_DUMP_NOT_IMPLEMENTED)消息:不支持二进制表转储的表的存储引擎错误:1186 SQLSTATE:HY000(ER_FLUSH_MASTER_BINLOG_CLOSED)消息:binlog的封闭 , 可以不复位主错误:1187 SQLSTATE:HY000(ER_INDEX_REBUILD)消息:无法重建的的转储表'%s'的索引错误:1188 SQLSTATE:HY000(ER_MASTER)消息:从主的错误:'%1! “ 错误:1189 SQLSTATE: 08S01 (ER_MASTER_NET_READ)消息:从主NET错误阅读错误:1190 SQLSTATE:08S01(ER_MASTER_NET_WRITE)消息:净写入主错误的错误:1191 SQLSTATE:HY000(ER_FT_MATCHING_KEY_NOT_FOUND)消息:无法找到匹配的列列表错误的FULLTEXT索引 :1192 SQLSTATE:HY000(ER_LOCK_OR_ACTIVE_TRANSACTION)消息:无法执行给定的命令,因为你已经积极锁定的表或一个活跃的交易错误:1193 SQLSTATE:HY000(ER_UNKNOWN_SYSTEM_VARIABLE)消息:未知的系统变量'%s'的错误:1194 SQLSTATE :HY000( ER_CRASHED_ON_USAGE)消息:表'%s'被标记为坠毁,并应修复的错误:1195 SQLSTATE:HY000(ER_CRASHED_ON_REPAIR)消息:表'%s'是应声而最后一个(自动)修复失败标志着1196 SQLSTATE :错误: HY000(ER_WARNING_NOT_COMPLETE_ROLLBACK)留言:不能改变一些非事务表回滚错误:1197 SQLSTATE:HY000(ER_TRANS_CACHE_FULL)消息:多语句事务需要超过“max_binlog_cache_size字节的存储空间,增加了mysqld的变量,并再试一次错误:1198 SQLSTATE:HY000(ER_SLAVE_MUST_STOP)消息:此操作无法与正在运行的从属执行;运行STOP SLAVE第一个错误:1199 SQLSTATE:HY000

 3、parse 解析

建表

create table t1(id int);insert into t1 values(1)
parse 解析:void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state) { /* ...... */ /* 检查query_cache,如果结果存在于cache中,直接返回 */ if (query_cache_send_result_to_client(thd, rawbuf, length) lex;    /* 解析语句 */ bool err= parse_sql(thd, parser_state, NULL); /* 整理语句格式,记录 general log */ /* ...... */ /* 执行语句 */ error= mysql_execute_command(thd); /* 提交或回滚没结束的事务(事务可能在mysql_execute_command中提交,用trx_end_by_hint标记事务是否已经提交) */ if (!thd->trx_end_by_hint)  { if (!error && lex->ci_on_success) trans_commit(thd);  if (error && lex->rb_on_fail) trans_rollback(thd); }

执行mysql_execute_command()

 /* */ /* ...... */ case SQLCOM_INSERT: {   /* 检查权限 */ if ((res= insert_precheck(thd, all_tables))) break; /* 执行insert */ res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, lex->duplicates, lex->ignore);/* 提交或者回滚事务 */ if (!res) { trans_commit_stmt(thd); trans_commit(thd); thd->trx_end_by_hint= TRUE; } else if (res) { trans_rollback_stmt(thd); trans_rollback(thd); thd->trx_end_by_hint= TRUE; }

mysql_insert()

bool mysql_insert(THD *thd,TABLE_LIST *table_list, List &fields, /* insert 的字段 */ List &values_list, /* insert 的值 */ List &update_fields, List &update_values, enum_duplicates duplic, bool ignore){  /*对每条记录调用 write_record */ while ((values= its++)) {    if (lock_type == TL_WRITE_DELAYED) { LEX_STRING const st_query = { query, thd->query_length() }; DEBUG_SYNC(thd, "before_write_delayed"); /* insert delay */ error= write_delayed(thd, table, st_query, log_on, &info); DEBUG_SYNC(thd, "after_write_delayed"); query=0; } else /* normal insert */ error= write_record(thd, table, &info, &update); }  /* 这里还有 thd->binlog_query()写binlog my_ok()返回ok报文,ok报文中包含影响行数 */ 

write_record

/* COPY_INFO *info 用来处理唯一键冲突,记录影响行数 COPY_INFO *update 处理 INSERT ON DUPLICATE KEY UPDATE 相关信息*/int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update){ if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE) { /* 处理 INSERT ON DUPLICATE KEY UPDATE 等复杂情况 */ } /* 调用存储引擎的接口 */ else if ((error=table->file->ha_write_row(table->record[0]))) { DEBUG_SYNC(thd, "write_row_noreplace"); if (!ignore_errors || table->file->is_fatal_error(error, HA_CHECK_DUP)) goto err;  table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; }}

ha_write_row、write_row

/* handler 是各个存储引擎的基类,这里我们使用InnoDB引擎*/ int handler::ha_write_row(uchar *buf){ /* 指定log_event类型*/ Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; error= write_row(buf);}

入引擎层

这里是innodb引擎,handler对应ha_innobase 插入的表信息保存在handler中

int ha_innobase::write_row(/*===================*/ uchar* record) /*!< in: a row in MySQL format */{error = row_insert_for_mysql((byte*) record, prebuilt);}
UNIV_INTERNdberr_trow_insert_for_mysql( /*=================*/ byte* mysql_rec, /*!< in: row in the MySQL format */ row_prebuilt_t* prebuilt) /*!row, prebuilt, mysql_rec); thr->run_node = node; thr->prev_node = node;/*插入记录*/ row_ins_step(thr);}
    UNIV_INTERN    que_thr_t*    row_ins_step(    /*=========*/     que_thr_t* thr) /*!table, LOCK_IX, thr);     /*插入记录*/     err = row_ins(node, thr);    }

非主键的处理

InnoDB表是基于B+树的索引组织表,如果InnoDB表没有主键和唯一键,需要分配隐含的row_id组织聚集索引

static __attribute__((nonnull, warn_unused_result))dberr_trow_ins(/*====*/ ins_node_t* node, /*!< in: row insert node */ que_thr_t* thr) /*!state == INS_NODE_ALLOC_ROW_ID) {/*若innodb表没有主键和唯一键,用row_id组织索引*/ row_ins_alloc_row_id_step(node);/*获取row_id的索引*/ node->index = dict_table_get_first_index(node->table); node->entry = UT_LIST_GET_FIRST(node->entry_list);}/*遍历所有索引,向每个索引中插入记录*/ while (node->index != NULL) { if (node->index->type != DICT_FTS) { /* 向索引中插入记录 */ err = row_ins_index_entry_step(node, thr); if (err != DB_SUCCESS) { return(err); } } /*获取下一个索引*/ node->index = dict_table_get_next_index(node->index); node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry); } }}

插入单个索引项

static __attribute__((nonnull, warn_unused_result))dberr_trow_ins_index_entry_step( /*=====================*/ ins_node_t* node, /*!< in: row insert node */ que_thr_t* thr) /*!index, node->entry, node->row);/*插入索引项*/ err = row_ins_index_entry(node->index, node->entry, thr); return(err);}
staticdberr_trow_ins_index_entry( /*================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in/out: index entry to insert */ que_thr_t* thr) /*!< in: query thread */ { if (dict_index_is_clust(index)) { /* 插入聚集索引 */ return(row_ins_clust_index_entry(index, entry, thr, 0)); } else { /* 插入二级索引 */ return(row_ins_sec_index_entry(index, entry, thr)); }}

row_ins_clust_index_entry 和 row_ins_sec_index_entry

函数结构类似,只分析插入聚集索引

UNIV_INTERNdberr_trow_ins_clust_index_entry(/*======================*/ dict_index_t* index, /*!< in: clustered index */ dtuple_t* entry, /*!< in/out: index entry to insert */ que_thr_t* thr, /*!< in: query thread */ ulint n_ext) /*!table->foreign_list)) { err = row_ins_check_foreign_constraints( index->table, index, entry, thr); if (err != DB_SUCCESS) { return(err); } }  /* flush log,make checkpoint(如果需要) */ log_free_check();/* 先尝试乐观插入,修改叶子节点 BTR_MODIFY_LEAF */ err = row_ins_clust_index_entry_low( 0, BTR_MODIFY_LEAF, index, n_uniq, entry, n_ext, thr,  &page_no, &modify_clock);  if (err != DB_FAIL) { DEBUG_SYNC_C("row_ins_clust_index_entry_leaf_after"); return(err); } /* flush log,make checkpoint(如果需要) */ log_free_check();/* 乐观插入失败,尝试悲观插入 BTR_MODIFY_TREE */ return(row_ins_clust_index_entry_low( 0, BTR_MODIFY_TREE, index, n_uniq, entry, n_ext, thr, &page_no, &modify_clock));

4、直接简单插入记录

CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;insert into t1 values (4,2);
./storage/innobase/handler/ha_innodb.ccha_innobase::write_row|–>row_insert_for_mysql  |–>转换记录格式row_mysql_convert_row_to_innobase |–>保存检查点savept = trx_savept_take(trx); |–>row_ins_step  |–>加IX锁lock_table(0, node->table, LOCK_IX, thr)    |–> row_ins     //轮询索引,向表中插入记录,这里只有聚集索引     |–>row_ins_index_entry_step      |–>构建索引记录(node->entry)row_ins_index_entry_set_vals      |–>row_ins_index_entry(node->index, node->entry, 0, TRUE, thr)|–>检查外键约束row_ins_check_foreign_constraints|->使用如下两步尝试插入记录  >>step1.row_ins_index_entry_low(BTR_MODIFY_LEAF, index, entry,n_ext, thr)/* Try first optimistic descent to the B-tree */  >>step2,若step1失败,row_ins_index_entry_low(BTR_MODIFY_TREE, index, entry,n_ext, thr);/* Try then pessimistic descent to the B-tree */    |–> row_ins_index_entry_low     |–>确定search_mode      1)如果是聚集索引,search_mode为传参BTR_MODIFY_LEAF或BTR_MODIFY_TREE      2)当前事务的check_unique_secondary为false时(由变量unique_checks控制,默认为true,表示检查唯一索引约束),search_mode = mode | BTR_INSERT | BTR_IGNORE_SEC_UNIQUE      3)否则,search_mode = mode | BTR_INSERT      |–>btr_cur_search_to_nth_level  查询索引树,将cursor移动到记录相应的位置(待分析)      |–>检测是否有dupkey,主键索引调用row_ins_duplicate_error_in_clust,二级索引调用row_ins_scan_sec_index_for_duplicate      |–>modify = row_ins_must_modify  (待分析)>>当modify!=0时,表明已经有一个足够长的common prefix,直接覆盖插入记录(待分析)>>当modify=0时1)当mode=BTR_MODIFY_LEAF时,调用btr_cur_optimistic_insert,尝试向一个索引page中插入记录,如果页面空闲空间太小,则返回失败 |–>btr_cur_ins_lock_and_undo //检查是否有锁冲突,并插入undolog  |–>lock_rec_insert_check_and_lock //检查锁冲突   |–>没有下一个记录的锁冲突,即lock_rec_get_first返回NULL,更新二级索引trx id,返回   |–>调用lock_rec_other_has_conflicting,检查是否有其他事务锁有下一个记录的LOCK_X, LOCK_GAP和LOCK_INSERT_INTENTION锁   |–>检测到有冲突lock_rec_enqueue_waiting    |–> 创建锁记录lock_rec_create,type_code为LOCK_X | LOCK_GAP|LOCK_INSERT_INTENTION    |–>检测死锁    |–>如果是聚集索引且不是Insert buffer     |–>写入undo信息 trx_undo_report_row_operation(待分析)     |–>设置聚集索引记录的trx id和回滚段指针,row_upd_index_entry_sys_field   |–>插入记录page_cur_tuple_insert   |–>更新该page的哈希索引btr_search_update_hash_on_insert(待分析)   |–>继承下一条记录的gap锁?(inherit为TRUE),调lock_update_insert,这里不调用 (待分析)  2)否则 a)调用buf_LRU_buf_pool_running_out,返回true,表示少于25%的buffer pool可用。根据注释,对于大事务,会将锁存储在buffer pool中(待证实 b)调用btr_cur_pessimistic_insert(待分析)

drop table t1;

简化版步骤

  • 转换记录格式

  • 保存检查点

  • 插入

  1. 加IX锁

  2. 轮询索引,向表中插入记录,包括步骤:

  • 构建索引记录
  • 检查外键约束
  • row_ins_index_entry_low两步插入:先乐观插入 否则 悲观插入

5、插入记录on duplicate key

CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;insert into t1 values (1,2);insert into t1 values (1,5) on duplicate key update b = b+1;
ha_innobase::write_row->row_insert_for_mysql –>row_ins_step  –>row_ins->row_ins_index_entry  |–>row_ins_index_entry_low   |–>调用row_ins_duplicate_error_in_clust检测是否有dup key错误    |–>对记录加锁     >>对于REPLACE, LOAD DATAFILE REPLACE以及INSERT ON DUPLICATE KEY UPDATE操作,对记录加一个排他锁(LOCK_X)row_ins_set_exclusive_rec_lock      a)聚集索引lock_clust_rec_read_check_and_lock      b)二级索引lock_sec_rec_read_check_and_lock     >>否则,加一个共享锁(LOCK_S)row_ins_set_shared_rec_lock    |–>调用row_ins_dupl_error_with_rec检查物理记录和将要插入的记录是否相同

从Innodb层返回到Server层后,write_record函数根据返回的错误值,继续下面的逻辑

|–>判断是否是dup key错误|–>if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) // bug#52020相关点,稍后再议|–>一堆乱七八糟的检查,构建记录等….|–>row_update_for_mysql传递record到innodb更新数据 |–>row_upd_step->row_upd  |–>row_upd_clust_step   |–>如果没有x锁,则尝试加锁lock_clust_rec_modify_check_and_lock   |–>row_upd_clust_rec(待分析)    |–>btr_cur_optimistic_update