存储引擎相关的命令
查看 MySQL 提供的所有存储引擎
show engines;
MySQL 当前默认的存储引擎
show variables like '%storage_engine%';
查看表的存储引擎
show table status like "table_name" ;
MyISAM 和 InnoDB 的区别
MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,可谓是风光一时。
5.5 版本之后,MySQL 引入了 InnoDB(事务性数据库引擎),MySQL 5.5 版本后默认的存储引擎为 InnoDB。
1.是否支持行级锁
MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
2.是否支持事务
MyISAM 不提供事务支持。
InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力。
3.是否支持外键
MyISAM 不支持外键,而 InnoDB 支持外键。
4.是否支持数据库异常崩溃后的安全恢复
MyISAM 不支持异常崩溃后的安全恢复,而 InnoDB 支持。
使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log
。
🌈 拓展一下:
- MySQL InnoDB 引擎使用
redo log
(重做日志) 保证事务的持久性
,使用undo log
(回滚日志) 来保证事务的原子性
。 - MySQL InnoDB 引擎通过 锁机制、MVCC 等手段来保证事务的
隔离性
(默认支持的隔离级别是 REPEATABLE-READ )。 - 保证了事务的持久性、原子性、隔离性之后,
一致性
才能得到保障。
5.是否支持 MVCC
MyISAM 不支持,而 InnoDB 支持。
什么是MVCC
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
关于 MyISAM 和 InnoDB 的选择问题
不要轻易相信“MyISAM 比 InnoDB 快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB 的速度都可以让 MyISAM 望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。
一般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择 MyISAM 也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。
锁机制与 InnoDB 锁算法
MyISAM 和 InnoDB 存储引擎使用的锁
- MyISAM 采用表级锁(table-level locking)。
- InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁
表级锁和行级锁对比:
- 表级锁: MySQL 中锁定
粒度最大
的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。 - 行级锁: MySQL 中锁定
粒度最小
的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁
InnoDB 存储引擎的锁的算法有三种
- Record lock:单个行记录上的锁
- Gap lock:间隙锁,锁定一个范围,不包括记录本身
- Next-key lock:record+gap 锁定一个范围,包含记录本身
查询缓存
执行查询语句的时候,会先查询缓存。不过,MySQL 8.0 版本后移除,因为这个功能不太实用
开启mysql缓存后,数据没有更新的情况下,相同的查询sql会使用缓存数据返回结果。在数据更新较少,类似查询较多的情况下,使用mysql缓存可以显著提升查询效率。
show variables like '%query_cache%';
(1) have_query_cache表示是否支持查询缓存,YES表示支持
(2) query_cache_type表示缓存类型,OFF表示关闭查询缓存,ON表示开启查询缓存,DEMAND表示用户自定义查询缓存
(3) query_cache_limit表示支持的最大单条查询sql数据量
(4) query_cache_min_res_unit表示查询缓存最小单位
(5) query_cache_size表示查询缓存空间大小
(6) query_cache_wlock_invalidate表示查询缓存是否支持写锁,OFF表示不支持,即读取数据不考虑写锁,ON表示支持,即读取数据会被写锁阻塞
查询缓存使用
(1) 只有字符串相等查询sql才使用相同缓存
,即select name from city与SELECT name FROM city不使用同一个缓存。
(2) 在query_cache_type为ON的情况下,默认所有查询都使用缓存,我们可以使用sql_no_cache显示指定某个查询不使用缓存
select sql_no_cache name from city;
(3) 在query_cache_type为DEMAND的情况下,需要使用sql_cache指定某个查询使用缓存
select sql_cache name from city;
设置查询缓存
my.cnf 加入以下配置,重启 MySQL 开启查询缓存, 0 表示OFF,1表示 ON, 2表示DEMAND
query_cache_type=1
query_cache_size=600000
MySQL 执行以下命令也可以开启查询缓存
set global query_cache_type=1;
set global query_cache_size=600000;
执行show status like 'Qcache%';
(1) Qcache_free_blocks表示已分配内存块中空闲块数量;
(2) Qcache_total_blocks表示当前查询缓存占用的内存块数量;
(3) Qcache_free_memory表示缓存空闲空间大小;
(4) Qcache_hits表示缓存命次数;
(5) Qcache_inserts表示缓存未命中时,数据写入缓存次数;
(6) Qcache_queries_in_cache表示缓存查询语句数量;
(7) Qcache_lowmem_prunes表示缓存修剪次数,缓存满时,会使用LRU算法移除最久未被使用缓存,此值较大,说明缓存空间太小;
(8) Qcache_not_cached表示没有被缓存的查询sql数量。
查询缓存的碎片率
查询缓存的碎片率 =(Qcache_free_blocks / Qcache_total_blocks)* 100%
如果Qcache_free_blocks的值过大,则碎片率越高,证明我们缓存内存碎片略多,可以尝试适当的调小query_cache_min_res_unit的值,也可以使用 FLUSH QUERY CACHE语句来清理缓存碎片。
查询缓存利用率
查询缓存利用率 = (query_cache_size - Qcache_free_memory) / query_cache_size * 100%
如果查询缓存利用率太低,则表示query_cache_size设置的可能过大,可适度调小,如果缓存利用率非常高,同时Qcache_lowmem_prunes的值比较大,则表示query_cache_size的值设置的略小。
query_cache_min_res_unit的预估值参考计算公式: (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
查询缓存命中率
查询缓存命中率 = (Qcache_hits / Com_select)* 100%
上述公式中的Com_select表示查询语句的执行次数(这样描述并不准确),可以使用如下语句获得查询语句的执行次数
show status like 'Com_select%';
重置缓存
reset query cache;
开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果也不会被缓存。
缓存建立之后,MySQL 的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。
缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。 因此,开启查询缓存要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十 MB 比较合适。此外,还可以通过 sql_cache 和 sql_no_cache 来控制某个查询语句是否需要缓存:
select sql_no_cache count(*) from usr;
事务
何为事务?
一言蔽之,事务是逻辑上的一组操作,要么都执行,要么都不执行。
何为数据库事务?
数据库事务在我们日常开发中接触的最多了。如果你的项目属于单体架构的话,你接触到的往往就是数据库事务了。
平时,我们在谈论事务的时候,如果没有特指分布式事务,往往指的就是数据库事务。
简单来说:数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:要么全部执行成功,要么全部不执行 。
开启一个事务
START TRANSACTION;
多条 SQL 语句
SQL1,SQL2...
提交事务
COMMIT;
另外,关系型数据库(例如:MySQL、SQL Server、Oracle 等)事务都有 ACID 特性:
何为 ACID 特性呢?
- 原子性(Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性(Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
- 隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
- 持久性(Durabilily): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务带来哪些问题?
- 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
- 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。
- 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
- 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。
事务隔离级别有哪些?
-** READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读**。
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化): 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
MySQL 的默认隔离级别是——REPEATABLE-READ(可重读)
MySQL InnoDB 的 REPEATABLE-READ(可重读)并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是 Next-Key Locks。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEAaTABLE-READ(可重读) 并不会有任何性能损失。
I>nnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。