MySQL存储引擎

什么是存储引擎?

数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以获得特定的功能。

现在许多数据库管理系统都支持多种不同的存储引擎。MySQL 的核心就是存储引擎。

因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(Table Type,即存储和操作此表的类型)。

mysql存储引擎

MySQL给开发者提供了查询存储引擎的功能,执行以下sql即可查询到mysql中的存储引擎

SHOW ENGINES

我的mysql版本是5.7.28,下面是在Navicat中执行的结果


MyISAM存储引擎

在5.5版本之前,MyISAM是MySQL的默认存储引擎,由MYD和MYI组成。该存储引擎并发性差,不支持事务,所以使用场景比较少。

特性:

  • 不支持事务;
  • 不支持外键,如果强行增加外键,不会提示错误,只是外键不其作用;
  • 对数据的查询缓存只会缓存索引,不会像InnoDB一样缓存数据,而且是利用操作系统本身的缓存;
  • 默认的锁粒度为表级锁,所以并发度很差,加锁快,锁冲突较少,所以不太容易发生死锁;
  • 支持全文索引(MySQL5.6之后,InnoDB存储引擎也对全文索引做了支持),但是MySQL的全文索引基本不会使用,对于全文索引,现在有其他成熟的解决方案,比如:ElasticSearch,Solr,Sphinx等。
  • 数据库所在主机如果宕机,MyISAM的数据文件容易损坏,而且难恢复;
  • 支持数据压缩,命令:进入到mysql的bin文件夹, .\myisampack.exe -b -f "需要压缩的test.MYI地址" (此命令实在Windows运行)。
  • 运行完以后,会出现一个以OLD结尾的文件,删除OLD可能会出现问题。需要恢复 check table tablename,repair table tablename

限制:

  • 版本 < MySQL5.0时默认表大小为4G,如存储大,则需要修改MAX_Rows和AVG_ROW_LENGTH
  • 版本 > MySQL5.0时默认支持为256TB

适用场景:

  • 非事务型应用
  • 只读类应用,读取数据的速度快
  • 空间类应用(坐标,空间函数)

InnoDB存储引擎

从MySQL5.5版本之后,MySQL的默认内置存储引擎已经是InnoDB了
InnoDB是事务型数据库的首选引擎,通过上图也看到了,InnoDB是目前MYSQL的默认事务型引擎,是目前最重要、使用最广泛的存储引擎。支持事务安全表(ACID),支持行锁定和外键。InnoDB主要特性有:

  • InnoDB是一种事务性存储引擎
  • 完全支持事物的ACID特性,默认的事务隔离级别为可重复度,通过MVCC(并发版本控制)来实现的。
  • 使用的锁粒度为行级锁,可以支持更高的并发
  • Redo Log和Undo Log
  • 使用的锁粒度为行级锁,可以支持更高的并发
  • 行级锁是由存储引擎层实现的
  • 支持外键
  • 配合一些热备工具可以支持在线热备份
  • 在InnoDB中存在着缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度
  • 对于InnoDB类型的表,其数据的物理组织形式是聚簇表。所有的数据按照主键来组织。数据和索引放在一块,都位于B+数的叶子节点上

InnoDB使用表空间进行数据存储,储存在innodb_file_per_table表空间中,参数:on:表示独立表空间,OFF:表示系统表空间。5.6之前是系统表空间,之后为独立表空间。

独立表空间:.frm .ibd 存储数据+索引。 可以通过 optimize table 表名 .ibd收缩数据文件,同时可以向多个文件刷新数据。
系统表空间:.frm是放在数据库的文件下的。 .ibdata1是放在data文件夹下的,表公用的,会产生IO的瓶颈。 系统表空间无法简单的收缩文件大小

建议对InnoDB使用独立表空间

把原来存在于系统表空间中的表转移到独立表空间中的方法

  • 1、使用mysqldump导出所有数据库表数据
  • 2、停止mysql服务器,修改参数,并删除InnoDB相关文件
  • 3、重启mysql服务,重建InnoDB系统表空间
  • 4、重新导入数据

什么是锁

  • 锁的主要作用是管理共享资源的并发访问
  • 锁用于实现事物的隔离性
