一条sql详解MYSQL的架构设计详情

目录
  • 1 前言
  • 2 应用层
    • 2.1 连接线程处理
  • 3 服务层
    • 3.1 SQL 接口
    • 3.2 SQL解析器
    • 3.3 SQL优化器
    • 3.4 执行器
    • 3.5 查询缓存
  • 4 存储引擎层
    • 4.1 概述
    • 4.2 缓冲池(buffer pool)
      • 4.2.1 数据页、缓存页和脏页
      • 4.2.2 元数据
      • 4.2.3 free链表
      • 4.2.4 flush链表
      • 4.2.5 LRU链表
      • 4.2.6 小结
    • 4.3 undo log
    • 4.4 redo log
  • 5 总结

1 前言

对于一个服务端开发来说 MYSQL 可能是他使用最熟悉的数据库工具,然而,大部分的Java工程师对MySQL的了解和掌握程度,大致就停留在这么一个阶段:它可以建库、建表、建索引,然后就是对里面的数据进行增删改查,语句性能有点差?没关系,在表里建几个索引或者调整一下查询逻辑就可以了,一条sql,MYSQL是如何处理的,为我们做了什么,完全是个黑盒。本文主要通过sql执行的过程打破这样一个黑盒的认知,来了解MYSQL的逻辑架构。

MYSQL的逻辑架构可分为3层:应用层、服务层、存储引擎层。其中存储引擎是MYSQL最有特色的地方,MySQL区别于其他数据库的最重要特点是其插件式的表存储引擎,本文也将着重聊聊最常用的innoDB存储引擎的架构设计原理,假设现有如下sql:

update users set name=’zhangsan’ where id = 10

作为一个java服务端工程师,见到这样一个sql,本能的脑海中立刻就浮现出如下信息:

  • 一个表名为users的表
  • 有两个字段 id、name,id是主键
  • 把users表里的id=10的这个用户名修改为“zhangsan”

那么MYSQL是如何处理这样一个sql呢?带着这个问题,我们来看一下MYSQL是如何通过一个个组件来处理这个sql,来了解MYSQL的整体架构

2 应用层

2.1 连接线程处理

当MYSQL面对上面的sql,首先应该做什么呢?是如何解析?如何选择索引?如何提交事务?当然不是,首先应该解决的是怎么把sql语句传给它。大家都知道,如果我们要访问数据库,那么,首先就需要和数据库建立连接,那么这个连接由谁来建呢,答案就是MYSQL驱动,下面这段maven配置大家应该都很熟悉

java程序就是通过这个驱动包来与数据库建立网络连接。

下图示意:

从图中可以看到这样一个场景:java程序很多个线程并发请求执行上述sql,我们都知道数据库连接是非常占用资源的,尤其是在高并发的情况下,如果每次都去建立数据库连接就会有性能问题,也会影响一个应用程序的延展性,针对这个问题,连接池出现了。

下图示意:

从图中可见网络连接交由线程3监听和读取sql请求,至此MYSQL已经收到我们的请求,当然MYSQL在建立连接时还做了用户鉴权,鉴权依据是: 用户名,客户端主机地址和用户密码;在获取连接后,处理请求时还会做sql请求的安全校验,根据用户的权限判断用户是否可以执行这条sql。

3 服务层

3.1 SQL 接口

从上图中我们知道线程3负责监听并读取sql,拿到这个sql之后,如何执行是一项极其复杂的任务,所以MYSQL提供了SQL接口这么一个组件,线程3会将sql转交给SQL接口来执行如下图:

SQL接口具体处理功能有:DDL、DML、存储过程、视图、触发器等。

3.2 SQL解析器

接着问题来了,SQL接口如何执行本文sql呢?,数据库怎么理解本文这个sql呢?相信懂sql语法的人立马就能知道什么意思,但是MYSQL是个系统不是人,它无法直接理解sql的意思,这个时候关键的组件出场了,SQL解析器的作用主要就是是解析sql语句,最终生成语法树,比如本文sql就可以拆解成如下几个部分:

  • 需要从users表里更新数据
  • 需要更新id字段是10的那行数据
  • 需要把这行数据的name字段的值改为 “zhangsan”

3.3 SQL优化器

