一 MySQL架构与历史

一 MySQL架构与历史

    • 1.1 MySQL逻辑架构
      • 1.1.1 连接管理与安全性
      • 1.1.2 优化与执行
    • 1.2 并发控制
      • 1.2.1 读写锁
      • 1.2.2 锁粒度
    • 1.3 事务
      • 1.3.1 隔离级别
      • 1.3.2 死锁
      • 1.3.3 事务日志
      • 1.3.4 MySQL中的事务
    • 1.4 多版本并发控制MVCC
    • 1.5 MySQL的存储引擎
      • 1.5.1 InnoDB存储引擎
      • 1.5.2 MylSAM存储引擎
      • 1.5.3 MySQL内建的其他存储引擎
      • 1.5.4 第三方存储引擎
      • 1.5.5 选择合适的引擎
      • 1.5.6 转换表的引擎
    • 1.6 MySQL时间线(Timeline)
    • 1.7 MySQL的开发模式
    • 1.8 总结

  • MySQL最重要、最与众不同的特性是它的存储引擎架构,这种架构的设计将查询处理及其他系统任务和数据的存储/提取相分离。这种处理和存储分离的设计可以在使用时根据性能、特性,以及其他需求来选择数据存储的方式。

本章概要地描述了MySQL的服务器架构、各种存储引擎之间的主要区别,以及这些区别的重要性。也会回顾一下MySQL的历史背景和基准测试,并试图通过简化细节和演示案例来讨论MySQL的原理。

1.1 MySQL逻辑架构

下图所示为MySQL逻辑架构图

一 MySQL架构与历史_第1张图片

  1. 最上层的服务并不是MySQL独有的,大多数基于网络的客户端/服务器的工具或服务都有类似的架构。如连接处理、授权认证、安全等。
  2. 第二层架构。大多数MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现﹔存储过程、触发器、视图等。
  3. 第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。每个存储引擎都有它的优势和劣势,服务器通过API与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明。存储引擎API包含几十个底层函数,用于执行如“开始一个事务”或“根据主键提取一行记录”等操作。但存储引擎不会去解析SQL,不同存储引擎间也不会相互通信,而只是简单地响应上层服务器的请求。

1.1.1 连接管理与安全性

  • 每个客户端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在这个单独的线程中执行,该线程只能轮流在某个CPU核心或CPU中运行。服务器会负责缓存线程,因此不需要为每一个新建的连接创建或销毁线程。
  • 当客户端(应用)连接到 MySQL 服务器时,服务器需要对其进行认证。认证基于用户名、原始主机信息和密码。如果使用了安全套接字(SSL)的方式连接,还可以使用X.509证书认证。
  • 一旦客户端连接成功,服务器会继续验证该客户端是否具有执行某个特定查询的权限(例如,是否允许客户端对world数据库的Country表执行SELECT语句)。

1.1.2 优化与执行

  • MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等。用户可以通过特殊的关键字提示优化器,影响它的决策过程。也可以请求优化器解释优化过程的各个因素,使用户可以知道服务器是如何进行优化决策的,并提供一个参考基准,便于用户重构查询和schema、修改相关配置,使应用尽可能高效运行。
  • 优化器并不关心表使用的是什么存储引擎,但存储引擎对于优化查询是有影响的。优化器会请求存储引擎提供容量或某个具体操作的开销信息,以及表数据的统计信息等。例如,某些存储引擎的某种索引,可能对一些特定的查询有优化。
  • 对于SELECT语句,在解析查询之前,服务器会先检查查询缓存,如果能够在其中找到对应的查询,服务器就不必再执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。

1.2 并发控制

本章的目的是讨论MySQL在两个层面的并发控制﹔服务器层与存储引擎层。本章只简要地讨论MySQL 如何控制并发读写。

1.2.1 读写锁

  • 在处理并发读或写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。共享锁和排他锁,也叫读锁和写锁。
  • 锁的概念如下﹔读锁是共享的,或者说是相互不阻塞的。多个客户在同一时刻可以同时读取同一个资源,而互不干扰。写锁则是排他的,一个写锁会阻塞其他的写锁和读锁。

1.2.2 锁粒度

