参考

Mysql官网解释 5.6版本,存储引擎的详细介绍
https://dev.mysql.com/doc/refman/5.6/en/myisam-storage-engine.html

概述

Mysql支持常用的4种存储引擎:Myisam、InnoDB(默认)、Memory、Merge

  • Myisam:高效缓存 多读少写时读取更快,B+树索引
  • InnoDB:支持事务、行级锁、外键,B+树索引
  • Memory:基于Hash索引,内存存储
  • Merge:分表时子表必须是Myisam

SHOW ENGINES;查看,Mysql支持多种存储引擎。

MyISAM

  1. 特点
  • 在 heave-read的业务场景下,访问速度快
  • 主索引和辅助索引一样,都是非聚簇索引,数据和索引分离,存储的value值是真实数据的物理地址
  • 默认保存了表的总行数,select Count(*) 直接返回结果
  • 表锁,没有行锁,所以无法支持并发的写,比如无法支持同时修改一张表中2个不同的数据
  • Merge引擎分表时,子表必须是Myisam
  1. 使用场景:多读少写,不需要事务、外键,也不容易服务器异常的场景
  2. Myisam缓存机制
    每次通过索引读取数据的时候,Mysql都会缓存 这个 IndexPage 到内存当中,这个内存块的大小由 key_buffer_size(默认 8M)决定,记录了这个索引块里面的所有数据对应的物理地址信息,并且mysql有一个LRU队列去管理这个缓存块。
    包括修改的时候,也是直接修改这个缓存块(同时会记录日志),并且标记这个page为dirty,当从LRU队列里面移除的时候,如果Dirty则需要写回到硬盘上。
    因为缓存的只是索引和物理存储地址,而不缓存真正的数据,所以和Innodb相比,相同大小的缓存空间,Myisam可以缓存更多的索引。
  1. When read, a MyISAM table's indexes can be read once from the .MYI file and loaded in the MyISAM Key Cache (as sized by key_buffer_size).
  1. 5.7版本之后 Myisam不支持分区功能,只有InnoDB和NDB支持分区

InnoDB

  • 支持事务:通过内部MVCC机制实现
  • 支持外键
  • 支持行级锁:行锁是建立在索引的基础之上的,行锁锁的是索引,不是数据,所以提高并发写的能力要在查询字段添加索引,否则用的还是表锁
  • 支持并发读写,因为索引查询是行锁
  • 自动增长列
  • 辅助索引是以非聚簇索引实现的,辅助索引的value存的是主键,所以主键越小越好,减小辅助索引树的大小
  • InnoDB is typically said to have better crash recovery,灾难恢复更稳定

为什么Myisam比Innodb读取更快?

结论:在多读少写的业务场景下,Myisam的read的速度快好几倍。但是在有读有写或者多读少写的业务场景下,Myisam因为是表锁会阻塞,读和写都慢。

  1. 非聚簇索引一次lookup:在非主键索引的Query的业务场景下,Myisam只需要一次B+树 key lookup,就可以读取到data的物理地址,再一次IO读取,就可以获取到磁盘的数据信息,但是 InnoDB却需要2次索引树查找,这个都是不在一个物理page的查找,所以Myisam理论上在这一点设计上差不多快一倍。
  2. Myisam没有MVCC,在任意时间节点,一个数据就只有1个值,而Innodb就可能同时存在多个版本,加大了搜索筛选条件。
  3. Myisam可以更好的利用缓存:因为索引和数据是分离的,只缓存索引(索引值里面有key值和dataAddress),然后再一次IO读取,就可以捞取到数据。
    但是Innodb因为数据和索引是存在一起的,必须同时缓存相应的索引和数据,所以相同缓存空间下,InnoDB可以缓存的数据量更少,缓存的命中率更低,但是一旦命中,则无需IO操作,可以直接返回数据。

Myisam PK Innodb

  1. Innodb优势: 事务、外键、行级锁、并发读写、灾难恢复更靠谱
  2. Myisam优势: 文本索引、count(*)存储、多读少写时快速读取和高效缓存、表数据可以拷贝迁移

数据存储结构

数据存储位置: 在 %datadir%/databaseName
默认一个page的大小是16k

  1. Myisam
    Myisam的存储结构: 索引和数据是分开存储的。
  • student.frm :存储表结构等相关信息
  • student.MYI:存储表的索引数据
  • student.MYD:存储表的具体数据

Myisam的存储结构比较独立,可以通过直接拷贝这3个文件,来实现表数据的 跨库迁移,甚至可以是跨操作系统的迁移

  1. InnoDB
    InnoDB的存储结构:分为两种模式(共享模式和 默认的独占模式 innodb_file_per_table)
  • student.frm :存储表结构等相关信息
  • student.ibd :存储表全部的数据内容和索引内容

共享表空间以及独占表空间都是针对数据的从物理意义上来讲:

  • 共享表空间: 会把表集中存储在一个系统表空间里。即每一个数据库的所有表的数据,索引文件全部放在一个文件中。该文件目录默认的是服务器的数据目录。 默认的文件名为:ibdata1 初始化为10M。
  • 独占表空间: 每一个表分别创建一个表空间,这时。在对应的数据库目录里每一个表都有.ibd文件(这个文件包括了单独一个表的数据内容以及索引内容)

memory

  • 内存存储,mysql重启数据消失
  • 默认索引Hash算法,速度快,但是范围查找就慢,也可以通过指定 B+树为索引
  • 不支持事务、一般被redis memcache取代

merge

用来做分表用的,多个结构相同的表,虚拟出一个merge表,可单独取操作子表来实现高性能,通知也支持操作merge表来实现,逻辑上统一的数据。

注意事项

  1. InnoDB只有在走索引查询加锁时才是行锁,否则都是表锁
    例如User表 主键 id,但是name字段没有索引,事务A 通过非索引字段 name来查询 huangzs的用户,并且锁定该数据(可能有多个 huangzs用户),因为name字段没有索引,所以是表锁
    在没有commit之前,开启事务B,尝试修改 name=’huang’的操作是不能执行的,因为当前表被锁住了
  1. TXA
  2. begin;
  3. update user set memo ='hzs' where name='huangzs'
  4. TXB:
  5. begin;
  6. update user set memo ='hzs' where name='huang'

行级锁的正确使用:查询条件是通过主键字段进行行级锁的,所以可以并发的修改数据

  1. TXA
  2. begin;
  3. update user set memo ='hzs' where id=500;
  4. TXB:
  5. begin;
  6. update user set memo ='hzs' where id=501;
  1. select的时候,只有 lock in share mode 或者 for update 才会锁定数据,没有限定词的话,默认是直接读取的,不存在竞争。也就是一行数据即使加锁了,直接select也可以查的到
  2. InnoDB死锁了也不用怕,去倒杯水就好了:mysql 默认有innodb_lock_wait_timeout:50s的设置,超过50s会自动释放锁