当通过SQL 解析器理解了sql语句要干什么之后,该如何实现呢,以本文的更新语句为例,我们可以有以下两种实现方式:

  • 直接定位到users表中id字段等于10的一行数据,然后查出这行数据数据,然后设置name字段为“zhangsan”;
  • 也可以通过更新name字段索引的方式在name索引上遍历id等于10的索引值,然后设置name字段为“zhangsan”。

上面两种途径都能实现最终结果,显然第一种路径更好一些,所以,SQL优化器就是从众多实现路径中选则一条最优的路径出来,也就是我们常说的执行计划。

3.4 执行器

通过SQL优化器我们得到一套执行计划,那么,这个计划怎么执行呢?这个时候就不得不提MYSQL存储引擎,我们都知道MySQL和其他关系型数据库不一样的地方在于它的弹性以及可以通过插件形式提供不同种类的存储引擎,类似java接口的多实现,MYSQL肯定会有一套标准的存储引擎接口,而执行器就是按照执行计划一步一步的调用存储引擎接口完成sql执行而已,如下图:

上图专门将binlog标出来是为了和下文innodb存储引擎的undo log、redo log做区分,强调binlog是server层的日志,后续binlog 和redo log的两阶段方式完成事务的提交会再次提到。

3.5 查询缓存

MYSQL服务层为追求高效也引入了QUERY BUFFER 这个组件,但是这个组件比较鸡肋,缓存不仅需要sql全字匹配命中,而且对基础表的任何修改都会导致这些表的所有缓存失效,既不符合现在用户变量的开发模式,大部分时候也不高效。

MYSQL从5.7开始不推荐使用默认关闭,8.0中不再支持,详细原因如下图:

截图来源MYSQL开发者专区文档

4 存储引擎层

4.1 概述

上文执行器拿到执行计划后,调用存储引擎的接口来完成sql的执行,那么存储引擎如何帮助我们去访问、操作内存以及磁盘上的数据呢?我们都知道MYSQL的存储引擎有很多,实现方式各一,下面让我们继续通过上文的sql来初步了解我们常用的Innodb存储引擎的核心原理和架构设计

重温一下本文sql:

update users set name='zhangsan' where id = 10 —-历史name = ‘lisi'

4.2 缓冲池(buffer pool)

InnoDB存储引擎中有一个非常重要的放在内存里的组件,就是缓冲池(Buffer Pool),这里面会缓存很多的数据,以便于以后在查询的时候,万一你要是内存缓冲池里有数据,就可以不用去查磁盘了,如下图:

缓冲池(buffer pool)在Innodb中的地位类似于我们现在系统设计中redis的地位,在Innodb中引入这一组件的就是为了高效的存取,我们都知道MYSQL查询数据很快,究其原因不止是索引查询,深层次的原因就是所有的增删改查都是在buffer pool这块内存上操作的,相比于操作磁盘,效率不言自明。

4.2.1 数据页、缓存页和脏页

还是拿我们的sql举例,更新id=10的这条记录,难道从磁盘里只拉取id=10数据进入内存中吗?很明显不是,毕竟加入内存的记录不止这一张表,而且单表每行记录也不一样,内存管理会非常困难的,所以,MYSQL对数据抽象出来的一个叫数据页的逻辑概念,每页固定大小默认16KB,可以存多条数据,并且buffer pool里的存储结构和数据页一致,这样内存管理就会简单的多,数据页注册元数据后加载进内存后就是缓存页。

从图中可以看到在缓存页在sql更新完还未刷回硬盘时数据和磁盘中的数据页是不一致的,这个时候我们称这种缓存页为脏页。至于后续脏页如何落盘暂时不提。

4.2.2 元数据

从上图我们看到buffer pool中除了缓存页,还多了一个元数据内存结构,这个可以简单的理解为登记,比如因为疫情外地人回家过年会被当地政府进行登记,记录从哪来、到哪去等信息,便于管理,buffer pool也是这样做的;但是元数据可不止记录缓存页的磁盘地址和内存地址这么简单,buffer pool核心原理都是通过元数据来实现的

4.2.3 free链表