表锁

  • 表锁是MySQL中开销最小的策略。表锁会锁定整张表。一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获得读锁,读锁之间不相互阻塞。
  • 在特定的场景中,表锁也可能有良好的性能。例如,READ
    LOCAL表锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面。
  • 尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如,服务器会为诸如ALTER
    TABLE之类的语句使用表锁,而忽略存储引擎的锁机制。

行级锁

  • 行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。在 InnoDB 和 XtraDB,以及其他一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。

1.3 事务

  • 事务就是一组原子性的SQL查询,或者说一个独立的工作单元。事务内的语句,要么全部执行成功,要么全部执行失败。
  • 可以用 START TRANSACTION 语句开始一个事务,然后要么使用 COMMIT 提交事务将修改的数据持久保留,要么使用 ROLLBACK 撤销所有的修改。
  • ACID表示原子性、一致性、隔离性和持久性。一个运行良好的事务处理系统,必须具备这些标准特征。

原子性(atomicity): 一个事务必须被视为一个不可分割的最小工作单元。

一致性(consistency) :数据库总是从一个一致性的状态转换到另外一个一致性的状态。

隔离性(isolation) :通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。

持久性(durability) :一旦事务提交,则其所做的修改就会永久保存到数据库中。即使系统崩溃,修改的数据也不会丢失。

1.3.1 隔离级别

  • 较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

READ UNCOMMITTED(未提交读)

  • 在 READ UNCOMMITTED 级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED 不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

READ COMMITTED(提交读)

  • 大多数数据库系统的默认隔离级别都是READ COMMITTED(但
    MySQL不是)。一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。

REPEATABLE READ(可重复读)

  • REPEATABLE READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但理论上,可重复读隔离级别还是无法解决幻读的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom
    Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。可重复读是MySQL的默认事务隔离级别

SERIALIZABLE(可串行化)

  • SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

1.3.2 死锁

  • 死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
  • InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。

1.3.3 事务日志

  • 事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。
  • 事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序IO,而不像随机IO需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引擎都是这样实现的,通常称之为预写式日志,修改数据需要写两次磁盘。
  • 如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。具体的恢复方式则视存储引擎而定。

1.3.4 MySQL中的事务

MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。另外还有一些第三方存储引擎也支持事务,比较知名的包括XtraDB和PBXT。

自动提交(AUTOCOMMIT)

  • MySQL默认采用自动提交模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。
  • 在当前连接中,可以通过设置AUTOCOMMIT 变量来启用或者禁用自动提交模式。1或ON表示启用,0或OFF表示禁用。当AUTOCOMMIT=0 时,所有的查询都在一个事务中,直到显式地执行 COMMIT 提交或 ROLLBACK 回滚,该事务结束,同时又开始了另一个新事务。

在事务中混合使用存储引擎

  • MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,使用多种存储引擎是不可靠的。

隐式和显式锁定

  • InnoDB 采用的是两阶段锁定协议。在事务执行过程中,随时都可以执行锁定,锁只有在执行 COMMIT 或 ROLLBACK 时才会释放,并且所有的锁在同一时刻被释放。前面描述的锁定都是隐式锁定,InnoDB会根据隔离级别在需要的时候自动加锁。
  • 另外,InnoDB也支持通过特定的语句进行显式锁定,这些语句不属于SQL规范:
SELECT ... LOCK IN SHARE MODE
SELECT ... FOR UPDATE
  • MySQL也支持 LOCK TABLES 和 UNLOCK TABLES 语句,这是在服务器层实现的,和存储引擎无关。它们有自己的用途,但并不能替代事务处理。如果应用需要用到事务,还是应该选择事务型存储引擎。

1.4 多版本并发控制MVCC

  • MVCC是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
  • InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

下面看一下在 REPEATABLE READ 隔离级别下,MVCC具体是如何操作的。

  1. SELECT:InnoDB会根据以下两个条件检查每行记录:

a. InnoDB只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或修改过的
b.行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
只有符合上述两个条件的记录,才能返回作为查询结果。

  1. INSERT:InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。
  2. DELETE:InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
  3. UPDATE:InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

MVCC 只在 REPEATABLE READ 和 READ COMMITTED 两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE则会对所有读取的行都加锁。