锁的类型
  • 共享锁(也称读锁)
  • 排它锁((也称写锁)

InnoDB 多版本控制

为保证并发操作和回滚操作, InnoDB会将修改前的数据存放在回滚段(undo log)中。
InnoDB会在数据库的每一行上额外增加三个字段以实现多版本控制:

  • 第一个字段是DB_TRX_ID用来存放针对该行最后一次执行insert、 update操作的事务ID,而delete操作也会被认为是update,只是会有额外的一位来代表事务为删除操作;
  • 第二个字段是DB_ROLL_PTR指针指向回滚段里对应的undo日志记录;
  • 第三个字段是DB_ROW_ID代表每一行的行ID。

回滚段中的undo日志记录只有在事务commit提交之后才会被丢弃,为避免回滚段越来越大,要注意及时执行commit命令

初始数据行的情况,六个字段的值分别是1,2,3,4,5,6



数据在数据库存储,不只是只有数据存储还有其他辅助信息存储例如(隐含id,事务id,回滚指针)等等信息

事务1修改该数据行,将六个字段的值分别*10,并生成回滚日志记录
事务2读取该数据行



undo log 会保存修改前数据的哪一行的状态 ,执行roallback 会找到回滚指针 进行回到以前的数据

事务2按照自己的事务ID和行数据中的事务ID做对比,并按照事务隔离级别选取事务1修改前的回滚段中的数据返回

InnoDB 体系结构


特点:

  • 根据主键寻址速度很快
  • 主键值递增的insert插入效率较好
  • 主键值随机insert插入操作效率差

InnoDB 存储引擎体系架构

缓存池
buffer pool缓存池是InnoDB在内存中开辟的用来缓存表数据和索引数据的区域, 一般可以设置为50%~80%的物理内存大小, 通过对经常访问的数据放置到内存当中来加快访问速度。

Buffer pool以page页的格式组成,页之间组成list列表,并通过LRU算法(最近最少使用算法) 对长久不使用的页进行置换。
数据的读写需要经过缓存(缓存在buffer pool 即在内存中)数据以整页(16K)位单位读取到缓存中缓存中的数据以LRU策略换出(最少使用策略)IO效率高,性能好


Adaptive Hash Index(自适应哈希索引):

Adaptive Hash index属性使得InnoDB更像是内存数据库。该属性通过innodb_adapitve_hash_index开启,也可以通过—skip-innodb_adaptive_hash_index参数关闭
InnoDB存储引擎会监控对表上索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应(adaptive) 的。自适应哈希索引通过缓冲池的B+树构造而来,因此建立的速度很快。而且不需要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式 来为某些页建立哈希索引。

哈希(hash)是一种非常快的等值查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。
而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般3-4层,故需要3-4次的查询。
innodb会监控对表上个索引页的查询。如果观察到建立哈希索引可以带来速度提升,则自动建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。
AHI有一个要求,就是对这个页的连续访问模式必须是一样的。
例如对于(a,b)访问模式情况:
where a = xxx
where a = xxx and b = xxx
AHI启动后,读写速度提高了2倍,辅助索引的连接操作性能可以提高5倍。
AHI,是数据库自动优化的,尽量使用符合AHI条件的查询,以提高效率。

Redo log buffer

Redo log buffer是一块用来存放写入redo log文件内容的内存区域,内存的大小由innodb_log_buffer_size参数确定。该buffer的内容会定期刷新到磁盘的redo log文件中。
参数innodb_flush_log_at_trx_commit决定了刷新到文件的方式,参数innodb_flush_log_at_timeout参数决定了刷新的频率。

Doublewrite缓存

Doublewrite缓存是位于系统表空间的存储区域,用来缓存InnoDB的数据页从innodb buffer pool中flush之后并写入到数据文件之前,所以当操作系统或者数据库进程在数据页写磁盘的过程中崩溃,Innodb可以在doublewrite缓存中找到数据页的备份而用来执行crash恢复。
数据页写入到doublewrite缓存的动作所需要的IO消耗要小于写入到数据文件的消耗,因为此写入操作会以一次大的连续块的方式写入。

在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write

doublewrite组成
内存中的doublewrite buffer,大小2M,
物理磁盘上共享表空间中连续的128个页,即2个区(extend),大小同样为2M。

对缓冲池的脏页进行刷新时,不是直接写磁盘,而是会通过memcpy()函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite 再分两次,每次1M顺序地写入共享表空间的物理磁盘上,在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer 中的页写入各个 表空间文件中,此时的写入则是离散的。如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,innodb可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。

Undo 日志

Undo日志是由一系列事务的undo日志记录组成,每一条undo日志记录包含了事务数据回滚的相关原始信息,所以当其它的事务需要查看修改前的原始数据,则会从此undo日志记录中获取。Undo日志存放在回滚段中的undo日志段中。默认情况下回滚段是作为系统表空间的一部分,但也可以有自己独立的undo表空间,通过设置
innodb_undo_tablespaces和innodb_undo_directory两个参数。
Innodb支持最大128个回滚段,其中的32个用来服务临时表的相关事务操作,剩下的96个服务非临时表,每个回滚段可以同时支持1023个数据修改事务,也就是总共96K个数据修改事务。
Innodb_undo_logs参数用来设置回滚段的个数。

Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方 (这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了 ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态

File-per-table表空间

File-per-table表空间意味着innodb的数据表不是共享一个系统表空间,而是每个表一个独立的表空间。可以通过设置innodb_file_per_table开启此属性。开启之后每个表数据和索引数据都会默认单独存放在数据文件夹下的.ibd数据文件中。

mysql> show variables like '%per_table%';
+-----------------------+-------+
| Variable_name        | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+

temporary表空间

temporary临时表空间用来存放临时表,默认情况下是在数据文件夹下的ibtmp1数据文件,此数据文件被设置为每次自动增长12MB大小,当然也可以设置innodb_temp_data_file_path来指定临时表空间文件的存放位置。
临时表空间文件在正常的shutdown之后会自动清除,但在crash发生时不会清除,这就需要手动去删除表空间文件或重启服务器。

mysql> show variables like '%innodb_temp%';
+----------------------------+-----------------------+
| Variable_name | Value |
+----------------------------+-----------------------+
| innodb_temp_data_file_path | ibtmp1:12M:autoextend |

如果发现临时表空间数据文件比较大,可以考虑重启MySQL来释放空间大小。

redo log

redo日志是存在于磁盘上的文件,包括ib_logfile0和ib_logfile1两个文件,常用于在crash恢复发生时将还没来得及写入到数据文件中但已经完成提交的事务在数据库初始化时重新执行一遍
InnoDB对redo log buffer写入到redo log文件的方式提供了组提交(group commit)的方式,意味着针对一次写磁盘操作可以包含多个事务数据,用此方法提高性能。
为了IO效率,数据库修改的文件都在内存缓存中完成的;那么我们知道一旦断电,内存中的数据将消失, 而数据库是如何保证数据的完整性? 那就是数据持久化与事务日志

如果宕机了则:应用已经持久化好了的日志文件,读取日志文件中没有被持久化到数据文件里面的记录;将这些记录重新持久化到我们的数据文件中


innodb日志持久化相关参数:innodb_flush_log_at_trx_commit

  • 0:每秒写入并持久化一次(不安全,性能高,无论mysql或服务器宕机,都会丢数据最多1秒的数据)
  • 1:每次commit都持久化(安全,性能低, IO负担重)
  • 2:每次commit都写入内存的内存缓存,每秒再刷新到磁盘(安全,性能折中, mysql宕机数据不会丢失,服务器宕机数据会丢失最多1秒的数据)

innodb_flush_log_at_timeout参数决定最多丢失多少秒的数据,默认是1秒

适用场景:

大多数的OLTP应用。

CSV存储引擎

特点:

  • 数据以文本方式存储,表的字段不能为空,不能有主键。
  • .csv文件存储表内容
  • .csm文件存储表的元数据如表状态和数据量
  • .frm文件存储表结构信息
  • 以CSV格式进行数据存储
  • 所有列的字段都不能为null
  • 不支持索引,不适合大表,不适合在线处理
  • 可以对数据文件在线编辑

适用场景

  • 适合作为数据交换的中间表



Archive存储引擎

Archive存储引擎适合的场景有限,由于其支持压缩,故主要是用来做日志,流水等数据的归档,主要特点:

  • 以zlib对表数据进行压缩,磁盘I/O更少
  • 数据存储在.ARZ为后缀的文件中
  • 仅支持select和insert操作,存入的数据就只能查询,不能做修改和删除
  • 只支持自增键上的索引,不支持其他索引

适用场景

  • 日志和数据采集类应用

Memory存储引擎

也称为heap存储引擎,将数据存在内存中,和市场上的Redis,memcached等思想类似,为了提高数据的访问速度,主要特点:

  • 支持hash索引和BTree索引,等值查找使用hash索引,范围查找使用BTree索引,不正确的索引会对性能造成很大的影响
  • 支持的数据类型有限制,比如:不支持TEXT和BLOB类型,对于字符串类型的数据,只支持固定长度的行,VARCHAR会被自动存储为CHAR类型;
  • 支持的锁粒度为表级锁。所以,在访问量比较大时,表级锁会成为MEMORY存储引擎的瓶颈,表的最大大小由max_heap_table_size参数决定,该参数默认值为16M
  • 由于数据是存放在内存中,所以在服务器重启之后,所有数据都会丢失;
  • 查询的时候,如果有用到临时表,而且临时表中有BLOB,TEXT类型的字段,那么这个临时表就会转化为MyISAM类型的表,性能会急剧降低;

临时表:

  • 系统使用临时表:超过限制使用Myisam临时表;未超过限制使用Memory表
  • create temporary table 建立的临时表可以使用任何的存储引擎

适用场景:

  • 用于查找或者是映射表,例如邮编和地区的对应表
  • 用于保存数据分析中产生的中间表
  • 用于缓存周期性聚合数据的结果表

注意:

但是Memory数据易丢失,所以要求数据可再生

Federated存储引擎

Federated存储引擎能让你访问远程的MySQL数据库而不使用replication或cluster技术(类似于Oracle的dblink),使用Federated存储引擎的表,本地只存储表的结构信息和远程服务器的连接信息,数据都存放在远程数据库上,查询时通过建表时指定的连接符去获取远程库的数据返回到本地。

Federated存储引擎默认不启用:

  • 如果是使用的源码,需要使用CMake 加上DWITH_FEDERATED_STORAGE_ENGINE选项。
  • 如果是二进制包,则在启动MySQL时指定 [--federated] 选项开启或在my.cnf文件中的[mysqld]部分加上federated参数

创建远程连接表
mysql配置远程连接必须在本地创建federated存储引擎的表,配置远程连接参数,本地创建的表必须和远程表的定义保持一致,这里我就拿本地另一个案例数据库来做测试,效果和远程是一样。

show create table sakila.actor;

创建远程存储引擎表

CREATE TABLE FEDERATED_actor (
   `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
   `first_name` varchar(45) NOT NULL,
   `last_name` varchar(45) NOT NULL,
   `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`actor_id`),
   KEY `idx_actor_last_name` (`last_name`)
 ) ENGINE =FEDERATED CONNECTION='mysql://root:[email protected]:3306/sakila/actor'; 

注意:本地创建的表名必须在远程服务器存在,创建的字段也必须是远程表中的字段,可以比远程表的字段少,但是不能多,本地存储引擎选择:ENGINE =FEDERATED,

CONNECTION选项中的连接字符串的一般形式如下:

scheme://user_name[:password]@host_name[:port_num]/db_name/tbl_name

还有一些连接字符串的例子:

CONNECTION='mysql://username:password@hostname:port/database/tablename'
CONNECTION='mysql://username@hostname/database/tablename'
CONNECTION='mysql://username:password@hostname/database/tablename'

注意:配置密码作为纯文本的话会存在安全问题,运行show create table,show table status是可以见的

特点

  • 提供了访问远程MySQL服务器上表的方法
  • 本地不存储数据,数据全部放到远程服务器上
  • 本地需要保存表结构和远程服务器的连接信息

适用场景:

  • 偶尔的统计分析及手工查询

如何选择合适的存储引擎

  • 使用场景是否需要事务支持
  • 是否需要支持高并发,InnoDB的并发度远高于MyISAM
  • 是否需要支持外键
  • 是否需要支持在线热备
  • 高效缓冲数据,InnoDB对数据和索引都做了缓冲,而MyISAM只缓冲了索引
  • 索引,不同存储引擎的索引并不太一样
  • 大部分情况下选择InnoDB存储引擎都是合适的

MySQL服务器参数介绍

MySQL获取配置信息路径:

  • 命令行参数
mysqld_safe --datadir=/data/sql_data
  • 配置文件
mysqld --help --verbose | grep -A 1 'Default options'
/etc/my.cnf、/etc/mysql/my.cnf、$mysql_home/my.cnf、~/.my.cnf

MySQL配置参数的作用域

  • 全局参数(需要重新登录才能生效)
    set global 参数名=参数值;
    set @@global.参数名:=参数值;
  • 会话参数
    set[session]参数名=参数值;
    set @@session.参数名 := 参数值;

内存配置相关参数

  • 确定可以使用的内存的上限

  • 确定MySQL的每个连接使用的内存(都是为线程分配的)

    • sort_buffer_size:排序缓冲区大小
    • join_buffer_size:连接缓冲区的大小
    • read_buffer_size:分配的值必须是4k的倍数
    • read_rnd_buffer_size:索引缓冲区的大小
  • 确认需要为操作系统保留多少内存

  • 如何为缓存池分配内存(Innodb严重依赖缓存池)
    InnoDB缓存池(InnoDB buffer pool)是提升InnoDB提升性能的关键,它既可以缓存数据,又可以缓存索引,甚至其他的管理数据(元数据、行级锁)等。可以使用show variables like 'innodb%pool%'; 来查看相关的参数选项。

    • Innodb_buffer_pool_size
      总内存-(每个线程所需要的内存*连接数)-系统保留内存
      在一个独立的只使用InnoDB引擎的MySQL服务器中,根据经验,推荐设置innodb-buffer-pool-size为服务器总可用内存的80%。

    • innodb_buffer_pool_instance
      innodb_buffer_pool_instance默认值是1,表示InnoDB缓存池被划分为一个区域,适当的增加该参数值,可以提升InnoDB的并发性能。

    • innodb_additional_mem_pool_size
      指定InnoDB用于来存储数据字典和其他内部数据的缓存大小,默认值是2M.InnoDB的表个数越多,就应该适当的增加该参数的大小。

    • key_buffer_size
      select sum(index_length) from information_schema.tables where engine='misaim'
      key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度。通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。比例key_reads /key_read_requests应该尽可能的低,至少是1:100,1:1000更好(上述状态值可以使用SHOW STATUS LIKE ‘key_read%'获得)。
      key_buffer_size只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。可以使用检查状态值created_tmp_disk_tables得知详情。
      对于1G内存的机器,如果不使用MyISAM表,推荐值是16M(8-64M)

I/O相关配置参数

Innodb I/O 相关配置
  • Innodb_log_file_size:控制日志文件的大小。
  • Innodb_log_files_in_group:控制日志文件的个数。
  • 事务日志总大小:Innodb_log_file_size * Innodb_log_files_in_group
  • Innodb_log_buffer_size:控制日志缓冲区的大小
  • Innodb_flush_log_at_trx_commit:事物日志的刷新频率
    • 0:每秒进行一次log写入cache,并flush log到磁盘
    • 1[默认]:在每次事物提交执行log写入cache,并flush log到磁盘
    • 2[建议]:每次事物提交,执行log数据写入cache,每秒执行一次flush log到磁盘
  • Innodb_flush_method=O_DIRECT:Innodb刷新的方式,该参数决定了Innodb的数据文件和日志文件如何与文件系统进行交互
  • Innodb_file_pre_table=1:控制Innodb如何使用表空间,1表示Innodb会为所有的表建立单独的表空间,否则Innodb会把所有的表存储在系统表空间中
  • Innodb_doublewrite=1:控制Innodb是否使用双写缓存,主要作用是为了避免列没有写完整导致的数据损坏
myisam I/O 相关配置
  • delay_key_write:该参数控制关键字缓冲中的脏块什么时候可以刷新到磁盘文件中
    • OFF:每次写操作后刷新键缓冲中的脏块到磁盘
    • ON:只对在建表时指定了delay_key_write选项的表使用延迟刷新
    • ALL:对所有myisam表都使用延迟键写入

安全相关配置参数

  • expire_logs_days:指定自动清理binlog的天数
  • max_allowed_packet:控制MySQL可以接收的包的大小
  • skip_name_resolve:禁用DNS查找
  • sysdate_is_now:确保sysdate()返回确定性日期
  • read_only:禁止非super权限的用户写权限
  • skip_slave_start:禁用Slave的自动恢复
  • sql_mode:设置MySQL所使用的SQL模式
    • only_full_group_by:对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么将认为这个SQL是不合法的,因为列不在GROUP BY从句中
    • strict_trans_tables:在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做任何限制
    • no_zero_in_date:在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入'0000-00-00'。在非严格模式,可以接受该日期,但会生成警告。
    • no_zero_date:在严格模式,不要将 '0000-00-00'做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告
    • error_for_division_by_zero:在严格模式,在INSERT或UPDATE过程中,如果被零除(或MOD(X,0)),则产生错误(否则为警告)。如果未给出该模式,被零除时MySQL返回NULL。如果用到INSERT IGNORE或UPDATE IGNORE中,MySQL生成被零除警告,但操作结果为NULL。
    • no_auto_create_user:防止GRANT自动创建新用户,除非还指定了密码。
    • no_engine_substitution:如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常

其它配置参数

  • sync_binlog:控制MySQL如何向磁盘刷新binlog
  • tmp_table_size 和 max_heap_table_size:控制内存临时表大小
  • max_connections:控制允许的最大连接数

参考:
https://www.cnblogs.com/orange-time/p/10552801.html

https://segmentfault.com/a/1190000019400925

https://www.linuxidc.com/Linux/2019-01/156387.htm

https://www.cnblogs.com/chenmh/p/5045140.html

https://www.cnblogs.com/only-me/p/11538273.html

https://blog.csdn.net/xinyuan_java/article/details/97137075

http://c.biancheng.net/view/2418.html

你可能感兴趣的:(MySQL存储引擎)