存储引擎负责MySQL中数据的存储和提取
服务器通过存储引擎API进行通信
但存储引擎不会去解析SQL (InnoDB是一个例外,它会解析外键定义,因为MySQL服务端没有实现该功能。),不同存储引擎之间也不会相互通信,而只是简单地响应服务器的请求
默认情况下,每个客户端连接都会在服务器进程中拥有一个线程,该连接的查询只会在这个单独的线程中执行,该线程驻留在一个内核或者CPU上。服务器维护了一个缓存区,用于存放已就绪的线程,因此不需要为每个新的连接创建或者销毁线程
优化器并不关心表使用的是什么存储引擎,但存储引擎对于查询优化是有影响的。优化器会向存储引擎询问它的一些功能、某个具体操作的成本,以及表数据的统计信息
查询缓存已经被官方标注为被弃用的特性,并在8.0版本中被完全移除
锁是数据库实现一致性保证的方法
每种MySQL存储引擎都可以实现自己的锁策略和锁粒度
ANSI SQL标准定义了4种隔离级别
大多数数据库系统的默认隔离级别是READ COMMITTED(但MySQL不是)
可重复读隔离级别还是无法解决另外一个幻读,InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题
InnoDB目前处理死锁的方式是将持有最少行级排他锁的事务回滚(这是一种最容易回滚的近似算法)
死锁的产生有双重原因:有些是因为真正的数据冲突,这种情况通常很难避免,但有些则完全是由于存储引擎的实现方式导致的(一些存储引擎会锁定整个表,而其他存储引擎则实现了更复杂的基于行的锁定。这些逻辑大部分都是在存储引擎层实现的)
默认情况下,单个INSERT、UPDATE或DELETE语句会被隐式包装在一个事务中并在执行成功后立即提交,这称为自动提交(AUTOCOMMIT)模式。还有一些命令,当在活动的事务中发出时,会导致MySQL在事务的所有语句执行完毕前提交当前事务。这些通常是进行重大更改的DDL命令
MySQL不在服务器层管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,混合使用多种存储引擎是不可靠的。
InnoDB使用两阶段锁定协议,在事务执行期间,随时都可以获取锁,但锁只有在提交或回滚后才会释放,并且所有的锁会同时释放,InnoDB会根据隔离级别自动处理锁
InnoDB还支持通过特定的语句进行显式锁定,这些语句不属于SQL规范:【for update】、【for share】,MySQL还支持LOCK TABLES和UNLOCK TABLES命令,这些命令在服务器级别而不在存储引擎中实现
但各自的实现机制不尽相同,因为MVCC如何工作没有统一的标准
MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低
InnoDB通过为每个事务在启动时分配一个事务ID来实现MVCC
InnoDB是为了处理大量短期事务而设计的
InnoDB默认为REPEATABLE READ隔离级别,并且通过间隙锁(next-key locking)策略来防止在这个隔离级别上的幻读:InnoDB不只锁定在查询中涉及的行,还会对索引结构中的间隙进行锁定,以防止幻行被插入
从5.7版开始,Performance Schema在默认情况下是启用的。大多数插桩默认是禁用的,只启用了全局、线程、语句和事务插桩。从8.0版本开始,默认情况下还启用了元数据锁和内存插桩。mysql、information_schema和performance_schema数据库没有启用插桩
Oracle DBA在几年前就放弃了基于比率的调优(InnoDB缓冲池命中率)
整型数据比字符型数据的比较操作代价更低,因为字符集和排序规则(collation)使字符型数据的比较更复杂
可为NULL的列会使用更多的存储空间,在MySQL里也需要特殊处理
VARCHAR需要额外使用1或2字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只使用1字节表示,否则使用2字节
决定使用原生SQL还是JSON取决于在数据库中存储JSON的便捷性是否大于性能。
对于完全“随机”的字符串要非常小心(对于一些有很多写入的非常大的表,这种伪随机值实际上可以帮助消除“热点”),如MD5()、SHA1()或UUID()生成的字符串。这些函数生成的新值会任意分布在很大的空间内,这会减慢INSERT和某些类型的SELECT查询的速度
MySQL的存储引擎API通过在服务器和存储引擎之间以行缓冲区格式复制行来工作
MySQL限制每个联接有61个表
MySQL会对NULL值进行索引,而Oracle则不会
索引优化应该是对查询性能优化最有效的手段 ,MySQL只能有效地使用索引的最左前缀列。
在MySQL中,索引是在存储引擎层而不是服务器层实现的。不同存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引
B-tree是按照索引列中的数据大小顺序存储的,所以很适合按照范围来查询
B-tree索引适用于全键值、键值范围或键前缀查找,
也有些限制并不是B-tree本身导致的,而是MySQL查询优化器和存储引擎使用索引的方式导致的,这部分限制在未来的版本中可能就不再是限制了
最常见的B-tree索引,按照顺序存储数据,所以MySQL可以用来做ORDER BY和GROUPBY操作。
MySQL无法使用前缀索引做ORDER BY和GROUP BY操作,也无法使用前缀索引做覆盖扫描。
索引合并:查询能够同时使用两个单列索引进行扫描,并将结果进行合并。这种算法有三个变种:OR条件的联合(union),AND条件的相交(intersection),组合前两种情况的联合及相交
将选择性最高的列放到索引最前列,考虑如何避免大量随机I/O和排序可能更重要
插入速度严重依赖于插入顺序。按照主键的顺序插入行是将数据加载到InnoDB表中最快的方式。但如果不是按照主键的顺序加载数据,那么在加载完成后最好使用OPTIMIZE TABLE命令重新组织一下表
设计优秀的索引应该考虑到整个查询,而不单是WHERE条件部分
MySQL需要单独维护重复的索引,优化器在优化查询的时候也需要逐个地进行评估,这会影响性能,同时也浪费磁盘空间
设计查询的时候,一个需要考虑的重要问题是,是否需要将一个复杂的查询分成多个简单的查询。
MySQL的客户端和服务器之间的通信协议是“半双工”的
一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计 MySQL只是基于其成本模型选择最优的执行计划,不一定是最快的,数据存储地方mysql不知道磁盘?内存?
MySQL对查询的静态优化只需要做一次,但对查询的动态优化则在每次执行时都需要重新评估。有时候甚至在查询的执行过程中也会重新优化
MySQL将IN()列表中的数据先进行排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个O(logn)复杂度的操作,等价地转换成OR查询的复杂度为O(n),对于IN()列表中有大量取值的时候,MySQL的处理速度将会更快
在MySQL 8.0.20版本之后,已经不再使用基于块的嵌套循环联接操作,取而代之的是哈希联接
反转联接顺序会让查询进行更少的回溯和重读操作(305060>5011>3011 循环连接》大表哈希连接》小表哈希连接)
当搜索空间非常大的时候,优化器不可能逐一评估每一种联接顺序的成本。这时,优化器选择使用“贪婪”搜索的方式查找“最优”的联接顺序。实际上,当需要联接的表超过optimizer_search_depth的限制的时候,就会选择“贪婪”搜索模式
尽可能避免排序或者尽可能避免对大量数据进行排序,MySQL将这个过程统一称为文件排序(filesort),即使完全是在内存中排序不需要任何磁盘文件时也是如此。
DISTINCT这样的约束来避免文件排序。
除非你确实需要服务器消除重复的行,否则一定要使用UNION ALL ,MySQL总是将结果放入临时表,然后再读出,再返回给客户端
建议坚持使用基于行的复制,除非某些场景下明确需要临时使用基于语句的复制。基于行的复制提供了最安全的数据复制方法
GTID解决了运行MySQL复制的一个令人痛苦的问题:处理日志文件和位置。