MySQL 的架构与历史

参考内容: 《高性能 MySQL(第三版))》 码农翻身知识星球 数据库村的旺财和小强

存储引擎

Writing a Custom Storage Engine / Overview 中说存储引擎负责管理 MySQL 的数据存储和索引,MySQL 服务器通过已经定义好的 API 与存储引擎进行通信。

不同的存储引擎都有各自的优势和劣势,存储引擎相互之间不会通信,除了 InnoDB 外其它引擎都不会解析 SQL,API 屏蔽了存储引擎之间的差异,使得这些差异对上层查询过程透明。存储引擎 API 包含了几十个底层函数,下面列举了其中的一小部分函数,如果你要自己实现一个存储引擎,那所做的工作就是把这些接口函数实现。

//创建一个表
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;

//打开一个表
int open(const char *name, int mode, int test_if_locked);

//写入一行数据
int write_row(byte *buf);

//更新一行
int update_row(const byte *old_data, byte *new_data);

//删除一行
int delete_row(const byte *buf);

//开始一个事务
int  start_stmt(THD *thd, thr_lock_type lock_type);

//提交一个事务
int (*commit)(THD *thd, bool all);

//回滚一个事务
int (*rollback)(THD *thd, bool all);

// ......

MySQL 内建了很多存储引擎,除此之外还有很多社区版的第三方引擎,对于这么多引擎我们应该怎么选择呢?这里可以归结为一句话「除非需要用到某些 InnoDB 不具备的特性,并且没有其它办法可以代替,否则都应该选择 InnoDB 引擎」。例如,如果要用到全文索引,建议优先考虑 InnoDB 加上 Sphinx 组合,而不是使用支持全文索引的 MyISAM。当然,如果不需要用到 InnoDB 的特性,同时其它引擎的特性能够更好的满足需求,也可以考虑一下其它引擎。

除非万不得已,否则不要混合使用多种存储引擎。如果应用需要不同的存储引擎,你至少应该考虑「事务」、「备份」、「崩溃恢复」、「特有的特性」几个因素。

并发控制

数据库需要大量的磁盘读写操作,自然而然就会产生并发控制的问题。我们可以简单的使用读写锁来解决解决并发控制问题。读锁是共享的,多个客户可以在同一时刻读取同一个资源;写锁是排他的,一个写锁会阻塞其它写锁和读锁,只有这样才能在同一时间内只有一个用户能执行写入,并防止其它用户读取正在写入的同一资源。

上一段内容只说了锁,但是没有讨论锁的粒度,锁定资源时是直接锁定整个数据库还是仅锁定具体的表,亦或是只锁定要操作的那几行记录?首先我们需要明白的是任何时候,在给定的资源上,锁定的数据量越少则系统的并发程度越高,只要相互之间不冲突即可。

但是我们还忘了另一件事,加锁本身也是需要消耗资源的,获得锁、检查锁的解除、释放锁等等都会增加系统的开销,如果系统花费大量的时间来管理锁,而不是管理数据,那么系统的并发程度同样会受到影响。因此锁策略就是在锁的开销和数据的安全性之间寻求平衡。

大多数商业数据库在锁策略上并没有提供更多的选择,一般都是在表上施加行级锁。MySQL 则提供了多种选择,每种 MySQL 引擎都可以实现自己的锁策略和锁粒度,比较重要的两种锁策略是「表锁」和「行级锁」。

多版本并发控制

刘大在数据库村的旺财和小强中把「事务的隔离级别」和「多版本并发控制(MVCC)」讲的已经比较透彻了,此处我仅借助文字描述来加深我自己的理解。

MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁的操作,因为避免了加锁的这些开销,所以 MVCC 能够提升并发性能。包括 Oracle、PostgreSQL、MySQL 等等数据库都实现了 MVCC,只是各自的实现机制不一样,因为 MVCC 没有一个统一的实现标准,比较典型的实现方式有「乐观并发控制」和「悲观并发控制」。

MVCC 是通过保存数据在某个时间点的快照来实现的(这难道不是一个版本控制器吗)。以 InnoDB 为例,它是通过在每行记录后面保存两个隐藏的列来实现,一个保存了行的创建时间,另一个则保存了行的过期(删除)时间。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增,事务开始时刻的版本号会作为事务的版本号,用来和查询到的每行记录记录的版本号做比较。

因此根据事务的开始时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。因为同一条记录在存储器中拥有多个版本,每个事务也有自己的版本号,只要把事务和记录的版本号做对比即可知道需要取哪个版本的记录了。一言以蔽之,MVCC 是一个借助空间来换取时间的策略。

小知识点

MySQL 使用文件系统的目录和文件来保存数据库和表的定义,所以大小写敏感就和具体的平台密切相关,在 Windows 中大小写不敏感,而在 Linux 等类 Unix 中则是敏感的。不同的存储引擎保存数据和索引的方式是不同的,但是表的定义则是在 MySQL 服务层统一处理的,my_table表的定义保存会被保存在my_table.frm中。

如果表在创建并导入数据后,不会再对该表进行修改操作,那么这样的表比较适合 MyISAM 压缩表,而且压缩表也支持索引。可以使用myisampack对 MyISAM 表进行压缩,压缩后的表是不能进行修改的,但是可以极大的减少磁盘的空间占用,因此也可以减少磁盘 I/O,这无疑是一种提升查询性能的方法。

数据库MySQL