MySQL | InnoDB | MyISAM |
---|---|---|
事物支持 | 是 | 否 |
外键支持 | 是 | 否 |
锁 | 行级 | 表级 |
全文索引 | 否 | 是【只支持英文】 |
崩溃安全恢复支持 | 是 | 否 |
选择:MyISAM相对简单,因此在效率上要优于InnoDB。若是系统插入和查询操做多,不须要事务和外键,选择MyISAM,若是须要频繁的更新、删除操做,或者须要事务、外键、行级锁的时候,选择InnoDB。java
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住知足条件的行,解决幻读须要锁表。mysql
事务隔离级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
读未提交(read-uncommitted) | 最低级别 | 是 | 是 | 是 |
读已提交(read-committed) | 语句级 | 否 | 是 | 是 |
可重复读(repeatable-read) | 事务级 | 否 | 否 | 是 |
串行化(serializable) | 最高级别,事务级 | 否 | 否 | 否 |
①. 优点:ios
②. 劣势:程序员
③. 加锁方式:面试
自动加锁。sql
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及的数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁;固然咱们也能够显示的加锁: 加共享锁:select from tableName where ... lock in share mode 加排他锁:select from tableName where ... for update数据库
④. 间隙锁【Next-Key锁】:编程
当咱们用范围条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫作"间隙(GAP)"。InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。数组
危害(坑):若执行的条件范围过大,则InnoDB会将整个范围内全部的索引键值所有锁定,很容易对性能形成影响。缓存
Transaction-A mysql> update innodb_lock set k=66 where id >=6; Query OK, 1 row affected (0.63 sec) mysql> commit; Transaction-B mysql> insert into innodb_lock (id,k,v) values(7,'7','7000'); Query OK, 1 row affected (18.99 sec)
⑤. 排他锁:
排他锁,也称写锁,独占锁,当前写操做没有完成前,它会阻断其余写锁和读锁。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4000 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4001' where id=4; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.04 sec) # Transaction_B mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (9.53 sec)
⑥. 共享锁:
共享锁,也称读锁,多用于判断数据是否存在,多个读操做能够同时进行而不会互相影响。若是事务对读锁进行修改操做,极可能会形成死锁。以下图所示。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; Query OK, 1 row affected (31.29 sec) Rows matched: 1 Changed: 1 Warnings: 0 # Transaction_B mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
⑦. 分析行锁定:
经过检查InnoDB_row_lock 状态变量分析系统上的行锁的争夺状况,命令:
show status like 'innodb_row_lock%'
mysql> show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+
innodb_row_lock_current_waits: 当前正在等待锁定的数量 innodb_row_lock_time: 从系统启动到如今锁定总时间长度;【很是重要的参数】 innodb_row_lock_time_avg: 每次等待所花平均时间;【很是重要的参数】 innodb_row_lock_time_max: 从系统启动到如今等待最长的一次所花的时间; innodb_row_lock_waits: 系统启动后到如今总共等待的次数;很是重要的参数,直接决定优化的方向和策略。
⑧. 行锁优化:
①. 优点:
②. 劣势:
③. 加锁方式:
自动加锁。
查询操做(SELECT),会自动给涉及的全部表加读锁,更新操做(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。也能够显示加锁:
共享读锁:lock table tableName read; 独占写锁:lock table tableName write; 批量解锁:unlock tables;
④. 共享读锁:
对MyISAM表的读操做(加读锁),不会阻塞其余进程对同一表的读操做,但会阻塞对同一表的写操做。只有当读锁释放后,才能执行其余进程的写操做。
Transaction-A mysql> lock table myisam_lock read; Query OK, 0 rows affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> update myisam_lock set v='1001' where k='1'; ERROR 1099 (HY000): Table 'myisam_lock' was locked with a READ lock and can't be updated mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; 8 rows in set (0.01 sec) mysql> update myisam_lock set v='1001' where k='1'; Query OK, 1 row affected (18.67 sec)
⑤. 独占写锁:
对MyISAM表的写操做(加写锁),会阻塞其余进程对同一表的读和写操做,只有当写锁释放后,才会执行其余进程的读写操做。
Transaction-A mysql> set autocommit=0; Query OK, 0 rows affected (0.05 sec) mysql> lock table myisam_lock write; Query OK, 0 rows affected (0.03 sec) mysql> update myisam_lock set v='2001' where k='2'; Query OK, 1 row affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> update innodb_lock set v='1001' where k='1'; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (42.83 sec)
小结:表锁,读锁会阻塞写,不会阻塞读。而写锁则会把读、写都阻塞。
⑥. 查看加锁状况:
show open tables; 1表示加锁,0表示未加锁。
mysql> show open tables where in_use > 0; +----------+-------------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------------+--------+-------------+ | lock | myisam_lock | 1 | 0 | +----------+-------------+--------+-------------+
⑦. 分析表锁定:
经过检查table_locks_waited 和 table_locks_immediate 状态变量分析系统上的表锁定,命令:
show status like 'table_locks%';
mysql> show status like 'table_locks%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Table_locks_immediate | 104 | | Table_locks_waited | 0 | +----------------------------+-------+
此外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合作写为主的存储引擎。由于写锁后,其余线程不能作任何操做,大量的更新会使查询很可贵到锁,从而形成永久阻塞。
InnoDB默认采用行锁,在未使用索引字段查询时升级为表锁。MySQL这样设计并非给你挖坑。它有本身的设计目的。 即使你在条件中使用了索引字段,MySQL会根据自身的执行计划,考虑是否使用索引(因此explain命令中会有possible_key 和 key)。若是MySQL认为全表扫描效率更高,它就不会使用索引,这种状况下InnoDB将使用表锁,而不是行锁。所以,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。
咱们拿出一本新华字典,它的目录实际上就是一种索引:非汇集索引。咱们能够经过目录迅速定位咱们要查的字。而字典的内容部分通常都是按照拼音排序的,这实际上又是一种索引:汇集索引。汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。
①、mysql主要使用B+树来构建索引,为何不用二叉树和红黑树?
因磁盘I/O效率低下,mysql为了尽可能减小磁盘IO的存取次数,须要利用了磁盘存取的局部性原理进行磁盘预读。
在InnoDB里,每一个页默认16KB,假设索引的是8B的long型数据,每一个key后有个页号4B,还有6B的其余数据(参考《MySQL技术内幕:InnoDB存储引擎》P193的页面数据),那么每一个页的扇出系数为16KB/(8B+4B+6B)≈1000,即每一个页能够索引1000个key。在高度h=3时,s=1000^3=10亿!!也就是说,InnoDB经过三次索引页的I/O,便可索引10亿的key。一般来讲,索引树的高度在2~4。
②. B+Tree与B-Tree的区别
M阶B-Tree
定义:
特性:
M阶B+Tree
定义:
特性:
③. B+Tree与B-Tree的区别
④. 为何mysql的索引使用B+树而不是B树呢?
MySQL自身常见的性能问题有磁盘空间不足,磁盘I/O太大,服务器硬件性能低。
使用explain关键字能够模拟优化器执行sql查询语句,从而得知MySQL 是如何处理sql语句。
①. id
select 查询的序列号,包含一组能够重复的数字,表示查询中sql语句的执行顺序。通常有三种状况: 第一种:id所有相同,sql的执行顺序是由上至下; 第二种:id所有不一样,sql的执行顺序是根据id大的优先执行(若是是子查询,id的序号会递增); 第三种:id既存在相同,又存在不一样的。先根据id大的优先执行,再根据相同id从上至下的执行。
②. select_type
select 查询的类型,主要是用于区别普通查询,联合查询,嵌套的复杂查询:
subquery和union 还能够被标记为dependent和uncacheable。 dependent意味着select依赖于外层查询中发现的数据。 uncacheable意味着select中的某些特性阻止结果被缓存于一个item_cache中。
③. table
查询结果来自于哪一个表。
④. partitions
表所使用的分区,若是要统计十年公司订单的金额,能够把数据分为十个区,每年表明一个区。这样能够大大的提升查询效率。
⑤. type
这是一个很是重要的参数,链接类型,常见的有:all , index , range , ref , eq_ref , const , system , null 八个级别。
性能从最优到最差的排序:null > system > const > eq_ref > ref > range > index > all
对java程序员来讲,若保证查询至少达到range级别或者最好能达到ref则算是一个优秀而又负责的程序员。
explain select * from t1 where name='yayun';
explain select t1.name from t1, t2 where t1.id=t2.id;
explain select * from t1 where id = 1
explain select * from t1 where id = (select min(id) from t2);
⑥. possible_keys
显示查询语句可能用到的索引(即查询涉及字段中存在索引的字段,可能为一个、多个或为null),不必定被查询实际使用,仅供参考使用。
⑦. key
显示查询语句实际使用的索引字段。若为null,则表示没有使用索引。
⑧. key_len
显示索引中使用的字节数,可经过key_len计算查询中使用的索引长度。
在不损失精确性的状况下索引长度越短越好。key_len 显示的值为索引字段的最可能长度,并不是实际使用长度,即key_len是根据表定义计算而得,并非经过表内检索出的。
⑨. ref
表示上述表的链接匹配条件,即哪些列或常量被用于查找索引列上的值。即显示使用哪一个列或常数与key一块儿从表中选择行。
**⑩. rows
根据表统计信息及索引选用状况,大体估算出找到所需的记录所须要读取的行数,值越大越很差。
即根据查询语句及索引选用状况,大体估算出要获得查询结果,所须要在表中读取的行数。
⑪. filtered
一个百分比的值,和rows 列的值一块儿使用,能够估计出查询执行计划(QEP)中的前一个表的结果集,从而肯定join操做的循环次数。小表驱动大表,减轻链接的次数。
⑫. extra
包含不适合在其余列中显示但又十分重要的额外信息。
经过explain的参数介绍,咱们能够得知:
可从如下几个方面对数据库性能进行优化:
①. 数据库设计
等。
②. 索引设计
③. 总结
就一句话:使用合适的数据类型,选择合适的索引:
使用合适的数据类型
选择合适的索引列(即哪些列适合添加索引)
①. 分区
把一张表的数据分红N个区块,在逻辑上看最终只是一张表,但底层是由N个物理区块组成的,经过将不一样数据按必定规则放到不一样的区块中提高表的查询效率。
②. 分表
③. 分库
面对高并发的读写访问,当数据库没法承载写操做压力时,无论如何扩展slave服务器,此时都没有意义了。所以需对数据库进行拆分,从而提升数据库写入能力,这就是分库。
④. 问题
对于不少的数据库系统都可以缓存执行计划,对于彻底相同的sql, 可使用已经已经存在的执行计划,从而跳过解析和生成执行计划的过程。MYSQL提供了更为高级的查询结果缓存功能,对于彻底相同的SQL (字符串彻底相同且大小写敏感) 能够执行返回查询结果。
MySQL缓存机制简单的说就是缓存sql文本及查询结果,若是运行相同的sql,服务器直接从缓存中取到结果,而不须要再去解析和执行sql。若是表更改了,那么使用这个表的全部缓冲查询将再也不有效,查询缓存值的相关条目被清空。更改指的是表中任何数据或是结构的改变,包括INSERT、UPDATE、 DELETE、TRUNCATE(截断)、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改变了的表的使用MERGE表的查询。显然,这对于频繁更新的表,查询缓存是不适合的,而对于一些不常改变数据且有 大量相同sql查询的表,查询缓存会节约很大的性能。
问:有个表特别大,字段是姓名、年龄、班级,若是调用select * from table where name = xxx and age = xxx该如何经过创建索引的方式优化查询速度?
答:因为mysql查询每次只能使用一个索引,若是在name、age两列上建立复合索引的话将带来更高的效率。若是咱们建立了(name, age)的复合索引,那么其实至关于建立了(name)、(name, age)两个索引,这被称为最佳左前缀特性。所以咱们在建立复合索引时应该将最经常使用做限制条件的列放在最左边,依次递减。其次还要考虑该列的数据离散程度,若是有不少不一样的值的话建议放在左边,name的离散程度也大于age。
问:max(xxx)如何用索引优化?
答:在xxx列上创建索引,由于索引是B+树顺序排列的,锁在下次查询的时候就会使用索引来查询到最大的值是哪一个。
问:如何对分页进行优化?
答:SELECT * FROM big_table order by xx LIMIT 1000000,20,这条语句会查询出1000020条的全部数据而后丢弃掉前1000000条,为了不全表扫描的操做,在order by的列上加索引就能经过扫描索引来查询。可是这条语句会查询仍是会扫描1000020条,还能改进成select id from big_table where id >= 1000000 order by xx LIMIT 0,20,用ID做为过滤条件将不须要查询的数据直接去除。
针对Java程序员,笔者最近整理了一份完整的一线互联网大厂面试真题,包含了Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分布式、高并发、性能调优、微服务等架构技术。
须要的朋友请点击下方传送门免费领取
如下是部分面试题截图