buffer pool在MYSQL初始化的时候,就根据配置在内存中申请了一块连续的空间,申请过后就按数据页的大小和元数据的大小进行合理的划分出很多个连续的、空的缓存页,当需要查询数据的时候就会从磁盘读入数据页放入到缓存页当中,但是由于脏页的存在,数据还未刷盘不能使用,那么数据页加载进哪个缓存页就是个问题。为了解决哪些缓存页是空闲的,MYSQL团队为Buffer pool设计了一个free链表,它是一个双向链表的数据结构,这个free链表里每个节点就是一个空闲的缓存页的元数据块地址,也就是说只要一个缓存页是空闲的,那么他的元数据块就会放入这个free链表中,这样加载数据页是只需要从free链表中找空闲的缓存页即可。

从图中即可看出链表的大致结构,那么现在我们要更新users表中id=10的记录,首先要知道id=10这条记录的数据页有没有在缓存页当中,然后在决定是否是加载数据页还是直接使用缓存页,所以,buffer pool里还有左下角这种hash表,用表空间+数据页号作为key,缓存页地址为value,可以快速判断数据页是否被缓存。

4.2.4 flush链表

本文sql执行更新后,这样就导致内存中的数据和磁盘上的数据不一致,这就表明这个缓存页是脏页,脏页是需要刷新到磁盘文件的。但是不可能所有缓存页都刷回磁盘,比如有的缓存页可能只是查询的时候用到了,没有别更新过,所以数据库就引入flush链表,flush链表和free链表的实现方式一样,都是在元数据中增加两个指针做成双向链表,用来标记链表上的都是脏页,需要刷回磁盘,后续IO线程异步刷盘就是将flush链表的数据刷盘,然后把缓存页移除flush链表,加入free链表当中。

4.2.5 LRU链表

随着不停的把磁盘上的数据页加载到空闲的缓存页里去,free链表中空闲的缓存页越来越少,如果free链表空了,这时候就无法从磁盘加载数据页了,这时候就需要淘汰掉一些缓存页,首先想到的就是把修改过的缓存页刷新回磁盘上,然后清空这个缓存页

具体选择哪个缓存页进行清空呢,数据库引入LRU链表,结构和free链表基本一致,最近访问的缓存页都会被移动到LRU链表的头部,这样尾部的就是少访问的数据,但是这样的LRU有个问题,就是MYSQL的预读机制,会把不常访问或者不访问的数据连带着加载到内存,这样就把这一部分也放在了LRU头结点上,很明显不合理,同理,全表扫描也有这个问题。

从上面可以看出,如果此时需要淘汰缓存页,就可能把热点数据提前淘汰掉。对于这种不合理的LRU算法MYSQL基于冷热数据分离的方法对LRU算法进行如下优化:LRU链表被拆分为两个部分,一部分热数据,一部分冷数据,数据页第一次加载到缓存的时候是放在冷数据表头,在1s后再次访问这个缓存页,就很有可能是热数据,就会把它挪到热数据表头区域,这样设计防止了刚加载就访问造成的假热现象。

冷热区域缓存页移动规则如下:

  • 冷数据 -> 热数据:冷数据区的缓存页是在 1s 后再被访问到就移动到热数据区的链表头部
  • 热数据 -> 冷数据:能留在热数据区域的缓存页,证明都是缓存命中率比较高的,会经常被访问到。如果每个缓存页被访问都移动到链表头部,那这个操作将会非常的频繁。所以 InnoDB 存储引擎做了一个优化,只有在热数据区域的后 3/4 的缓存页被访问了,才会移动到链表头部;如果是热数据区域的前 1/4 的缓存页被访问到,它是不会被移动到链表头部去的。这样尽可能的减少链表中节点的移动了

4.2.6 小结

现在我们了解了更新数据会先把数据加载进buffer pool在进行,了解buffer pool是如何通过冷热数据分离的机制优化LRU链表,为系统设计中缓存过期淘汰策略提供的新的解决思路。既然,数据更新是把数据载入buffer pool中修改,那么更新完缓存页之后数据库是如何保证事务提交、如何保证数据页和缓存页数据一致的呢

4.3 undo log

说到事务就不得不提事务是如何回滚的,innodb是引入了undo log的日志组件来实现事务回滚的,以本文sql为例, 在数据加载进缓存页后,修改之前,会将执行的sql取反保存在undo log中,逻辑类似sql:

update users set name='lisi' where id = 10

