Mysql体系结构和存储引擎
数据库和数据库实例
数据库:物理操作系统文件或其他形式文件类型的集合。在MySQL数据库中,数据库文件可以是fim、 MYD、MYI、ibd结尾的文件。当使用NDB引擎时,数据库的文件可能不是操作系统上的文件,而是存放于内存之中的文件,但是定义仍然不变。
实例:MySQL数据库由后台线程以及一个共享内存区组成。共享内存可以被运行的后台线程所共享。需要牢记的是,数据库实例才是真正用于操作数据库文件的。
这两个词有时可以互换使用,不过两者的概念完全不同。在MySQL数据库中,实例与数据库的关通常系是一一对应的,即一个实例对应一个数据库,一个数据库对应一个实例。但是,在集群情况下可能存在一个数据库被多个数据实例使用的情况。
很多人会把这两个概念混淆,即MySQL是数据库,MySQL 也是数据库实例。这样来理解Oracle和Microsoft SQL Server数据库可能是正确的,但是这会给以后理解MySQL体系结构中的存储引擎带来问题。从概念上来说,数据库是文件的集合,是依照某种数据模型组织起来并存放于二级存储器中的数据集合;数据库实例是程序,是位于用户与操作系统之间的-层数据管理软件,用户对数据库数据的任何操作,包括数据库定义、数据查询、数据维护、数据库运行控制等都是在数据库实例下进行的,应用程序只有通过数据库实例才能和数据库打交道。
MySQL被设计为一个单进程多线程架构的数据库,这点与SQL Server比较类似,但与Oracle多进程的架构有所不同(Oracle 的Windows版本也是单进程多线程架构的)。这也就是说,MySQL 数据库实例在系统上的表现就是一个进程。
在Linux中,可以通过一下命令启动Mysql数据库实例,以及查看进程:
# 启动实例
./mysqld_
# 查看进程
ps -ef | grep mysqld
当启动实例时,MySQL数据库会去读取配置文件,根据配置文件的参数来启动数据库实例。这与Oracle的参数文件(spfile) 相似,不同的是,Oracle 中如果没有参数文件,在启动实例时会提示找不到该参数文件,数据库启动失败。而在MySQL数据库中,可以没有配置文件,在这种情况下,MySQL会按照编译时的默认参数设置启动实例。用以下命令可以查看当MySQL数据库实例启动时,会在哪些位置查找配置文件。
> mysql --help | grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
可以看到,数据库按/etc/my.cnf =》/etc/mysql/my.cnf =》 /usr/local/mysql/etc/my.cnf =》 ~/.my.cnf 的顺序读取配置文件的。如果几个配置文件中都有同一参数,则以最后读取到的配置文件为准。
体系结构
可以看到,Mysql由以下几部分组成:
- 连接池组件
- 管理服务和工具组件
- SQL接口组件
- 查询分析器组件
- 优化器组件
- 缓冲(Cache) 组件
- 插件式存储引擎
- 物理文件
其主要分为以下几层:
- 连接管理层,包括连接池支持的各种组件
- 服务管理层,含管理服务和相关工具、接口、分析、优化等组件
- 存储引擎,含各种以插件提供的数据存储引擎
- 文件存储层,含各种实际落到硬盘的物理文件
从图中还可以发现,MySQL数据库区别于其他数据库的最重要的一个特点就是其插件式的表存储引擎。MySQL插件式的存储引擎架构提供了一系列标准的管理和服务支持,这些标准与存储引擎本身无关,可能是每个数据库系统本身都必需的,如SQL分析器和优化器等,而存储引擎是底层物理结构的实现,每个存储引擎开发者可以按照自己的意愿来进行开发。
需要特别注意的是,存储引擎是基于表的,而不是数据库。
存储引擎
存储引擎是MySQL区别于其他数据库的一个最重要特性。存储引擎的好处是,每个存储引擎都有各自的特点,能够根据具体的应用建立不同存储引擎表。对于开发人员来说,存储引擎对其是透明的,但了解各种存储引擎的区别对于开发人员来说也是有好处的。对于DBA来说,他们应该深刻地认识到MySQL数据库的核心在于存储引擎。
由于MySQL数据库的开源特性,用户可以根据MySQL预定义的存储引擎接口编写自己的存储引擎。若用户对某一种存储引擎的性能或功能不满意,可以通过修改源码来得到想要的特性,这就是开源带给我们的方便与力量。
InnoDB
InnoDB存储引擎支持事务,其设计目标主要面向在线事务处理(OLTP)的应用。其特点是行锁设计、支持外键,并支持类似于Oracle的非锁定读,即默认读取操作不会产生锁。从MySQL数据库5.5.8版本开始,InnoDB 存储引擎是默认的存储引擎。
InnoDB存储引擎将数据放在一个逻辑的表空间中,这个表空间就像黑盒一样由InnoDB存储引擎自身进行管理。从MySQL4.1 (包括4.1) 版本开始,它可以将每个
InnoDB存储引擎的表单独存放到一个独立的ibd文件中。此外,InnoDB 存储引擎支持用裸设备(row disk)用来建立其表空间。
InnoDB通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了SQL标准的4种隔离级别,默认为REPEATABLE级别。同时,使用一种被称为next-key
locking的策略来避免幻读(phantom)现象的产生。
除此之外,InnoDB 储存引擎还提供了**插人缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead)**等高性能和高可用的功能。
对于表中数据的存储,InnoDB 存储引擎采用了聚集(clustered) 的方式,因此每张表的存储都是按主键的顺序进行存放。如果没有显式地在表定义时指定主键,InnoDB 存储引擎会为每一行生成一个 6字节的ROWID,并以此作为主键。
MyISAM
MyISAM存储引擎不支持事务、表锁设计,支持全文索引,主要面向一些OLAP数据库应用。在MySQL5.5.8版本之前MyISAM存储引擎是默认的存储引擎(除
Windows版本外)。
数据库系统与文件系统很大的一个不同之处在于对事务的支持,然而MyISAM存储引擎是不支持事务的。究其根本,这也不是很难理解。试想用户是否在所有的应用中都需要事务呢?在数据仓库中,如果没有ETL这些操作,只是简单的报表查询是否还需要事务的支持呢?此外,MyISAM存储引擎的另一个与众不同的地方是它的缓冲池只缓存(cache) 索引文件,而不缓冲数据文件,这点和大多数的数据库都非常不同。
MyISAM存储引擎表由MYD和MYI组成,MYD用来存放数据文件,MYI用来存放索引文件。可以通过使用myisampack工具来进一步压缩数据文件,因为myisampack工具使用赫夫曼(Huffman) 编码静态算法来压缩数据,因此使用myisampack工具压缩后的表是只读的,当然用户也可以通过myisampack来解压数据文件。
在MySQL 5.0版本之前,MyISAM默认支持的表大小为4GB,如果需要支持大于4GB的MyISAM表时,则需要制定MAX_ROWS和AVG_ROW_LENGTH属性。从MySQL 5.0版本开始,MyISAM 默认支持256TB的单表数据,这足够满足一般应用需求。
注意:对于MylSAM存储引擎表,MySQL数据库只缓存其索引文件,数据文件的缓存交由操作系统本身来完成,这与其他使用LRU算法缓存数据的大部.
分数据库大不相同。此外,在MySQL 5.1.23版本之前,无论是在32位还是64位操作系统环境下,缓存索引的缓冲区最大只能设置为4GB。在之后的版本
中,64位系统可以支持大于4GB的索引缓冲区。
NDB
NDB存储引擎是一个集群存储引擎,类似于Oracle的RAC集群,不过与Qracle RACshare everything架构不同的是,其结构是share nothing的集群架构,因此能提供更高的可用性。NDB的特点是数据全部放在内存中(从MySQL5.1版本开始,可以将非索引数据放在磁盘上),因此主键查找(primary key lookups)的速度极快,并且通过添加NDB数据存储节点(Data Node)可以线性地提高数据库性能,是高可用、高性能的集群系统。
关于NDB存储引擎,有一个问题值得注意,那就是NDB存储引擎的连接操作(JOIN)是在MySQL数据库层完成的,而不是在存储引擎层完成的。这意味着,复杂的
连接操作需要巨大的网络开销,因此查询速度很慢。如果解决了这个问题,NDB存储引擎的市场应该是非常巨大的。
Memory
Memory存储引擎(之前称HEAP存储引擎)将表中的数据存放在内存中,如果数据库重启或发生崩溃,表中的数据都将消失。它非常适合用于存储临时数据的临时表,
以及数据仓库中的纬度表。Memory 存储引擎默认使用哈希索引,而不是我们熟悉的B+树索引。
虽然Memory存储引擎速度非常快,但在使用上还是有一定的限制。 比如,只支持表锁,并发性能较差,并且不支持TEXT和BLOB列类型。最重要的是,存储变长字段(varchar)时是按照定常字段(char) 的方式进行的,因此会浪费内存(这个问题之前已经提到,eBay 的工程师Igor Chernyshev已经给出了patch解决方案)。
此外有一点容易被忽视,MySQL 数据库使用Memory存储引擎作为临时表来存放查询的中间结果集( intermediate result)。 如果中间结果集大于Memory存储引擎表的容量设置,又或者中间结果含有TEXT或BLOB列类型字段,则MySQL数据库会把其转换到MyISAM存储引擎表而存放到磁盘中。之前提到MyISAM不缓存数据文件,因此这时产生的临时表的性能对于查询会有损失。
Archive
Archive存储引擎只支持INSERT和SELECT操作,从MySQL5.1开始支持索引。Archive存储引擎使用zlib算法将数据行(row)进行压缩后存储,压缩比一般可
达1:10。正如其名字所示,Archive存储引擎非常适合存储归档数据,如日志信息。Archive存储引擎使用行锁来实现高并发的插人操作,但是其本身并不是事务安全的存储引擎,其设计目标主要是提供高速的插人和压缩功能。
Federated
Federated存储引擎表并不存放数据,它只是指向一台远程MySQL数据库服务器上的表。这非常类似于SQLServer的链接服务器和Oracle的透明网关,不同的是,当前Federated存储引擎只支持MySQL数据库表,不支持异构数据库表。
Maria
Maria存储引擎是新开发的引擎,设计目标主要是用来取代原有的MyISAM存储引擎,从而成为MySQL的默认存储引擎。Maria 存储引擎的开发者是MySQL的创始人之一的MichaelWidenius。因此,它可以看做是MyISAM的后续版本。Maria存储引擎的特点是:支持缓存数据和索引文件,应用了行锁设计,提供了MVCC功能,支持事务和非事务安全的选项,以及更好的BLOB字符类型的处理性能。
比较
除了上诉的存储引擎,还有一些别的存储引擎,比如:Merge、CSV、Sphinx、Infobright。
下图取自于MySQL的官方手册,展现了一些常用MySQL存储引擎之间的不同之处,包括存储容量的限制、事务支持、锁的粒度、MVCC支持、支持的索引、备份和复制等。
可以通过SHOW ENGINES
语句查看当前使用的MySQL数据库所支持的存储引擎,也可以通过查找information schema 架构下的ENGINES表,如下所示:
下面将通过MySQL提供的示例数据库来简单显示各存储引擎之间的不同。这里将分别运行以下语句,然后统计每次使用各存储引擎后表的大小。
mysql>CREATE TABLE mytest Engine=MyISAM
->AS SELECT * FROM salaries;
Query oK, 2844047 rowS affected (4.37 sec)
Records: 2844047 Duplicates: 0 Warnings: 0
mysql>ALTER TABLE mytest Engine= InnoDB;
Query oK, 2844047 rows affected (15.86 sec)
Records: 2844047 Duplicates: 0 warnings: 0
mysql>ALTER TABLE mytest Engine =ARCHIVE ;
Query oK, 2844047 rows affected (16.03 sec)
Records: 2844047 Duplicates: 0 warnings: 0
通过每次的统计,可以发现当最初表使用MyISAM存储引擎时,表的大少为40.7MB,使用InnoDB存储引擎时表增大到了113.6MB,而使用Archive存储引擎时表的大小却只有20.2MB。该例子只从表的大小方面简单地揭示了各存储引擎的不同。
连接MySQL
连接MySQL操作是一个连接进程和MySQL数据库实例进行通信。从程序设计的角度来说,本质上是进程通信。如果对进程通信比较了解,可以知道常用的进程通信方式有:管道、命名管道、命名字、TCP/IP套接字、UNIX域套接字。MySQL数据库提供的连接方式从本质上看都是上述提及的进程通信方式。
(1)TCP/IP
TCP/IP套接字方式是MySQL数据库在任何平台下都提供的连接方式,也是网络中使用得最多的一种方式。这种方式在TCP/IP连接上建立一个基于网络的连接请求,一般情况下客户端(client) 在一台服务器上,而MySQL实例(server) 在另一台服务器上,这两台机器通过一个TCP/IP网络连接。例如用户可以在Windows服务器下请求一台远程Linux服务器下的MySQL实例,如下所示:
mysql -h 192.168.0.101 -u root -p
这里需要注意的是,在通过TCP/IP连接到MySQL实例时,MySQL数据库会先检查一张权限视图,用来判断发起请求的客户端IP是否允许连接到MySQL实例。该视图在mysql架构下,表名为user,如下所示:
mysql>USE mysql;
Database changed
mysql> SELECT host, user, password FROM user;
row★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
host: 192.168.24.%
user: root
password: *75DBD4EA548120B54FE693006C41AA9A16DE8FBE
★★★★★★★★★★★★★★★★★★★★★★★★★★★★2. row ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
host: nineyou0-43
user: root
password: *75DBD4FA548120B54FE693006C41AA9A16DE8FBE
★★★★★★★★★★★★★★★★★★★★★★★★★★★★3. row★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
host: 127.0.0.1
user: root
password: *75DBD4EA548120B54EE693006C41AA9A16DE8EBE
★★★★★★★★★★★★★★★★★★★★★★★★★★★★4. row★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
host: 192.168.0.100
user: z1m
password: *DAE0939275CC7CD8E0293812A31735DA9CF0953C
★★★★★★★★★★★★★★★★★★★★★★★★★★★★5. row★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
host: %
user: david
password:
5 rows in set (0.00 sec)
从这张权限表中可以看到,MySQL允许david这个用户在任何IP段下连接该实例,并且不需要密码。此外,还给出了root用户在各个网段下的访问控制权限。
(2)命名管道和共享内存
在Windows 2000、Windows XP、Windows 2003和Windows Vista以及在此之上的平台上,如果两个需要进程通信的进程在同一台服务器上,那么可以使用命名管道,Microsoft SQL Server数据库默认安装后的本地连接也是使用命名管道。在MySQL数据库中须在配置文件中启用**–enable named-pipe选项。在MySQL 4.1之后的版本中,MySQL还提供了共享内存的连接方式,这是通过在配置文件中添加–shared-memory实现的。如果想使用共享内存的方式,在连接时,MySQL 客户端还必须使用–protocol-memory**选项。
(3)UNIX域套接字
在Linux和UNIX环境下,还可以使用UNIX域套接字。UNIX域套接字其实不是一个网络协议,所以只能在MySQL客户端和数据库实例在一台服务器上的情况下使用。用户可以在配置文件中指定套接字文件的路径,如–socket=/tmp/mysql.sock。当数据库实例启动后,用户可以通过下列命令来进行UNIX域套接字文件的查找:
mysql>SHOW VARIABLES LIKE 'socket' ;
*************************** 1. row ***************************
Variable_ name: socket
Value: 1 tmp/mysql. sock
1 row in set (0.00 sec)
在知道UNIX域套接字文件的路径后,就可以使用该方式进行连接了:
mysql -u david -S tmp/mysql. sock