1.5 MySQL的存储引擎

  • 在文件系统中,MySQL将每个数据库(也称之为schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件保存表的定义。
  • 因为MySQL使用文件系统的目录和文件来保存数据库和表的定义,大小写敏感性和具体的平台密切相关。不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在MySQL服务层统一处理的。

1.5.1 InnoDB存储引擎

  • InnoDB 是 MySQL的默认事务型引擎。它用来处理大量的短期事务,短期事务大部分情况是正常提交的,很少会被回滚。
  • InnoDB的数据存储在表空间中,表空间由一系列的数据文件组成。InnoDB可以将每个表的数据和索引存放在单独的文件中。InnoDB也可以使用裸设备作为表空间的存储介质,但现代的文件系统使得裸设备不再是必要的选择。
  • InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。

InnoDB表是基于聚簇索引建立的。InnoDB的索引结构和MySQL的其他存储引擎有很大的不同,聚簇索引对主键查询有很高的性能。不过它的二级索引(非主键索引)中必须包含主键列,所以如果主键列很大,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。InnoDB的存储格式是平台独立的,也就是说可以将数据和索引文件从Intel平台复制到PowerPC或者Sun SPARC平台。

  • InnoDB内部做了很多优化,包括从磁盘读取数据时采用的可预测性预读,能够自动在内存中创建hash索引以加速读操作的自适应哈希索引,以及能够加速插入操作的插入缓冲区等。

裸设备(raw device),也叫裸分区(原始分区),是一种没有经过格式化,不被Unix通过文件系统来读取的特殊块设备文件。由应用程序负责对它进行读写操作。不经过文件系统的缓冲。它是不被操作系统直接管理的设备。这种设备少了操作系统这一层,I/O效率更高。不少数据库都能通过使用裸设备作为存储介质来提高I/O效率。

1.5.2 MylSAM存储引擎

  • 在 MySQL 5.1 及之前的版本,MyISAM
    是默认的存储引擎。MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务和行级锁,而且有一个缺陷是崩溃后无法安全恢复。

存储

  • MyISAM会将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。MyISAM表可以包含动态或静态(长度固定)行。MySQL会根据表的定义来决定采用何种行格式。MyISAM表可以存储的行记录数,一般受限于可用的磁盘空间,或者操作系统中单个文件的最大尺寸。

MyISAM特性

  1. 加锁与并发:MyISAM对整张表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁,写入时则对表加排他锁。但是在表有读取查询的同时,也可以往表中插入新的记录(称为并发插入)。
  2. 修复:对于MyISAM表,MySQL可以手工或者自动执行检查和修复操作,执行表的修复可能导致一些数据丢失,而且修复操作是非常慢的。
  3. 索引特性:对于MyISAM表,即使是BLOB和TEXT等长字段,也可以基于其前500个字符创建索引。MyISAM也支持全文索引,这是一种基于分词创建的索引,可以支持复杂的查询。
  4. 廷迟更新索引键:创建MyISAM表时,如果指定了DELAY_KEY_WRITE选项,在每次修改执行完成时,不会立刻将修改的索引数据写入磁盘,而是写到内存中的键缓冲区 ,只有在清理健缓冲区或关闭表的时候才会将对应的索引块写入到磁盘。这种方式可以极大地提升写入性能,但是在数据库或主机崩溃时会造成索引损坏,需要执行修复操作。延迟更新索引键的特性,可以在全局设置,也可以为单个表设置。

MyISAM压缩表

  • 如果表在创建并导入数据以后,不会再进行修改操作,那么适合采用MyISAM压缩表。
  • 可以使用myisampack对MyISAM表进行压缩(也叫打包pack)。压缩表是不能进行修改的(除非先将表解除压缩,修改数据,然后再次压缩)。压缩表可以极大地减少磁盘空间占用,因此也可以减少磁盘I/O,从而提升查询性能。压缩表也支持索引,但索引也是只读的。
  • 以现在的硬件能力,对大多数应用场景,读取压缩表数据时的解压带来的开销影响并不大,而减少I/O带来的好处则要大得多。压缩时表中的记录是独立压缩的,所以读取单行的时候不需要去解压整个表(甚至也不解压行所在的整个页面)。

MylSAM性能:MyISAM引擎设计简单,数据以紧密格式存储,所以在某些场景下的性能很好。MyISAM有一些服务器级别的性能扩展限制,比如对索引键缓冲区的Mutex锁,MariaDB基于段的索引键缓冲区机制来避免该问题。但MyISAM最典型的性能问题还是表锁的问题,如果你发现所有的查询都长期处于“Locked”状态,那么毫无疑问表锁就是罪魁祸首。

1.5.3 MySQL内建的其他存储引擎

Archive引擎

  • Archive存储引擎只支持INSERT和SELECT操作。Archive引擎会缓存所有的写并利用zlib对插入的行进行压缩,所以比 MyISAM 表的磁盘I/O更少。但是每次SELECT查询都需要执行全表扫描。所以Archive表适合日志和数据采集类应用,这类应用做数据分析时往往需要全表扫描。或者在一些需要更快速的INSERT操作的场合下也可以使用。
  • Archive引擎支持行级锁和专用的缓冲区,所以可以实现高并发的插入。在一个查询开始直到返回表中存在的所有行数之前,Archive引擎会阻止其他的SELECT执行,以实现一致性读。另外,也实现了批量插入在完成之前对读操作是不可见的。这种机制模仿了事务和MVCC的一些特性,但Archive引擎不是一个事务型的引擎,而是一个针对高速插入和压缩做了优化的简单引擎。

Blackhole引擎

  • Blackhole引擎没有实现任何的存储机制,它会丢弃所有插入的数据,不做任何保存。但是服务器会记录Blackhole表的日志,所以可以用于复制数据到备库,或者只是简单地记录到日志。这种特殊的存储引擎可以在一些特殊的复制架构和日志审核时发挥作用。但这种应用方式我们碰到过很多问题,因此并不推荐。

CSV引擎

  • CSV引擎可以将普通的CSV文件(逗号分割值的文件)作为MySQL的表来处理,但这种表不支持索引。CSV引擎可以在数据库运行时拷入或拷出文件。可以将Excel等电子表格软件中的数据存储为CSV文件,然后复制到MySQL数据目录下,就能在MySQL中打开使用。同样,如果将数据写入到一个CSV引擎表,其他的外部程序也能立即从表的数据文件中读取CSV格式的数据。因此CSV引擎可以作为一种数据交换的机制,非常有用。

Federated引擎

  • Federated引擎是访问其他MySQL服务器的一个代理,它会创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,然后提取或者发送需要的数据。尽管该引擎看起来提供了一种很好的跨服务器的灵活性,但也经常带来问题,因此默认是禁用的。MariaDB使用了它的一个后续改进版本,叫做FederatedX。

Memory引擎

  • 如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那么使用Memory表是非常有用的。Memory表至少比 MyISAM表要快一个数量级,因为所有的数据都保存在内存中,不需要进行磁盘IO。Memory表的结构在重启以后还会保留,但数据会丢失。

Memroy表在很多场景可以发挥好的作用:

  1. 用于查找或者映射表,例如将邮编和州名映射的表。
  2. 用于缓存周期性聚合数据的结果。
  3. 用于保存数据分析中产生的中间数据。
  • Memory表支持Hash索引,因此查找操作非常快。虽然Memory表的速度非常快,但还是无法取代传统的基于磁盘的表。Memroy表是表级锁,因此并发写入的性能较低。它不支持BLOB或TEXT类型的列,并且每行的长度是固定的,所以即使指定了VARCHAR列,实际存储时也会转换成CHAR,这可能导致部分内存的浪费(其中一些限制在Percona版本已经解决)。

如果MySQL在执行查询的过程中需要使用临时表来保存中间结果,内部使用的临时表就是Memory表。如果中间结果太大超出了Memory表的限制,或者含有BLOB或TEXT字段,则临时表会转换成MyISAM表。

Merge引擎

  • Merge引擎是MyISAM引擎的一个变种。Merge表是由多个MyISAM表合并而来的虚拟表。如果将MySQL用于日志或者数据仓库类应用,该引擎可以发挥作用。但是引入分区功能后,该引擎已经被放弃。

NDB集群引擎

  • NDB集群存储引擎,作为SQL和NDB原生协议之间的接口。MySQL服务器、NDB集群存储引擎,以及分布式的、share-nothing的、容灾的、高可用的NDB数据库的组合,被称为MySQL集群(MySQL Cluster)。

1.5.4 第三方存储引擎

OLTP类引擎

  • Percona的XtraDB存储引擎是基于InnoDB引擎的一个改进版本,已经包含在PerconaServer和 MariaDB中,它的改进点主要集中在性能、可测量性和操作灵活性方面。XtraDB可以作为InnoDB的一个完全的替代产品,甚至可以兼容地读写InnoDB的数据文件,并支持InnoDB的所有查询。
  • 另外还有一些和InnoDB非常类似的OLTP类存储引擎,比如都支持ACID事务和MVCC。其中一个就是PBXT,它支持引擎级别的复制、外键约束,并且以一种比较复杂的架构对固态存储(SSD)提供了适当的支持,还对较大的值类型如BLOB也做了优化。PBXT是一款社区支持的存储引擎,MariaDB包含了该引擎。
  • TokuDB 引擎使用了分形树的索引数据结构。该结构是缓存无关的,因此即使其大小超过内存性能也不会下降,也就没有内存生命周期和碎片的问题。TokuDB是一种大数据存储引擎,因为其拥有很高的压缩比,可以在很大的数据量上创建大量索引。目前其最适合在需要大量插入数据的分析型数据集的场景中使用.
  • RethinkDB最初是为固态存储(SSD)而设计的。该引擎比较特别的地方在于采用了一种只能追加的写时复制B树作为索引的数据结构。

面向列的存储引擎

  • MySQL默认是面向行的,每一行的数据一起存储,服务器的查询也是以行为单位处理的。而在大数据量处理时,面向列的方式可能效率更高。如果不需要整行的数据,面向列的方式可以传输更少的数据。如果每一列都单独存储,那么压缩的效率也会更高。
  1. Infobright是最有名的面向列的存储引擎。在非常大的数据量时,该引擎工作良好。Infobright是为数据分析和数据仓库应用设计的。

数据高度压缩,按照块进行排序,每个块都对应有一组元数据。在处理查询时,访问元数据可决定跳过该块,甚至可能只需要元数据即可满足查询的需求。但该引擎不支持索引,不过在这么大的数据量级,即使有索引也很难发挥作用,而且块结构也是一种准索引。Infobright需要对MySQL服务器做定制,因为一些地方需要修改以适应面向列存储的需要。如果查询无法在存储层使用面向列的模式执行,则需要在服务器层转换成按行处理,这个过程会很慢。

  1. 另外一个面向列的存储引擎是InfiniDB。InfiniDB可以在一组机器集群间做分布式查询。

社区存储引擎

  • 在这里列举了一些,大都没有在生产环境中应用过,慎用,后果自负。
  • Aria:之前的名字是Maria,是计划用来替代MyISAM的一款引擎。MariaDB包含了该引擎,之前计划开发的很多特性,有些因为在MariaDB服务器层实现,所以引擎层就取消了。可以说Aria就是解决了崩溃安全恢复问题的MyISAM,当然也还有一些特性是MyISAM不具备的,比如数据的缓存(MyISAM只能缓存索引)。
  • Groonga:这是一款全文索引引擎,号称可以提供准确而高效的全文索引。
  • oQGraph:该引擎支持图操作(比如查找两点之间的最短路径),用SQL很难实现该类操作。
  • Q4M:该引擎在MySQL内部实现了队列操作,而用SQL很难在一个语句实现这类队列操作。
  • SphinxSE:该引擎为Sphinx 全文索引搜索服务器提供了SQL接口。
  • Spider:该引擎可以将数据切分成不同的分区,比较高效透明地实现了分片,并且可以针对分片执行并行查询(分片可以分布在不同的服务器上)。
  • VPForMySQL:该引擎支持垂直分区,通过一系列的代理存储引擎实现。垂直分区指的是可以将表分成不同列的组合,并且单独存储。但对查询来说,看到的还是一张表。

1.5.5 选择合适的引擎

  • 大部分情况下,InnoDB都是正确的选择。
  • 但例如,如果要用到全文索引,建议优先考虑InnoDB 加上 Sphinx 的组合,而不是使用支持全文索引的MyISAM。或者如果不在乎可扩展能力和并发能力,也不在乎崩溃后的数据丢失问题,却对InnoDB的空间占用过多比较敏感,这种场合下选择MyISAM就比较合适。

如果应用需要不同的存储引擎,请先考虑以下几个因素。

  1. 事务:如果应用需要事务支持,那么InnoDB(或XtraDB)是目前最稳定并且经过验证的选择。如果不需要事务,并且主要是SELECT和INSERT操作,那么MyISAM是不错的选择。一般日志型的应用比较符合这一特性。
  2. 备份:备份的需求也会影响存储引擎的选择。如果可以定期地关闭服务器来执行备份,那么备份的因素可以忽略。反之,如果需要在线热备份,那么选择InnoDB就是基本的要求。
  3. 崩溃恢复:数据量比较大的时候,系统崩溃后如何快速地恢复是一个需要考虑的问题。相对而言,MyISAM崩溃后发生损坏的概率比InnoDB要高很多,而且恢复速度也要慢。因此,即使不需要事务支持,很多人也选择InnoDB引擎,这是一个非常重要的因素。
  4. 特有的特性:最后,有些应用可能依赖一些存储引擎独有的特性或者优化,比如很多应用依赖聚簇索引的优化。另外,MySQL中也只有MyISAM支持地理空间搜索。如果一个存储引擎拥有一些关键的特性,同时却又缺乏一些必要的特性,那么有时候不得不做折中的考虑,或者在架构设计上做一些取舍。某些存储引擎无法直接支持的特性,有时候通过变通也可以满足需求。

接下来会讨论一些常见的应用场景,在这些场景中会涉及很多的表,以及这些表如何选用合适的存储引擎。

日志型应用

假设你需要实时地记录一台中心电话交换机的每一通电话的日志到MySQL中,或者通过Apache的mod_log_sql模块将网站的所有访问信息直接记录到表中。

  • 这一类应用的插人速度有很高的要求,数据库不能成为瓶颈。MyISAM或者Archive存储引擎对这类应用比较合适,因为它们开销低,而且插入速度非常快。

如果需要对记录的日志做分析报表,生成报表的SQL很有可能会导致插入效率明显降低,该怎么办?

  1. 利用MySQL内置的复制方案将数据复制一份到备库,然后在备库上执行比较消耗时间和CPU的查询。这样主库只用于高效的插入工作,而备库上执行的查询也无须担心影响到日志的插人性能。当然也可以在系统负载较低的时候执行报表查询操作,但应用在不断变化,如果依赖这个策略可能以后会导致问题。
  2. 在日志记录表的名字中包含年和月的信息,比如 web_logs_2012_01或者web_logs_2012_jan。这样可以在已经没有插入操作的历史表上做频繁的查询操作,而不会干扰到最新的当前表上的插入操作。

只读或者大部分情况下只读的表

有些表的数据用于编制类目或分列清单(如工作岗位、竟拍、不动产等),这种应用场景是典型的读多写少的业务。

  • 如果不介意MyISAM的崩溃恢复问题,选用MyISAM引擎是合适的。不过不要低估崩溃恢复问题的重要性,有些存储引擎不会保证将数据安全地写入到磁盘中,而许多用户实际上并不清楚这样有多大的风险(MyISAM只将数据写到内存中,然后等待操作系统定期将数据刷出到磁盘上)。
  • 当设计上述类型的应用时,建议采用InnoDB。MyISAM引擎在一开始可能没有任何问题,但随着应用压力的上升,则可能迅速恶化。各种锁争用、崩溃后的数据丢失等问题都会随之而来。

订单处理

  • 如果涉及订单处理,那么支持事务就是必要选项。另外一个重要的考虑点是存储引擎对外键的支持情况。InnoDB是订单处理类应用的最佳选择。

电子公告牌和主题讨论论坛

  • 主题讨论区一般都有更新计数器﹐并且会为各个主题计算访问统计信息。多数应用只设计了几张表来保存所有的数据,所以核心表的读写压力可能非常大。为保证这些核心表的数据一致性,锁成为资源争用的主要因素。

尽管有这些设计缺陷,但大多数应用在中低负载时可以工作得很好。如果Web站点的规模迅速扩展,流量随之猛增,则数据库访问可能变得非常慢。此时一个典型的解决方案是更改为支持更高读写的存储引擎,但有时用户会发现这么做反而导致系统变得更慢了。用户可能没有意识到这是由于某些特殊查询的缘故,典型的如:mysq1>SELECT COUNT(*) FROM table;
问题就在于,不是所有的存储引擎运行上述查询都非常快:对于MyISAM确实会很快,但其他的可能都不行。

CD-ROM应用

  • 如果要发布一个基于CD-ROM或者DVD-ROM并且使用MySQL数据文件的应用,可以考虑使用MyISAM表或MyISAM压缩表,这样表之间可以隔离并且可以在不同介质上相互拷贝。MyISAM压缩表比未压缩的表要节约很多空间,但压缩表是只读的。

大数据量

  • 我们创建或管理的很多InnoDB数据库的数据量在3-5TB之间,或者更大,这是单台机器上的量,不是一个分片的量。这些系统运行得还不错,要做到这一点需要合理地选择硬件,做好物理设计,并为服务器的I/O瓶颈做好规划。在这样的数据量下,如果采用MyISAM,崩溃后的恢复就是一个噩梦。
  • 如果数据量继续增长到10TB以上的级别,可能就需要建立数据仓库。Infobright是MySQL数据仓库最成功的解决方案。也有一些大数据库不适合Infobright,却可能适合TokuDB。

1.5.6 转换表的引擎

ALTER TABLE

  • 将表从一个引擎修改为另一个引擎最简单的办法是使用 ALTER
    TABLE语句。下面的语句将mytable的引擎修改为InnoDB
mysql> ALTER TABLE mytable ENGINE = InnoDB;
  • 上述语法适用任何存储引擎,但有一个问题﹔需要执行很长时间。MySQL会按行将数据从原表复制到一张新的表中,在复制期间可能会消耗系统所有的I/O能力,同时原表上会加上读锁。所以,在繁忙的表上执行此操作要特别小心。
  • 如果转换表的存储引擎,将会失去和原引擎相关的所有特性。例如,如果将一张InnoDB表转换为MyISAM,然后再转换回InnoDB,原InnoDB表上所有的外键将丢失。

导出与导入

  • 为了更好地控制转换的过程,可以使用mysqldump工具将数据导出到文件,然后修改文件中CREATE TABLE语句的存储引擎选项,注意同时修改表名,因为同一个数据库中不能存在相同的表名,即使它们使用的是不同的存储引擎。同时要注意mysqldump默认会自动在CREATE TABLE语句前加上 DROP TABLE语句,不注意这一点可能会导致数据丢失。

创建与查询(CREATE和SELECT)

  • 第三种转换的技术综合了第一种方法的高效和第二种方法的安全。不需要导出整个表的数据,而是先创建一个新的存储引擎的表,然后利用INSERT…SELECT语法来导数据:
mysql> CREATE TABLE innodb_table LIKE myisam_table;
mysql> ALTER TABLE innodb_table ENGINE=InnoDB;
mysql>INSERT INTO innodb_table SELECT* FROM myisam_table;
  • 数据量不大的话,这样做很好。如果数据量很大,则可以考虑做分批处理,针对每一段数据执行事务提交操作,以避免大事务产生过多的undo。假设有主键字段id,重复运行以下语句(最小值x和最大值y进行相应的替换)将数据导入到新表:
mysql>START TRANSACTION;
mysql>INSERT INTO innodb_table SELECT * FROM myisam_table WHERE id BETWEEN x AND y;
mysql> COMMIT;
  • 这样操作完成以后,新表是原表的一个全量复制,原表还在,如果需要可以删除原表。如果有必要,可以在执行的过程中对原表加锁,以确保新表和原表的数据一致。
  • Percona Toolkit提供了一个pt-online-schema-change的工具(基于Facebook的在线schema变更技术),可以比较简单、方便地执行上述过程,避免手工操作可能导致的失误和烦琐。

1.6 MySQL时间线(Timeline)

  • 复制是MySQL成为互联网应用的数据库系统的关键特性。

1.7 MySQL的开发模式

1.8 总结

  • MySQL拥有分层的架构。上层是服务器层的服务和查询执行引擎,下层则是存储引擎。虽然有很多不同作用的插件API,但存储引擎API还是最重要的。如果能理解MySQL在存储引擎和服务层之间处理查询时如何通过API来回交互,就能抓住MySQL的核心基础架构的精髓。

你可能感兴趣的:(mysql,数据库)