当然如果是insert语句与之对应的就是delete语句,delete语句也就对应的insert 语句,这也就明白为什么delete的数据是可以回滚,而truncate数据之后无法回滚的根本原因,在于truncate无法生成undo log。

上图是本问sql执行的大致步骤,至于加入buffer pool这块上文已经详细了解过了,就不在赘述。从图中可以看出因为log直接刷盘比较损耗性能,所以引入log buffer进行缓存,然后在通过异步的方式把数据刷入磁盘既然数据更新之前的数据记录下来并成功刷入磁盘,则事务的回滚就不难实现了。

当然undo log 除了提供回滚功能,还为多版本并发控制(MVCC)提供了实现基础,实现了MYSQL的非阻塞读写,提高了系统的并发性。本文也不再深入

4.4 redo log

下面来了解一下innodb是如何保证buffer pool缓存的数据一致性问题,数据更新值内存后并不会立即刷新至磁盘数据页,而是一致以脏页的形式保存在buffer pool当中,这样做有两个原因会导致效率很差,一个是内存向磁盘写数据本身效率就慢,另一个就是随机IO会写磁盘的时间上附加上很多磁头寻址的时间,所以立即刷数据页效率很低。

Innodb是如何规避上述问题的呢,正常情况下,异步刷盘就已经可以解决了刷磁盘慢的问题,但是,假如MYSQL系统崩溃、宕机,这时候脏页还未及时刷盘,那么缓存页期间所有改动数据岂不是丢了,所以,Innodb引入了另一个组件redo log,专门记录数据被缓存期间做过的修改记录,然后立即写入redo log磁盘文件,相比于缓存页刷盘,redo log刷盘的数据了小多了,并且写redo log是顺序IO,而缓存页刷盘是随机IO。

下图示意:

这样当数据库异常宕机时,即使缓存页丢失数据也不会丢失,因为redo log已经落盘,数据库重启的时候会更近redo log把磁盘上历史的数据页重新载入内存,重新按redo log的修改记录操作一遍就能将缓存页中的数据恢复至宕机前的状态。

如果系统宕机时,redo log还没落盘数据岂不是丢了,对,这种情况下数据会丢,这种redo log丢数据分两中情况:

第一种情况,MYSQL有三种刷盘策略,通过innodb_flush_log_at_trx_commit参数进行配置

  • 配置为0:事务提交的时候不会把redolog buffer里的数据立即刷入磁盘,此时如果宕机则会导致已提交的数据修改丢失;
  • 配置为1:则是事务提交的时候必须把redolog buffer里的数据刷入磁盘,以保证事务提交后操作数据日志不丢;
  • 配置为2:则表示只是把数据交给操作系统进行刷盘,操作系统刷没刷成功则不管,理论上操作系统刷盘是先要经过os cache内存缓存的,就是说数据会先在os chache里没有真正的落盘,这种模式下也可能导致数据丢失

这第一种情况如果产生丢数据,是真的丢失,所以,如果对数据库丢失数据零容忍,建议配置策略为1

第二种情况,就是未写commit标记日志的情况,即下图第9步丢失的情况,但是这种情况系统认为事务提交失败,所以丢失了并不影响数据一致性。

图中7、8、9三个步骤是事务提交commit的时候才做的(本文只用一个sql来讲解,默认事务自动提交),redo log记录更新记录之后,执行器会把修改记录写在server层的binlog当中,很明显这是两个文件,如果出现上述宕机等异常情况,这两个文件的数据一致性是不能保证的,所以,为了保证两个文件的数据一致性,innodb会在binlog写完之后在redo log中补上一个commit标记告诉redo log事务成功。事务执行成功后操作redo log刷入磁盘,至此本文sql执行成功。

5 总结

通过一条update的sql的更新流程,清晰的看到MYSQL的整体架构设计,对Innodb存储引擎的几大核心组件如何相互协作、配合以实现高效的数据库系统有了更清晰的认识;核心组件buffer pool的冷热数据分离的缓存淘汰机制也为以后系统的架构设计提供了新的解决思路。

