mysql> show engines;
复制代码
Engine | Support | Comment | Transactions | XA | Savepoints |
---|---|---|---|---|---|
InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
MyISAM | YES | MyISAM storage engine | NO | NO | NO |
CSV | YES | CSV storage engine | NO | NO | NO |
ARCHIVE | YES | Archive storage engine | NO | NO | NO |
PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
其余引擎详情请移步:dev.mysql.com/doc/refman/…html
InnoDB: The default storage engine in MySQL 5.7. InnoDB is a transaction-safe (ACID compliant) storage engine for MySQL that has commit, rollback, and crash-recovery capabilities to protect user data. InnoDB row-level locking (without escalation to coarser granularity locks) and Oracle-style consistent nonlocking reads increase multi-user concurrency and performance. InnoDB stores user data in clustered indexes to reduce I/O for common queries based on primary keys. To maintain data integrity, InnoDB also supports FOREIGN KEY referential-integrity constraints. For more information about InnoDB, see Chapter 14, The InnoDB Storage Engine.mysql
innodb: mysql 5.7 中的默认存储引擎。innodb 是 mysql 的事务安全 (符合 acid) 存储引擎, 具备提交、回滚和崩溃恢复功能, 可保护用户数据。innodb 行级锁定 (不升级到更粗粒度锁) 和 oracle 类型一导致用非锁定读取,可提升多用户并发性和性能。innodb 将用户数据存储在汇集索引中, 以减小基于主键的常见查询的 I/O。为了保持数据完整性, innodb 还支持外键。有关 innodb 的详细信息, 请参阅第14章, inodb 存储引擎。算法
Hash indexes have somewhat different characteristics from those just discussed:
They are used only for equality comparisons that use the = or <=> operators (but are very fast). They are not used for comparison operators such as < that find a range of values. Systems that rely on this type of single-value lookup are known as “key-value stores”; to use MySQL for such applications, use hash indexes wherever possible.sql
哈希结构只适用于等值查询(但这样速度很是快)。哈希结构不支持顺序检索例如'<'、'>'、"between and"等,这种存储结构属于“键值”查询,符合这种需求能够考虑使用哈希索引。数据库
The optimizer cannot use a hash index to speed up ORDER BY operations. (This type of index cannot be used to search for the next entry in order.)express
优化器不能使用哈希索引来加快 order by 操做。(此类型的索引不能用于按顺序搜索下一个条目。数组
MySQL cannot determine approximately how many rows there are between two values (this is used by the range optimizer to decide which index to use). This may affect some queries if you change a MyISAM or InnoDB table to a hash-indexed MEMORY table.安全
mysql 不能大体肯定两个值之间有多少行 (范围优化器使用它来决定要使用哪一个索引)。若是将 MyISAM 或 InnoDB 表更改成哈希索引的内存表, 这可能会影响某些查询。bash
只有等值匹配才能适用哈希结构查询某一行。(而适用B-tree索引,最左前缀就能够用于查询。) 哈希表,优势就是查询快,缺点是范围查询效率很低(由于无序)。适用于等值查询。数据结构
A B-tree index can be used for column comparisons in expressions that use the =, >, >=, <, <=, or BETWEEN operators. The index also can be used for LIKE comparisons if the argument to LIKE is a constant string that does not start with a wildcard character.
一个B树索引能够适用于=、>、>=、<、<=、BETWEEN 等操做符。B树索引也能够用于LIKE比较,只有当LIKE的参数是一个字符串常量而且不以通配符开始才能够适用索引。 树结构,优势有序,而且多叉树能够减小磁盘I/O次数。
卫星数据:指索引元素所指向的数据记录。例如数据库中的某一行数据。
CREATE TABLE `r` (
`id` int NOT NULL primary key auto_increment,
`k` int not null,
`name` varchar(16),
index(k)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码
在表中插入(1,10,"张三"),(2,20,"李四"),(3,30,"王五")。则索引以下图
定义主键用主键做为聚簇索引。
没定义主键使用第一个惟一非空索引做为聚簇索引。
没定义主键,也没定义惟一索引,生成一个隐藏的列做为聚簇索引。
更多详情
基于主键索引和普通索引查询有什么区别?
在分析一个sql语句:select * from r where k between 8 and 22;
这个例子因为要查询的结果只有主键索引上面才有,因此不得不回表。那么如何避免回表?
若是sql语句是:select id from r where k between 8 and 22,因为这时只须要查询id值,而id值已经在k索引树上了,因此不须要回表查询,索引k已经覆盖了咱们的查询需求,称之为覆盖索引。
因为覆盖索引能够减小数的搜索次数,显著提升查询性能,因此使用覆盖索引是一个经常使用的优化手段。
场景:假设有一个市民表:
CREATE TABLE `citizen` (
`id` int(11) NOT NULL,
`id_card` varchar(32) DEFAULT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`ismale` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_card` (`id_card`),
KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB
复制代码
是否有必要建立身份证号和姓名的联合索引?
根据业务来看,若是有根据身份证号查询姓名的高频需求,能够考虑建立身份证号和姓名的联合索引,避免回表提升查询的效率。
select * from citizen where name = "张三" ;
复制代码
这个确定是能够用name索引的,若是要查询姓张的人,语句是
select * from citizen where name like '张%';
复制代码
这时也能够用上name的索引,查找到第一个以张开头的人,向后遍历直到不知足条件为止。
而若是要检索姓张,年龄10岁的男孩。
select * from tuser where name like '张%' and age=10 and ismale=1;
复制代码
这个在MySQL5.6之前是要根据查询到姓张的人开始一个一个回表去查询age是否知足10的,而5.6引入了索引下推优化(index condition pushdown),能够在遍历中,对索引中包含的字段先判断,过滤掉不知足的记录,减小回表次数。 如下两句可使用到索引。
1. SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%';
2. SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%';
复制代码
第一句只有'Patrick' <= key_col < 'Patricl'的行才会被筛选出来,而第二句只有'Pat' <= key_col < 'Pau' 会被筛选出来。
如下两句不会使用索引:
3. SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%';
4. SELECT * FROM tbl_name WHERE key_col LIKE other_col;
复制代码
第三句因为以通配符开始,不符合最左前缀原则,因此不能适用索引。第四句,因为LIKE的参数不是一个字符串常量,因此也不使用索引。
若是用 LIKE '%string%' 字符串长度超过3,会使用串匹配的BM算法提升查询效率。
另外,若是某一列有索引,若是值为空,使用where col_name IS NULL也是能够走索引的。
例如居民身份证号能够保证惟一,那么是否用身份证号当作主键建表?这里并太建议,根据上面介绍的聚簇索引和二级索引的结构以后,能够看出主键索引越长对于辅助索引创建须要更多的空间,另外对于聚簇索引,若是索引过长会致使主键索引树的高度变高,由于一个数据页默认是16k,主键索引越长则一个数据页能容纳的索引则越少。身份证号是18位,用字符串来存须要18个字节,而若是使用自增的long来作主键,则只有8个字节。另外一个好处就是自增主键能够保证插入只须要插入到数据页的队尾,不须要插入中间,而身份证号按照顺序排序有可能会插入中间位置,这样会致使数据页存满,数据页分裂等消耗。
场景一,根据邮箱登陆是一个广泛场景,若是邮箱不加索引则须要权标扫描,而若是加入全量索引则须要占用很大的空间。因为字符串索引支持最左前缀原则,则咱们能够这样建立索引:
alter table user add index index(email(5));
复制代码
这里设置email的最左前5个字符做为索引能够缩小范围,可是若是前5个字符可能重复的数据不少,好比zhangsan@XX.com、zhangsi@XX.com、zhangwu@XX.com、zhangliu@XX.com、zhangqi@XX.com都会搜索出来在遍历,区别度过小,在某字段简历索引的一个原则就是这个字段的区别度,如此创建索引区别度过小。因此应该取得区别度可接受的最左前缀。
select count(distinct email) as L from user;(查询总数)
复制代码
而后执行下列语句,来看每一个前缀长度索引的区别度,找一个可以接受的长度,好比你的要求是区别度大于95%,那么能够算一下多长的前缀符合你的要求,区别度=L(n)/L。
select
count(distinct left(email,4) as L4,
count(distinct left(email,5) as L5,
count(distinct left(email,6) as L6,
count(distinct left(email,7) as L7,
from user;
复制代码
select field_list from t where id_card = reverse('input_id_card_string');
复制代码
alter table t add id_card_crc int unsigned, add index(id_card_crc);
复制代码
查询时候用如下语句:
select field_list from t where id_card_crc=crc32('input_id_card_string') and id_card='input_id_card_string');
复制代码
这样能够先快速缩小结果集的范围,在根据结果集遍从来查询精确的身份证号,提升效率。
缺点:以上几种方式都不支持范围查询,能够本身根据业务场景本身选择合适的方式。
读未提交是指,一个事务还没提交时,它作的变动就能被别的事务看到。
读提交是指,一个事务提交以后,它作的变动才会被其余事务看到。
可重复读是指,一个事务执行过程当中看到的数据,老是跟这个事务在启动时看到的数据是一致的。
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,"读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
读取到其余事物未提交的结果。
在事物中无读到其余事物提交的UPDATE更新结果。
在事物中无读到其余事物提交的INSERT更新结果。
MySQL默认级别是可重复读,Oracel默认级别是提交读。
CREATE TABLE `r` (
`id` int NOT NULL primary key auto_increment,
`v` int not null
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into r values (1,1);
复制代码
事物A | 事物B |
---|---|
begin; | |
1, select * from r; | begin; |
update r set v=v+1 where id =1; | |
commit; | |
2, select * from r; | |
3, select * from r for update; | |
commit; |
其中1,2,3句id等于3的列 v的值都等于多少?
update r set v = 1 where id =1;
复制代码
事物A | 事物B |
---|---|
begin; | |
1, select * from r; | begin; |
update r set v=v+1 where id = 1; | |
commit; | |
2, select * from r; | |
update r set v=v+1 where id = 1; | |
3, select * from r; | |
commit; |
此时语句一、二、3查询结果中v的值是多少?
简单的select操做,属于快照读,不加锁。
select * from table where ?;
复制代码
特殊的读操做,插入/更新/删除操做,属于当前读,须要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
复制代码
全部以上的语句,都属于当前读,读取记录的最新版本。而且,读取以后,还须要保证其余并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其余的操做,都加的是X锁 (排它锁)。
为何并发读不须要加锁?
MVCC(参考高性能MySQL)
这里简单介绍一下MVCC(多版本并发控制),MVCC的实现是经过保存数据在某个时间点的快照来实现的。不一样存储引擎实现的方式也不一样。这里简单介绍一下InnoDB简化版行为来讲明MVCC是如何工做的。
InnoDB的MVCC是经过在每行记录后面保存的两个隐藏列来实现的。两个列一个保存了行的建立时间,另个一保存了行的过时时间。这里其实保存的并非具体时间,而是系统版本号(system version number)。每新开启一个事物,系统版本号都会自动递增,事物开始时刻的系统版本号会做为事物的版本号,用来和查询到的每行记录版本做为比较。
下面看下InnoDB, REPEATABLE READ隔离级别下MVCC是如何操做的。
a.查询版本号早于或等于当前事物版本的行数据。这样能够保证读取到的行,要么早于该事物已经存在,要么是本事物本身提交的。
b.行的删除版本要么未定义,要么大于当前事物的版本号。这样能够保证,当前事物读取到的行在事物开始以前是未被删除的。
为新插入的每一行数据保存当前系统版本号做为行版本号。
为删除的每一行保存当前系统版本号做为行删除标志。
a. 插入一行数据,保存当前系统版本号做为行版本号。 b. 同时添加当前的系统版本号做为原数据的删除标记。
持有行记录读锁的事物容许读取该行记录。
持有行记录写锁的事物容许更新活删除该行记录。
A record lock is a lock on an index record. For example, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; prevents any other transaction from inserting, updating, or deleting rows where the value of t.c1 is 10.
行锁是做用在索引记录上的。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;会防止其余事物对于t.c1=10的增、删、改。
Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking.
行锁老是锁索引记录的,尽管没有建立索引,对于没有建立索引的状况,InnoDB建立一个隐藏的聚簇索引并用该索引来实现行锁。
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.
间隙锁是做用于索引记录之间或第一条索引记录以前或最后一条索引记录以后的锁。
Gap locking is not needed for statements that lock rows using a unique index to search for a unique row.
间隙锁不会做用于使用惟一索引去检索惟一行记录的状况。
为何会有间隙锁?
防止幻读。(当前读)
delete from t where id = 10;
复制代码
对id = 10的记录加写锁 (走主键索引)。这个答案对吗?
多是对的也有多是错的,由于已知条件不足。缺乏下列条件:
1,id是不是主键?
2,id列若是不是主键,那么id上是否有索引?
3,id列上若是有二级索引,那么这个索引是不是惟一索引?
4,id=10的记录是否存在?
根据如下组合来判断加的锁:
看下面几个例子:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_k` (`k`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
复制代码
insert into t values(2,2),(6,6);
复制代码
MySQL5.7版本,InnoDB引擎,隔离级别是RR状况下,建立表t,其中id是主键,k是普通索引。
问题1:其中语句 一、二、3能够插入吗?
事物A | 事物B | 事物C | 事物D |
---|---|---|---|
begin; | |||
select * from t where k=4 for update; | |||
1, insert into t values (3,3); | |||
2, insert into t values (4,4); | |||
3, insert into t values (5,5); | |||
commit; |
是不能插入的,由于k是普通索引,因此会在索引 k 的B+树上 k=2 至 k=6 之间加上间隙锁,防止幻读。
问题2:其中语句 四、五、六、 7能够插入吗?
事物A | 事物B | 事物C | 事物D | 事物E |
---|---|---|---|---|
begin; | ||||
select * from t where k=4 for update; | ||||
4, insert into t values (1,2); | ||||
5, insert into t values (3,2); | ||||
6, insert into t values (5,6); | ||||
7, insert into t values (7,6); | ||||
commit; |
其中语句 4, 7 是能够插入的,而语句 5, 6 是不能插入的。
根据前面的加锁机制分析一下缘由:
首先,select * from t where k=4 for update,因为 k 是普通索引,因此在InnoDB引擎下,默认隔离级别RR状况下会加间隙锁防止新数据插入,防止幻读。加锁方式以下图:
间隙锁到底锁的是什么? 经过上面这个例子能够看出,其实间隙锁并无彻底锁住k=2和k=6记录的插入,也就是锁住的不是具体的值,而是全部可能插入新值k=4的位置,在本例中也就是(2,2)至(6,6)之间的位置,由于索引是有序排列的,因此k=4只能插入(2,2)至(6,6)之间。
什么状况下产生间隙锁?