1.TiDB 源码阅读系列文章(十六)INSERT 语句详解
2.故障分析 | 从 Insert 并发死锁分析 Insert 加锁源码逻辑
3.CockroachDB 源码闲逛 - II (insert a row)
4.MySQL全文索引源码剖析之Insert语句执行过程
5.解析Mybaits的法方法insert方法返回数字2147482646的原因
TiDB 源码阅读系列文章(十六)INSERT 语句详解
作者:于帅鹏 在之前的系列文章《TiDB 源码阅读系列文章(四)INSERT 语句概览》中,我们初步探讨了 INSERT 的源码基本流程。本文将深入讨论 TiDB 中 INSERT 语句的法方法多样性,特别是源码处理 Unique Key 冲突的策略,以期帮助读者全面理解这些复杂情况。法方法 首先,源码java源码转为exe我们来划分 TiDB 中的法方法 INSERT 语句类型:基本 INSERT、INSERT IGNORE、源码INSERT ON DUPLICATE KEY UPDATE、法方法以及 REPLACE INTO。源码这些语句各有不同的法方法行为和语义:基本 INSERT:遇到唯一约束冲突时,执行失败。源码
INSERT IGNORE:冲突时忽略,法方法但显示警告。源码
INSERT ON DUPLICATE KEY UPDATE:更新冲突行,法方法可能引发进一步的冲突处理。
REPLACE INTO:冲突时删除并替换,可影响多行。
在源码实现上,TiDB 为这些复杂操作设计了独特的逻辑。例如,Basic INSERT 的执行逻辑在 executor/insert.go 中,而 INSERT IGNORE 则需要在执行过程中即时检查并处理冲突,通过 batchChecker 实现批量检测。 INSERT ON DUPLICATE KEY UPDATE 是最复杂的,涉及 INSERT 和 UPDATE 的组合,它会先尝试 UPDATE 冲突行,然后在事务提交时统一执行。REPLACE INTO 则在冲突时执行删除和插入,一次操作可能影响多行。 深入理解这些 INSERT 语句的经传公式指标源码实现,有助于在实际使用时做出合理的选择,尤其是在处理大量数据和复杂约束时。对于希望贡献代码的开发者,这将是一个宝贵的参考资料。故障分析 | 从 Insert 并发死锁分析 Insert 加锁源码逻辑
死锁是数据库并发操作中的常见问题,涉及业务关联、机制复杂、类型多样等特点,给分析带来了挑战。本文以MySQL数据库中并发Insert导致死锁为例,通过问题发现、重现、根因分析和解决策略,提供一套科学有效的死锁处理方案。文章首先概述了死锁的基本现象和常见特性,指出死锁触发原因与应用逻辑相关,且涉及多个事务。由于不同数据库的锁实现机制差异,分析死锁问题往往不易。接着,文章详细描述了死锁问题的实例,包括日志提示、innodb status输出和事务执行过程。通过与研发团队的沟通和问题复现,文章进一步分析了事务之间的锁等待和持有状态,提出了问题的具体原因。为解决死锁问题,文章提出了优化唯一索引和调整并发策略的建议,并结合MySQL的锁实现机制,通过源码分析揭示了死锁产生的中视蓝图溯源码根本原因。最终,文章总结了避免死锁的关键措施,包括选择适合的隔离级别、减少对Unique索引的依赖,并通过性能数据追踪和源码理解来有效诊断和解决死锁问题。文章旨在为数据库运维人员提供一套实用的死锁处理方法,促进数据库系统稳定性和性能优化。
CockroachDB 源码闲逛 - II (insert a row)
本文将深入探讨 CockroachDB 的启动过程以及处理一条简单 SQL(如插入一行数据)的具体流程。CockroachDB 使用 Go 语言中流行的 Cobra 库来构建其命令行界面(CLI),在使用 `start` 命令启动服务端后,代码从特定位置开始执行。
启动初期,CockroachDB 会准备好各种日志和 pprof 功能。pprof 功能允许通过开关控制定期导出 CPU 和内存(通过 go/jemalloc)的性能分析报告,并定期清除旧的 pprof 数据,这有助于在排查问题时找到事故现场的堆栈或性能数据。
之后,服务端使用一个端口同时处理 PostgreSQL、HTTP 和 gRPC 协议,代码进入 `Server.start()` 方法。这个方法包含复杂的逻辑用于节点发现和 bootstrap。主要关注点在于 SQL 处理,尤其是 PostgreSQL 协议下的客户端连接。
当客户端通过 PG 协议连接到服务端时,代码进入 `pgwire.Server#ServeConn` 方法。通过校验版本等步骤后,进入 `conn.serveImpl` 方法,这是处理请求的主要逻辑。在这里,每个客户端连接由两个 goroutine 分别处理读取协议解析和命令执行。多线程轰炸源码这种设计允许在执行过程中同时接收客户端连接事件,例如在执行大规模 SQL 过程时,通过关闭其中一个 goroutine 可以在 SQL 执行的同时响应客户端的 `FIN` 指令。
在客户端连接的两个 goroutine 准备好后,发送的 SQL 语句开始在 `coordinator-side` 进行处理。首先,`read goroutine` 解析网络包,并根据不同的 PG cmd 分发到相应的方法进行处理。对于简单的文本执行查询,`handleSimpleQuery` 方法相对简单。为了区分不同批量的命令,当一组命令推送到 `stmtBuf` 后,会插入一个哨兵 `Sync` 来标记当前批次结束以及后续命令属于下一个批次。
随后,`process goroutine` 从 `stmtBuf` 中获取命令,根据不同的命令类型分发到相应的 `exec*` 方法。例如,简单查询产生的 `ExecStmt` 会进入 `execStmt` 方法,在此之前会创建 `stmtRes` 来封装后续返回客户端响应的缓冲区刷新逻辑。
在处理 SQL 语句时,CockroachDB 会维护一个状态机(StateMachine),用于管理当前连接的事务状态。状态机的定义和行为主要与事务相关,包括 `noTxn`、`open`、`abort`、`implicit` 等状态。在处理插入一行数据的简单语句(如 `INSERT INTO t (a) VALUES (1);`)时,流程如下:
首先,客户端与服务端建立连接,springmvc5.0源码启动两个 goroutine。当插入语句发送到服务端后,`read goroutine` 开始解析并放置到 `stmtBuf`。
随后,`process goroutine` 从 `stmtBuf` 拿出命令,识别为 `ExecStmt`。由于执行此语句前未开始事务,当前连接的状态机处于 `stateNoTxn`。因此,执行 `execStmtInNoTxnState` 方法,因为没有事务,仅执行 `execStmtInNoTxnState` 的默认分支,返回 `eventTxnStart` 事件和 `eventTxnStartPayload`。此时,状态机应用 `noTxnToOpen` 进程,为隐式事务的启动做准备。服务端通过 `client.NewTxn` 创建事务,获取时间戳并准备 `sender` 和 `coordinator` 等工作。接着,设置 `advanceInfo` 为 `advanceOne`、`noRewind`(无需回移 `stmtBuf`,通常重试时需要回移)和 `txnState` 为 `txnStart`。事务状态为 `open` 后,`execCmd` 会从 `stmtBuf` 中继续取出插入语句并执行。
当当前事务状态为 `open` 且为 `implicit` 时,`execStmtInOpenState` 方法继续执行。由于当前 SQL 不是 `BEGIN`、`COMMIT` 等操作,挂载了 `handleAutoCommit` 的 `defer` 函数,并处理 `AS OF` 时间逻辑后,进入 `dispatchToExecutionEngine` 方法。
在 `makeExecPlan` 方法中,创建逻辑计划。接下来,评估是否能够分布执行逻辑计划(对于插入操作,CockroachDB 当前不支持分布式计划)。然后,为逻辑计划准备上下文,调用 `execWithDistSQLEngine`。对于不可分布执行的情况,创建简化版的 `planCtx`,用于生成物理计划。在此步骤中,生成物理计划(如 `row count` 算子)并最终生成执行流程。
在准备和生成流程后,服务端启动在本地节点的执行流程。通过 `local execution` 的 `setup` 和 `run` 方法,执行生成的处理器(如 `planNodeToRowSource`)。在 `run` 方法中,执行 `rowCountNode` 算子,进而触发 `insertNode` 的 `BatchNext`,以火山模式(一次过一个批处理的多个行)执行插入操作。
插入操作中,`BatchNext` 分批处理,根据 `maxInsertBatchSize`(默认为 )进行分批。对于非最后一批次,会通过 `txn.Run` 发送至存储节点,将数据分批存储。在 `checkHelper` 函数中,检查表约束,分为 `eval` 和 `input` 模式,前者是老逻辑,后者在插入前检查约束结果,作为插入算子的输入,有利于优化插入操作。
添加批处理时,调用 `initResult` 准备每个 `CPut` 的结果。如果批处理中某个命令失败(如序列化失败),会在 `initResult` 中保存序列化失败信息。
之后,将准备好的批处理发送至 `replica-side`。在 `finalize` 中,将 `EndTransactionRequest` 添加到批处理的末尾,通过 `txn.Run` 发起。此时,批处理中包含一个条件 `put` 和一个结束事务请求,服务端通过 `DistSender.Send` 将批处理发送至 `replica-side`。批处理中的 `result` 包含 `err` 信息,用于验证批处理序列化无误。
在 `replica-side`,请求到达节点的存储层,找到相关范围的副本对象并处理等待逻辑。对于写入操作,使用 Raft 进行 `Replica.executeWriteBatch`。在此方法中,使用 `Latch` 机制来优化对交叠和非交叠批处理的处理,同时执行批处理命令的 `evaluateWriteBatch` 方法将所有命令应用到数据中,生成 `engine.Batch` 并构建 `ProposalData`。最终,通过 Raft 提出修改,实现数据的最终一致性。
最后,执行成功或失败后,结果会沿原路径返回至客户端。
总结,本文详细阐述了 CockroachDB 从启动到处理简单 SQL(如插入操作)的全过程。通过深入分析,读者能够更好地理解 CockroachDB 的内部工作机制,为后续阅读代码提供基础。未来计划将关注点扩展到重试处理逻辑,进一步探索 `stmtBuf` 和状态机在 CockroachDB 中的使用。
MySQL全文索引源码剖析之Insert语句执行过程
本文来源于华为云社区,作者为GaussDB数据库,探讨了MySQL全文索引源码中Insert语句的执行过程。
全文索引是一种常用于信息检索的技术,它通过倒排索引实现,即单词和文档的映射关系,如(单词,(文档,偏移))。以创建一个表并在opening_line列上建立全文索引为例,插入'Call me Ishmael.'时,文档会被分为'call', 'me', 'ishmael'等单词,并记录在全文索引中。
全文索引Cache的作用类似于Change Buffer,用于缓存分词结果,避免频繁刷盘。Innodb使用fts_cache_t结构来管理cache,每个全文索引的表都会在内存中创建一个fts_cache_t对象。
Insert语句的执行分为三个阶段:写入行记录阶段、事务提交阶段和刷脏阶段。写入行记录阶段生成doc_id并写入Innodb的行记录,并将doc_id缓存。事务提交阶段对文档进行分词,获取{ 单词,(文档,偏移)}关联对,并插入到cache。刷脏阶段后台线程将cache刷新到磁盘。
全文索引的并发插入可能导致OOM问题,可通过修复patch #解决。当MySQL进程崩溃时,fts_init_index函数会恢复crash前的cache数据。
解析Mybaits的insert方法返回数字的原因
解析MyBatis的insert方法返回数字的原因,主要在于执行器的配置。在配置文件中,如果将executorType设置为BATCH,更新和插入操作的返回值会丢失。这是由于BATCH执行器在MyBatis中的设计特性所导致。当配置为BATCH时,MyBatis会使用BatchExecutor进行操作,这个执行器会固定返回固定的数字,而不是实际影响的行数。
为了验证这一点,我们可以通过以下步骤进行操作:打开配置文件,检查SqlSessionTemplate构造参数ExecutorType的设置。发现它是一个枚举类型,仅包含SIMPLE、RESUME、BATCH三种配置值。当设置为BATCH时,操作会丢失返回值。
在实际代码中,操作最终指向的是SqlSession的insert方法。进一步深入源码,我们发现SqlSession的insert方法调用了update方法,而这个update方法实际上调用了executor的update方法。由于配置了BATCH,因此最终调用的是BatchExecutor的update方法。在这里,我们看到了问题的关键所在:BatchExecutor的update方法总是返回一个固定的数字,即-。
反之,当将executorType设置为SIMPLE时,返回的值将正常反映实际影响的行数。因此,解决这个问题的方法是将配置文件中的executorType设置为SIMPLE,或者直接注释掉相关配置,以避免使用BATCH执行器。
总结,配置MyBatis的insert方法返回数字的原因在于执行器的配置选择。通过翻阅源码,我们理解了MyBatis内部处理逻辑以及执行器之间的差异。在实际编程中,遇到类似问题时,翻阅源码可以帮助我们更深入地理解问题,并找到解决方法。