MySQL的最主要特性是它的存储引擎架构,这种架构设计将查询处理以及其他系统任务和数据的存储/提取相分离。
MySQL最上层服务是一些如连接处理,授权认证,安全等。
MySQL的核心服务功能大部分度在第二层架构中。包括查询解析,分析,优化,缓存以及所有的内置函数,所有跨存储引擎的功能都在这一层实现:存储过程,触发器和视图等。
第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。服务器通过API鱼存储引擎进行通信。
MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询,决定表的读取顺序,以及选择合适的索引号。用户可以通过特殊的关键字提示优化器,影响它的决策过程。也可以请求优化器解释优化过程的各个因素,使用户可以知道服务器是如何进行优化决策的,并提供一个参考基准,便于用户重构查询和schema。优化器并不关注使用的是什么存储引擎,但是存储引擎会影响优化器。优化器会请求存储引擎提供容量或某个具体的开销信息,以及表数据的统计信息等。对于select语句,在解析查询之前,服务器会先检查查询缓存,如果能在其中找到对应的查询,服务器就不必再执行查询解析,优化和执行的整个过程,而是直接返回查询缓存中的结果。
在处理并发的读或写时,通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种类型的锁通常被称为共享锁(shared lock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(write lock)。读锁是共享的,不互斥。多个客户在同一时刻可以同时读取同一个资源而互不干扰。写锁是排他的,一个写锁会阻塞其他的写锁和读锁,只有一个用户能执行。
锁策略是在锁的开销和数据的安全性之间寻求平衡。多数数据库都是在表上施加行级锁。
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。它会锁定整张表。READ LOCAL表锁支持某些并发操作并且写锁比读锁由更高的优先级别。
行级锁可以最大程度的支持并发处理,同时也有很大的锁开销。行级锁只在存储引擎层实现。
事务是一组原子性的SQL查询或一个独立的工作单元。用START TRANSACTION开始一个事务,要么用COMMIT提交事物,要么用ROLLBACK撤销所有修改。
事务的四种特性:
原子性(atomicity):一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要不全部失败回滚。
一致性(consistency):数据库总是从一个一致性状态转换到另一个一致性的状态。
隔离性(isolation):一个事务所作的修改在最终提交以前,对其他事务是不可见的。
持久性(durability):一旦数据提交,则所做的修改就会永久保存到数据库中。
若存储引擎不支持事务,可以通过LOCK TABLE语句为应用提供一定程度的保护。
在SQL标准中定义了四种隔离级别,每一种隔离级别都规定了一个事务中所做的修改,哪些在事务内是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。
READ UNCOMMITTED(为提交读):事务中的修改,即使没提交,对其他事务也是可见的。事务可以读取为提交的数据被称为脏读。
READ COMMITTED(提交读):一个事务开始时,只能看到意境提交事物所做的修改。即一个事务从开始到提交之前,所作的任何修改对其他事务都是不可见的。也叫不可重复读。
REPEATABLE READ(可重复读):该级别保证了在同一个事务中国多次读取同样记录的结果是一样的。可重复级别会产生幻读,即当某个事物在读取某个范围内的记录是,另外一个事务由在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。可重复读诗MySQL的默认事务隔离级别。
SERIALIZABLE(序列化):SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了幻读。SERIALIZABLE会在读取的每一行数据上都加锁,所以导致大量的超时和锁争用问题。
死锁是两个或多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。数据库系统实现了死锁检验和死锁超时机制。InnoDB目前处理死锁的方式是将持有最少行级排他锁的事务进行回滚。锁的行为和顺序是和存储引擎相关的。死锁的产生有两种原因:真正的数据冲突和存储引擎的实现方式导致的。
使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中。事务日志采用的是追加方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O。事务日志持久后,内存中被修改的数据在后台可以慢慢的刷回到磁盘。通常称之为欲写式日志,修改数据需要写两次日志。
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
MySQL默认采用自动提交模式。若不显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。在当前连接中更可以通过AUTOCOMMIT变量来启用或禁止自动提交模式
SET AUTOCOMMIT = 1/0;(1表示启用,0表示禁止)
MySQL可以通过SET TRANSACTION ISOLATION LEVEL命令来设置隔离级别。
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITED
InnoDB采用的是两阶段锁定协议。在事务执行过程中,随时可以执行锁定,锁只有在执行COMMIT或ROLLBACK时才会释放,并且所有的锁都是在同一时刻被释放。这些锁定都是隐式锁定,InnoDB会根据隔离级别在所需要的时候自动加锁。MySQL也支持LOCK TABLES和UNLOCK TABLES语句,这是在服务器层实现的。除了事务中禁用了AUTOCOMMIT,可以用LOCK TABLE外,其他任何时候都不需要显式的执行LOCK TABLE。
MVCC(多版本并发控制)是通过保存数据在某个时间点的快照来实现的。
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。一个保存了行的创建时间,一个保存行的过期时间,这个时间不是真实的时间,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号。
SELECT
InnoDB会根据以下两个条件检查每行记录:
a.InnoDB只查找版本早于当前事务版本的数据行
b.行的删除版本要么为定义,要么大于当前事务的版本号
INSERT
InnoDB为每新插入的每一行保存当前系统版本号作为行版本号
DELETE
InnoDB为删除的每一行保存当前系统版本号作为删除标识
UPDATE
InnoDB为插入每一行新纪录,保存当前系统版本号为行版本号,同时保存当前系统版本号到原来行为作行删除标识
MVCC在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。
MySQL将每个数据库保存为数据目录下的一个字目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件保存表的定义。在Windows中,文件名大小写不敏感,在类Unix中则大小写敏感。可以使用SHOW TABLE STATUS现实表的相关信息。
InnoDB存储引擎
InnoDB时MySQL的默认事务型引擎。它被设计为用来处理大量的短期的事务。InnoDB的性能和自动崩溃恢复特性使得它在非事务型存储的需求中也很流行。
InnoDB的数据存储在表空间中,表空间是由InnoDB管理的一个黑盒子,由一系列的数据文件组成。InnoDB可以将每个表的数据和索引存放在单独的文件中。InnoDB也可以使用裸设备作为表空间的存储介质。
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。默认级别是REPEATABLE READ,并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
间隙锁的出现最要集中在同一个事务中先delete后insert的情况下,当我们通过一个参数去删除一条记录时,若参数在数据库中存在,此时产生的是普通的行锁,锁住这个纪录然后删除,最后释放锁。若这条纪录不存在,这时候执行delete时获取到的是一个间隙锁,数据库会向左扫描到一滴一个比给定值参数小的值,向右扫描到第一个比给定参数大的值,然后以此为界,构建一个区间,锁住整个区间内的数据。这很容易造成死锁。
InnoDB表是基于蔟族索引建立的,蔟族索引对主键查询有很高的性能。不过它的二级索引必须包含主键列。因此若表上索引比较多时,主键应尽量的小。InnoDB支持热备份。
MyISAM
MyISAM提供全文索引,压缩,空间函数等,但MyISAM不支持事务和行级锁,而且崩溃后无法安全恢复。
MyISAM会将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。MyISAM表可以包含动态或者静态行。MyISAM表可以存储的行记录数,一般受限于可用过的磁盘空间,或者操作系统中国年单个文件的最大尺寸。在MySQL 5.0中,MyISAM表如果时变长行,则默认配置只能处理256TB的数据,因为指向数据记录的指针长度时6个字节。所有的MySQL版本都支持8个字节的指针。要改变MyISAM表指针的长度,可以通过修改表的MAX_ROWS和AVG_ROW_LENGTH选项的值来实现。
MyISAM特性
加锁与并发:MyISAM对整张表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁,写入时对所有表加排他锁。读取同时可以插入新的记录,被称为并发插入(concurrent insert)。
修复:对于MyISAM表,MySQL可以手工或者自动执行检查和修复操作。执行表的修复可能导致一些数据丢失,而且修复操作慢。可以通过CHECK TABLE mytable检查表的错误,若有错误可以通过执行REPAIR TABLE mytable进行修复。若MySQL已经关闭,可以通过myisamchk命令行工具进行检查和修复操作。
索引特性:对于MyISAM表,即使时BLOB和TEXT等长字段,可以基于前500个字符创建索引。MyISAM支持全文索引。
延迟更新索引键(delay key write):创建MyISAM表时,若指定了DELAY_KEY_WRITE选项,在每次修改执行完成时,不会立刻将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区(in-memory key buffer),只有在清理键缓冲区或关闭表时才会将对应的索引块写入到磁盘。这种方式可以提升性能,但是在数据库或主机崩溃时会造成索引损坏,需要执行修复操作。
若表在创建并导入数据以后,不会再进行修改操作,这样的表适合采用MyISAM压缩表。可以使用myisampack对MyISAM表进行压缩。压缩表时不能进行修改的,可以极大地减少磁盘空间占用,可以减少磁盘I/O,从而提升查询性能。压缩表也支持索引,但索引只是只读的。压缩表中的记录时独立压缩的,所以读取单行时不需要去解压整个表。
Archive引擎
Archive存储引擎只支持INSERT和SELECT操作。Archive引擎会缓存所有的写并利用zlib对插入的行进行压缩,所以比MyISAM表的磁盘I/O更少。但每次SELECT查询都需要执行全表扫描。Archive表适合日志和数据采集类应用,这类应用数据分析时往往需要全表扫描。Archive引擎支持行级锁和专用的缓冲区。在一个查询直到返回表中存在的所有行数之前,Archive引擎会组织其他的SELECT执行,以实现一致性读。Archive也实现了批量插入在完成之前对读操作是不可见的。但Archive引擎不是一个事务型引擎,而是一个针对告诉插入和压缩做了优化的简单引擎。
Blackhole引擎
Blackhole引擎没有实现任何的存储机制,会丢弃所有插入的数据,不做任何保存。但是服务器会记录Blackhole表的日志,所以可以用于复制数据库到备份库。这种存储引擎可以在一些特殊的复制架构和日志审核时发挥作用。
CSV引擎
CSV引擎可以将普通的CSV文件作为MySQL的表来处理,但这种表不支持索引。CSV引擎可以在数据库运行时拷入或者拷出文件。CSV引擎可以作为一种数据交换的机制。
Federated引擎
Federated引擎是访问其他MySQL服务器的一个代理,它会创建一个远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,然后提取或发送需要的数据。默认是禁用的。
Memory引擎
若需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没关系,此时可以使用Memory表(HEAP表)。Memory表所有数据都保存在内存中,不需要进行磁盘I/O。Memory表的结构在重启以后还会保留,但数据会丢失。
Memory表的应用场景:
用于查找(lookup)或映射(mapping)表
用于缓存周期性聚合数据(periodically aggregated data)的结果
用于保存数据分析中产生的中间数据
Memory表支持Hash索引。Memory表时表级锁,因此并发写入性能较低。不支持BLOB或TEXT类型的列,并且每行的长度时固定的,所以即使指定了VARCHAR,实际存储时也是CHAR。若MySQL在执行查询的过程中国年需要使用临时表来保存中间结果,内部使用的临时表时Memory表。若结果太大超出了Memory表的限制或含有BLOB或TEXT字段,则临时表会转换成MyISAM表。
Merge引擎
Merge表是由多个MyISAM表合并而来的虚拟表。
NDB集群引擎
作为SQL会NDB原生协议直接的借口。MySQL服务器,NDB集群存储引擎,以及分布式的,share-nothing的,容灾的,高可用的NDB数据库的组合被称为MySQL集群。
MySQL默认是面向行的,每一行的数据是一起存储的,服务器的查询也是以行为单位处理的。在大数据处理时,面向列的方式可能效率更高。Infobright时为数据分析和数据仓库应用设计的。数据高度压缩,按照块进行排序,每个块都对应有一组元数据。在处理查询时,访问元数据可决定跳过该块,甚至可能只需要元数据就可以满足查询需求。但该引擎不支持索引。它自身的块结构是一种准索引(quasi-index)。
存储引擎的选择
大部分时候,InnoDB时正确的存储引擎选择。如需要用到全文索引,可以考虑InnoDB加上Sphinx的组合,而不是选择支持全文索引的MyISAM。
需要从以下角度来选择存储引擎:事务(若支持事务,则InnoDB是最好的选择,若不需要事务,并且以SELECT和DELETE操作为主,则MyISAM是不错的选择),备份,崩溃恢复和特有特性。
如果数据量持续增长到10TB以上的级别,可能需要建立数据仓库。Infobright是MySQL数据仓库最成功的解决方案
转换存储引擎
ALTER TABLE
ALTER TABLE mytable ENIGINE = InnoDB;
该语句会执行很长时间。MySQL会按行将数据从原表复制到一张新表中,在复制期间可能会消耗系统所有的I/O能力,同时会在原表加读锁。并且会丢失所有引擎相关的特性。
导出导入
可以使用mysqldump工具将数据导出文件,然后修改文件中的CREATE TABLE语句的存储引擎选项,注意同时修改表名。mysqldump会自动在CREATE TABLE语句前加上DROP TABLE语句。
创建与查询
先创建一个新的存储引擎的表,然后利用INSERT... SELECT语句来倒入数据
CREATE TABLE innodb_table LIKE myisam_table;
ALTER TABLE Innodb_table ENGINE=InnoDB;
INSERT INTO innodb_table SELECT * FROM myisam_table;
数据量大的话可以考虑做分批处理,针对每一段数据执行事务提交操作,以免避免大事物产生过多的undo。