到此这篇关于一条sql详解MYSQL的架构设计详情的文章就介绍到这了,更多相关MYSQL架构设计内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL20个高性能架构设计原则(值得收藏)

    开源数据库架构设计原则 01. 技术选型 选择成熟的平台和技术,同时是最熟悉的,能做到极致的,用好不用坏,用熟不用生.目前业界的MySQL主流分支版本有Oracle官方版本的MySQL.Percona Server.MariaDB. 02. 高可用选择 高可用解决方案探讨的本质上是低宕机时间解决方案,可以理解成高可用的反面是不可用,绝大部分情况下数据库宕机才会导致数据库不可用.随着技术发展,开源数据库方面很多高可用组件(主从复制.半同步.MGR.MHA.Galera Cluster),对应场景,

  • MySQL 学习总结 之 初步了解 InnoDB 存储引擎的架构设计

    一.存储引擎 上节我们最后说到,SQL 的执行计划是执行器组件调用存储引擎的接口来完成的. 那我们可以理解为:MySQL 这个数据库管理系统是依靠存储引擎与存放数据的磁盘文件进行交互的. 那么 MySQL 有哪些存储引擎呢? 主要有 MyISAM.InnoDB.Memory等等.而现在互联网中,基本都是使用 InnoDB 存储引擎,所以接下来我将简单总结自己关于 InnoDB 存储引擎的学习,比较简单的介绍 InnoDB 存储引擎里面的组件. 二.缓冲池 我们现在都知道了,数据库的数据是存放在磁

  • MySQL架构设计思想详解

    目录 前言 1. MySQL整体架构 2. 连接器 3. 查询缓存 4. 分析器 5. 优化器 6. 执行器 7. 总结 前言 很多开发同学对SQL优化如数家珍,却对MySQL架构一知半解.岂不是只见树叶,不见森林,终将陷入细节中不能自拔. 今天就一块学习MySQL分层架构,深入了解MySQL底层实现原理,以及每层的作用,我们常见的SQL优化到底在哪一层做了优化? 1. MySQL整体架构 由图中可以看到MySQL架构主要分为Server层和存储引擎层. Server层又分为连接器.缓存.分析器

  • MySQL通透详解架构设计

    系统只要能从数据库连接池获取到一个数据库连接,就能执行CRUD.可通过数据库连接将待执行SQL发给MySQL. 大部分 crud boy只知道: 执行insert语句后,在表里会多条数据 执行update后,会更改表数据 执行delete后,会删除表里数据 执行select后,会查询表里数据出来 要是SQL性能丢人,建几个索引解决 - 这应该是目前行业内很多工程师对数据库的一个认知,完全当他是个黑盒来建表及执行SQL. 网络连接必须有线程处理 假设数据库服务器的连接池中的某个连接,接收到一条SQ

  • 一条sql详解MYSQL的架构设计详情

    目录 1 前言 2 应用层 2.1 连接线程处理 3 服务层 3.1 SQL 接口 3.2 SQL解析器 3.3 SQL优化器 3.4 执行器 3.5 查询缓存 4 存储引擎层 4.1 概述 4.2 缓冲池(buffer pool) 4.2.1 数据页.缓存页和脏页 4.2.2 元数据 4.2.3 free链表 4.2.4 flush链表 4.2.5 LRU链表 4.2.6 小结 4.3 undo log 4.4 redo log 5 总结 1 前言 对于一个服务端开发来说 MYSQL 可能是他

  • 详解一条sql语句在mysql中是如何执行的

    概览 最近开始在学习mysql相关知识,自己根据学到的知识点,根据自己的理解整理分享出来,本篇文章会分析下一个sql语句在mysql中的执行流程,包括sql的查询在mysql内部会怎么流转,sql语句的更新是怎么完成的. 一.mysql架构分析 下面是mysql的一个简要架构图: mysql主要分为Server层和存储引擎层 Server层:主要包括连接器.查询缓存.分析器.优化器.执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程.触发器.视图,函数等,还有一个通用的日志模块 bing

  • 详解MySQL客户端/服务器运行架构

    目录 前言 MySQL客户端/服务器架构 客户端与服务器连接的过程 TCP/IP 命名管道和共享内存 Unix域套接字文件 小结 前言 之前对MySQL的认知只限于会写些SQL,本篇开始进行对MySQL进行深入的学习,记录和整理下自己对MySQL不熟悉的地方.如果有需要可以关注我的专栏一起学习,共同进步!关于mysql的安装就不说了,直接从mysql的客户端/服务器运行架构开始学习.Let′s go~ MySQL客户端/服务器架构 MySQL其实跟我们平时使用的程序一样,是由两部分组成的,一部分

  • mysql查找删除重复数据并只保留一条实例详解

    有这样一张表,表数据及结果如下: school_id school_name total_student test_takers 1239 Abraham Lincoln High School 55 50 1240 Abraham Lincoln High School 70 35 1241 Acalanes High School 120 89 1242 Academy Of The Canyons 30 30 1243 Agoura High School 89 40 1244 Agour

  • 详解MySQL中的缓冲池(buffer pool)

    Mysql 中数据是要落盘的,这点大家都知道.读写磁盘速度是很慢的,尤其和内存比起来更是没的说.但是,我们平时在执行 SQL 时,无论写操作还是读操作都能很快得到结果,并没有预想中的那么慢. 可能你会说我有索引啊,有索引当然快了.但是铁子,索引文件也是存储在磁盘上的,查找过程会产生磁盘 I/O.如果同时对某行数据进行多次操作,那岂不是要重复产生很多次磁盘 IO 吗? 可能你想到了,那我把数据存在内存里不就可以了吗?内存速度比磁盘快,这准没毛病.没错,那该怎么存呢? 这就是我们今天所要讲的主题--

  • 详解MySQL的半同步

    前言 年后在进行腾讯二面的时候,写完算法的后问的第一个问题就是,MySQL的半同步是什么?我当时直接懵了,我以为是问的MySQL的两阶段提交的问题呢?结果确认了一下后不是两阶段提交,然后面试官看我连问的是啥都不知道,就直接跳过这个问题,直接聊下一个问题了.所以这次总结一下这部分的知识内容,文字内容比较多,可能会有些枯燥,但对于这方面感兴趣的人来说还是比较有意思的. MySQL的主从复制 我们的一般在大规模的项目上,都是使用MySQL的复制功能来创建MySQL的主从集群的.主要是可以通过为服务器配

  • 详解MySQL主从复制及读写分离

    前言 在企业实际应用中,成熟的业务通常数据量都比较大,而单台MySQL服务器在安全性.高可用性和高并发方面都无法满足实际的需求,我们可以在多台MySQL服务器(Master-Slave)部署 主从复制来实现同步数据,再通过 读写分离来提升数据库的并发负载能力.有点类似于rsync,但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据.语句做备份. 一.相关概述 主从复制:主数据库(Master)发送更新事件到从数据库(Slave),从数据库读取更新记录,并执行更新记录

  • 详解Mysql数据库平滑扩容解决高并发和大数据量问题

    目录 1 停机方案 2 停写方案 3 平滑扩容之双写方案(中小型数据) 4 平滑扩容之2N方案大数据量问题解决 4.1 扩容问题 4.2 解决方案 4.3 双主架构思想 4.4 环境部署 5 数据库秒级平滑2N扩容实践 5.1 新增数据库VIP 5.2 应用服务增加动态数据源 5.3 解除原双主同步 5.4 安装MariaDB扩容服务器 5.5 增加KeepAlived服务实现高可用 5.6 清理数据并验证 1 停机方案 发布公告 停止服务 离线数据迁移(拆分,重新分配数据) 数据校验 更改配置

  • 详解mysql中的冗余和重复索引

    mysql允许在相同列上创建多个索引,无论是有意还是无意,mysql需要单独维护重复的索引,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能. 重复索引是指的在相同的列上按照相同的顺序创建的相同类型的索引,应该避免这样创建重复索引,发现以后也应该立即删除.但,在相同的列上创建不同类型的索引来满足不同的查询需求是可以的. CREATE TABLE test( ID INT NOT NULL PRIMARY KEY, A INT NOT NULL, B INT NOT NULL, UNI

  • 详解MySQL导出指定表中的数据的实例

    详解MySQL导出指定表中的数据 要求: 1. 不导出创表的语句,因为表已经建好:默认会导出,先drop table然后create table: 2. 导出的insert语句加上ignore,允许重复执行:默认不会加上ignore: 3. insert语句中列出表中的字段,看得更清楚:默认不会: 4. 分记录生成多条insert语句,修改起来比较容易:默认是一条: 最终结果如下: mysqldump -pxxxxxx qzt qf1_mail_account --no-create-info

随机推荐