本文为极客时间《MySQL实战45讲》的总结笔记,如有侵权,请通知我,将立马删除。建议大家去购买这套课程,真的是物有所值。
其实查询过程两者的时间差距是微乎其微的,普通索引要比唯一索引多一次判断下一条记录是否符合,但InnoDB 的数据是按数据页为单位来读写的,所以就算多读一次也占用不了多少时间
普通索引可以使用change buffer,可以将一系列的更新写到change buffer中,后期再一次性写入到磁盘中,极大的提高了更新的效率,而唯一索引没有办法使用change buffer
对于写多读少的业务来说,页面在写完以后马上被访问到的概率比小,此时 change buffer 的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。反过来,如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭change buffer
Mysql是根据扫描行数来判断选择哪个索引,扫描行数越少,则越容易被选择,查看扫描行数可以通过explain
关键字来查看。
explain select * from user where sex = 1
既然Mysql是根据扫描行数来选择索引的,它选错索引肯定也是因为在判断扫描行数的时候出了问题,那Mysql又是怎么来判断扫描行数的呢?答案是:采样统计
是的就是这么不靠谱的采样,Mysql官方也说了这个误差可能会达到30%~50%
对于这种情况我们可以使用analyze table 表名
来重新统计索引信息达到让Mysql选择正确的索引。或者使用force index
来强制给它指定一个索引
select * from user force index(sex) where sex = 1
如果字符串过长,而前面几个字段可以确定一个唯一值,比如邮箱,前面都是几位数字[email protected],我们不用给全部字段加上索引,而只需要索引前面几个数字即可,这样就极大的节省索引占的空间了。
alter table user add index index_email(email,9)
这个数字9怎么去确定呢,我们可以通过下面的语句,来尝试,如果查出来的值越少,就越好。
select count(distinct left(email,7)) as L7,
count(distinct left(email,8)) as L8,
count(distinct left(email,9)) as L9,
from user
有的时候字段前面都是一样,而后面是不一样的,比如身份证号,这时就不好利用前缀索引了,不过我们可以将身份证的倒序存储,这样就巧妙的再次利用前缀索引的优势了。
select * from t where id_card = reverse('input_id_card');
这种就是将字符串计算出一个hash值,然后给表新增一个字段将hash存储进去,下次查找时先将字符串换算为hash再去表中查找hash列,不过这种只适合等值查询,不能进行范围查询。
当Mysql执行过程中会突然慢下来,过一会又好了,而且不是随机的,持续时间很短,看起来就好像Mysql“抖”了一下。这个过程其实是Mysql在刷"脏页"的过程。
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“”脏页”,当把内存中的数据更新到硬盘之后这个页也就变成了“干净页”
大概下面四种情况会发生刷“脏页”过程:
我们可以通过设置innodb_io_capacity
参数来控制Mysql刷脏页的速度,如果你用的是SSD,那么建议你把这个参数设置大点。这个参数在information_schema
数据库中的GLOBAL_VARIABLES
表中设置。
当innodb_file_per_table
的参数为OFF时,表的数据会放到共享内存中,也就是和数据字典放一块。而为ON时,表的数据存储在以.ibd
为后缀的文件中,当我们使用drop table
删除表时,会直接删除这个文件达到回收的目的,而如果数据是放到了共享内存中,那么即使表删除了,空间也是不会回收的。所以我们一般都将此参数设置为ON,MySQL5.5.6版本之后默认就是ON了。
当我们删除某一行记录时,其实MySQL只是把此行记录标记为了“可复用”,但磁盘大小是不会变的,所以通过delete
表中记录是达不到回收表空间的。这些被标记为“可复用”而没有使用的空间看起来就像是“空洞”,其实不止删除会造成空洞,插入一样可以,如果我们不是按顺序插入,而是随机插入,那么就可能造成页分裂,而之前那一页末尾可能还有未使用的空间。
我们可以通过重建表来达到回收表空间,可以使用下面这个命令:
alter table 表名 engine = InnoDB
三种重建方式对比:
alter table 表名 engine = InnoDB
analyze table 表名
optimize table 表名
以下的讨论都是没有where条件判断的,如果有条件判断,则不适用。
count(其他字段) < count(主键) < count(1) ≈ count(*)
答: 一个事务的 binlog 是有完整格式的
答:它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log
答:其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关。在时刻 B,也就是 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个 binlog 恢复出来的库)使用。所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。
回答:如果只从崩溃恢复的角度来讲是可以的。你可以把binlog
关掉,这样就没有两阶段提交了,但系统依然是crash-safe
的。
但是,如果你了解一下业界各个公司的使用场景的话,就会发现在正式的生产库上,binlog
都是开着的。因为binlog有着redo log无法替代的功能。
redo log是循环写,写到末尾是要回到开头继续写的。这样历史日志没法保留 ,redo log也就起不到归档的作用。
binlog作为MySQL一开始就有的功能,被用在了很多地方。其中,MySQL系统高可用的基础,就是binlog复制。
这些系统就靠消费MySQL的binlog来更新自己的数据。关掉binlog的话,这些下游系统就没法输入了。
总之,由于现在包括MySQL+高可用在内的很多系统机制都依赖于binlog,所以“鸠占鹊巢”redo log还做不到。