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

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

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

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

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

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

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

目录

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事务的锁等待

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

SELECT
r.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_time
from information_schema.innodb_lock_waits w
inner join information_schema.innodb_trx b
on b.trx_id = w.blocking_trx_id
inner join information_schema.innodb_trx r
on 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) <= 0) 
 {
 LEX *lex= thd->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<Item> &fields, /* insert 的字段 */ List<List_item> &values_list, /* insert 的值 */ List<Item> &update_fields,
 List<Item> &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_INTERN
dberr_t
row_insert_for_mysql( 
/*=================*/
 byte* mysql_rec, /*!< in: row in the MySQL format */
 row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL
 handle */
{
		/*记录格式从MySQL转换成InnoDB*/
		row_mysql_convert_row_to_innobase(node->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) /*!< in: query thread */
    {
            /*给表加IX锁*/
            err = lock_table(0, node->table, LOCK_IX, thr);

            /*插入记录*/
            err = row_ins(node, thr);
    }

非主键的处理

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

static __attribute__((nonnull, warn_unused_result))
dberr_t
row_ins(
/*====*/
 ins_node_t* node, /*!< in: row insert node */
 que_thr_t* thr) /*!< in: query thread */
{
		if (node->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_t
row_ins_index_entry_step( 
/*=====================*/
 ins_node_t* node, /*!< in: row insert node */
 que_thr_t* thr) /*!< in: query thread */
{
 dberr_t err;

 /*给索引项赋值*/
 row_ins_index_entry_set_vals(node->index, node->entry, node->row);

		/*插入索引项*/
 err = row_ins_index_entry(node->index, node->entry, thr);

 return(err);
}
static
dberr_t
row_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_INTERN
dberr_t
row_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) /*!< in: number of externally stored columns */
{
 if (UT_LIST_GET_FIRST(index->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.cc
ha_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值