解读mysql的索引和事务的正确姿式

1、索引是作什么的?

不少时候,当你的应用程序进行SQL查询速度很慢时,应该想一想是否能够建索引。mysql

大多数MySQL索引(PRIMARY KEY、UNIQUE、INDEX和FULLTEXT)在B树中存储。只是空间列类型的索引使用R-树,而且MEMORY表还支持hash索引。正则表达式

索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引能够大大加快查询的速度,这是由于使用索引后能够不用扫描全表来定位某行的数据,而是先经过索引表找到该行数据对应的物理地址而后访问相应的数据。算法

2、索引的优缺点

优点:能够快速检索,减小I/O次数,加快检索速度;根据索引分组和排序,能够加快分组和排序;sql

劣势:索引自己也是表,所以会占用存储空间,通常来讲,索引表占用的空间的数据表的1.5倍;索引表的维护和建立须要时间成本,这个成本随着数据量增大而增大;构建索引会下降数据表的修改操做(删除,添加,修改)的效率,由于在修改数据表的同时还须要修改索引表;数据库

3、索引的分类

常见的索引类型有:主键索引、惟一索引、普通索引、全文索引、组合索引bash

一、主键索引:即主索引,根据主键pk_clolum(length)创建索引,不容许重复,不容许空值;服务器

ALTER TABLE 'table_name' ADD PRIMARY KEY('id');
复制代码

二、惟一索引:用来创建索引的列的值必须是惟一的,容许空值网络

ALTER TABLE 'table_name' ADD UNIQUE('email');
复制代码

三、普通索引:用表中的普通列构建的索引,没有任何限制多线程

ALTER TABLE 'table_name' ADD INDEX index_name('description');
复制代码

四、全文索引:用大文本对象的列构建的索引(下一部分会讲解)并发

ALTER TABLE 'table_name' ADD FULLTEXT('content');
复制代码

五、组合索引:用多个列组合构建的索引,这多个列中的值不容许有空值

ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');
复制代码

遵循“最左前缀”原则,把最经常使用做为检索或排序的列放在最左,依次递减,组合索引至关于创建了col1,col1col2,col1col2col3三个索引,而col2或者col3是不能使用索引的。

在使用组合索引的时候可能由于列名长度过长而致使索引的key太大,致使效率下降,在容许的状况下,能够只取col1和col2的前几个字符做为索引

ALTER TABLE 'table_name' ADD INDEX index_name(col1(4),col2(3));
复制代码

表示使用col1的前4个字符和col2的前3个字符做为索引

4、索引的实现原理

MySQL支持诸多存储引擎,而各类存储引擎对索引的支持也各不相同,所以MySQL数据库支持多种索引类型,如BTree索引,B+Tree索引,哈希索引,全文索引等等,

一、哈希索引:

只有memory(内存)存储引擎支持哈希索引,哈希索引用索引列的值计算该值的hashCode,而后在hashCode相应的位置存执该值所在行数据的物理位置,由于使用散列算法,所以访问速度很是快,可是一个值只能对应一个hashCode,并且是散列的分布方式,所以哈希索引不支持范围查找和排序的功能。

二、全文索引:

FULLTEXT(全文)索引,仅可用于MyISAM和InnoDB,针对较大的数据,生成全文索引很是的消耗时间和空间。对于文本的大对象,或者较大的CHAR类型的数据,若是使用普通索引,那么匹配文本前几个字符仍是可行的,可是想要匹配文本中间的几个单词,那么就要使用LIKE %word%来匹配,这样须要很长的时间来处理,响应时间会大大增长,这种状况,就可以使用时FULLTEXT索引了,在生成FULLTEXT索引时,会为文本生成一份单词的清单,在索引时及根据这个单词的清单来索引。FULLTEXT能够在建立表的时候建立,也能够在须要的时候用ALTER或者CREATE INDEX来添加:

//建立表的时候添加FULLTEXT索引
CTREATE TABLE my_table(
id INT(10) PRIMARY KEY,
name VARCHAR(10) NOT NULL,
my_text text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
FULLTEXT(my_text));

//建立表之后,在须要的时候添加FULLTEXT索引
ALTER my_table ADD FULLTEXT ft_index(my_text);
CREATE INDEX ft_index ON my_table(my_text);
复制代码

对于较大的数据集,把数据添加到一个没有FULLTEXT索引的表,而后添加FULLTEXT索引的速度比把数据添加到一个已经有FULLTEXT索引的表快。

MySQL自带的全文索引只能用于MyISAM存储引擎,若是是其它数据引擎,那么全文索引不会生效。

在MySQL中,全文索引支队英文有用,目前对中文还不支持。

在MySQL中,若是检索的字符串过短则没法检索获得预期的结果,检索的字符串长度至少为4字节,此外,若是检索的字符包括中止词,那么中止词会被忽略。

三、BTree索引和B+Tree索引

BTree索引

BTree是平衡搜索多叉树,设树的度为d(d>1),高度为h,那么BTree要知足以一下条件:

每一个叶子结点的高度同样,等于h;

每一个非叶子结点由n-1个key和n个指针point组成,其中d<=n<=2d,key和point相互间隔,结点两端必定是key;

叶子结点指针都为null;

非叶子结点的key都是[key,data]二元组,其中key表示做为索引的键,data为键值所在行的数据;

BTree的结构以下:

image

在BTree的机构下,就可使用二分查找的查找方式,查找复杂度为h*log(n),通常来讲树的高度是很小的,通常为3左右,所以BTree是一个很是高效的查找结构。

B+Tree索引

B+Tree是BTree的一个变种,设d为树的度数,h为树的高度,B+Tree和BTree的不一样主要在于:

B+Tree中的非叶子结点不存储数据,只存储键值;

B+Tree的叶子结点没有指针,全部键值都会出如今叶子结点上,且key存储的键值对应的数据的物理地址;

B+Tree的结构以下:

image

通常来讲B+Tree比BTree更适合实现外存的索引结构,由于存储引擎的设计专家巧妙的利用了外存(磁盘)的存储结构,即磁盘的一个扇区是整数倍的page(页),页是存储中的一个单位,一般默认为4K,所以索引结构的节点被设计为一个页的大小,而后利用外存的“预读取”原则,每次读取的时候,把整个节点的数据读取到内存中,而后在内存中查找,已知内存的读取速度是外存读取I/O速度的几百倍,那么提高查找速度的关键就在于尽量少的磁盘I/O,那么能够知道,每一个节点中的key个数越多,那么树的高度越小,须要I/O的次数越少,所以通常来讲B+Tree比BTree更快,由于B+Tree的非叶节点中不存储data,就能够存储更多的key。

带顺序索引的B+TREE

不少存储引擎在B+Tree的基础上进行了优化,添加了指向相邻叶节点的指针,造成了带有顺序访问指针的B+Tree,这样作是为了提升区间查找的效率,只要找到第一个值那么就能够顺序的查找后面的值。

B+Tree的结构以下:

image

分析了MySQL的索引结构的实现原理,而后咱们来看看具体的存储引擎怎么实现索引结构的,MySQL中最多见的两种存储引擎分别是MyISAM和InnoDB,分别实现了非聚簇索引和聚簇索引。

首先要介绍几个概念,在索引的分类中,咱们能够按照索引的键是否为主键来分为“主索引”和“辅助索引”,使用主键键值创建的索引称为“主索引”,其它的称为“辅助索引”。所以主索引只能有一个,辅助索引能够有不少个。

MyISAM——非聚簇索引

MyISAM存储引擎采用的是非聚簇索引,非聚簇索引的主索引和辅助索引几乎是同样的,只是主索引不容许重复,不容许空值,他们的叶子结点的key都存储指向键值对应的数据的物理地址。

非聚簇索引的数据表和索引表是分开存储的。

非聚簇索引中的数据是根据数据的插入顺序保存。所以非聚簇索引更适合单个数据的查询。插入顺序不受键值影响。

只有在MyISAM中才能使用FULLTEXT索引。

最开始我一直不懂既然非聚簇索引的主索引和辅助索引指向相同的内容,为何还要辅助索引这个东西呢,后来才明白索引不就是用来查询的吗,用在那些地方呢,不就是WHERE和ORDER BY 语句后面吗,那么若是查询的条件不是主键怎么办呢,这个时候就须要辅助索引了。

InnoDB——聚簇索引

聚簇索引的主索引的叶子结点存储的是键值对应的数据自己,辅助索引的叶子结点存储的是键值对应的数据的主键键值。所以主键的值长度越小越好,类型越简单越好。

聚簇索引的数据和主键索引存储在一块儿。

聚簇索引的数据是根据主键的顺序保存。所以适合按主键索引的区间查找,能够有更少的磁盘I/O,加快查询速度。可是也是由于这个缘由,聚簇索引的插入顺序最好按照主键单调的顺序插入,不然会频繁的引发页分裂,严重影响性能。

在InnoDB中,若是只须要查找索引的列,就尽可能不要加入其它的列,这样会提升查询效率。

使用主索引的时候,更适合使用聚簇索引,由于聚簇索引只须要查找一次,而非聚簇索引在查到数据的地址后,还要进行一次I/O查找数据。

由于聚簇辅助索引存储的是主键的键值,所以能够在数据行移动或者页分裂的时候下降委会成本,由于这时不用维护辅助索引。可是辅助索引会占用更多的空间。

聚簇索引在插入新数据的时候比非聚簇索引慢不少,由于插入新数据时须要减压主键是否重复,这须要遍历主索引的全部叶节点,而非聚簇索引的叶节点保存的是数据地址,占用空间少,所以分布集中,查询的时候I/O更少,但聚簇索引的主索引中存储的是数据自己,数据占用空间大,分布范围更大,可能占用好多的扇区,所以须要更屡次I/O才能遍历完毕。

下图能够形象的说明聚簇索引和非聚簇索引的区别

image

5、索引的使用策略

何时要使用索引?

主键自动创建惟一索引;

常常做为查询条件在WHERE或者ORDER BY 语句中出现的列要创建索引;

做为排序的列要创建索引;

查询中与其余表关联的字段,外键关系创建索引

高并发条件下倾向组合索引;

何时不要使用索引?

常常增删改的列不要创建索引;

有大量重复的列不创建索引;

表记录太少不要创建索引;

在组合索引中不能有列的值为NULL,若是有,那么这一列对组合索引就是无效的;

在一个SELECT语句中,索引只能使用一次,若是在WHERE中使用了,那么在ORDER BY中就不要用了;

LIKE操做中,'%aaa%'不会使用索引,也就是索引会失效,可是‘aaa%’可使用索引;

在索引的列上使用表达式或者函数会使索引失效,例如:select * from users where YEAR(adddate)<2018,将在每一个行上进行运算,这将致使索引失效而进行全表扫描,所以咱们能够改为:select * from users where adddate<’2018-12-24′。

在查询条件中使用正则表达式时,只有在搜索模板的第一个字符不是通配符的状况下才能使用索引。

在查询条件中使用<>会致使索引失效。

在查询条件中使用IS NULL会致使索引失效。

在查询条件中使用OR链接多个条件会致使索引失效,这时应该改成两次查询,而后用UNION ALL链接起来。

尽可能不要包括多列排序,若是必定要,最好为这队列构建组合索引;

只有当数据库里已经有了足够多的测试数据时,它的性能测试结果才有实际参考价值。若是在测试数据库里只有几百条数据记录,它们每每在执行完第一条查询命令以后就被所有加载到内存里,这将使后续的查询命令都执行得很是快--无论有没有使用索引。只有当数据库里的记录超过了1000条、数据总量也超过了MySQL服务器上的内存总量时,数据库的性能测试结果才有意义。

6、索引的优化

一、最左前缀

索引的最左前缀和和B+Tree中的“最左前缀原理”有关,举例来讲就是若是设置了组合索引<col1,col2,col3>那么如下3中状况可使用索引:col1,<col1,col2>,<col1,col2,col3>,其它的列,好比<col2,col3>,<col1,col3>,col2,col3等等都是不能使用索引的。

根据最左前缀原则,咱们通常把排序分组频率最高的列放在最左边,以此类推。

二、带索引的模糊查询优化

在上面已经提到,使用LIKE进行模糊查询的时候,'%aaa%'不会使用索引,也就是索引会失效。若是是这种状况,只能使用全文索引来进行优化(上文有讲到)。

为检索的条件构建全文索引,而后使用

SELECT * FROM tablename MATCH(index_colum) ANGAINST(‘word’);
复制代码

事务介绍

首先,什么是事务?事务就是一段sql 语句的批处理,可是这个批处理是一个atom(原子),不可分割,要么都执行,要么回滚(rollback)都不执行。

MySQL 事务主要用于处理操做量大,复杂度高的数据。好比说,在人员管理系统中,你删除一我的员,你即须要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操做语句就构成一个事务!

  • 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。

  • 事务处理能够用来维护数据库的完整性,保证成批的 SQL 语句要么所有执行,要么所有不执行。

  • 事务用来管理 insert,update,delete 语句

通常来讲,事务是必须知足4个条件(ACID): Atomicity(原子性)、Consistency(稳定性)、Isolation(隔离性)、Durability(可靠性)

  • 一、事务的原子性:一组事务,要么成功;要么撤回。

  • 二、稳定性 :有非法数据(外键约束之类),事务撤回。

  • 三、隔离性:事务独立运行。一个事务处理后的结果,影响了其余事务,那么其余事务会撤回。事务的100%隔离,须要牺牲速度。

  • 四、可靠性:软、硬件崩溃后,InnoDB数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 选项 决定何时吧事务保存到日志里。

image

事务并发并不进行事务隔离形成的脏读、幻读、不可重复读

  • 脏读:事务A读到未提交事务B修改的数据,若是此时事务B中途执行失败回滚,那么此时事务A读取到的就是脏数据。好比事务A对money进行修改,此时事务B读取到事务A的更新结果,可是若是后面事务A回滚,那么事务B读取到的就是脏数据了。

  • 不可重复读:同一个事务中,对同一份数据读取的结果不一致。事务A在事务B对数据更新前进行读取,而后事务B更新提交,事务A再次读取,这时候两次读取的数据不一样。

  • 幻读:(同一个事务中,同一个查询屡次返回的结果不同。事务B查询表的记录数,而后事务A对表插入一条记录,接着事务B再次查询发现记录数不一样。注意这个解释是不正确,网络上有不少这样的解释,包括我认为比较权威的专家,可是通过实验发现并不正确。因此这是须要注意的)。能够作这样一个实验,事务A查询记录数,事务B插入一条记录(主键值为6),提交,而后事务A查询记录数,发现记录数没有改变,可是此时插入一条主键值为6的记录发现冲突了,感受像出现了幻觉。

区别

一、脏读和不可重复读:脏读是事务读取了还未提交事务的更新数据。不可重复读是同一个事务中,几回读取的数据不一样。

二、不可重复读和幻读的区别:都是在同一个事务中,前者是几回读取数据不一样,后者是几回读取数据总体不一样。

隔离级别

image

image

  • 隔离级别改变影响锁的周期

  • mysql支持上面4种隔离级别,默认为可重复读

image

image

MySQL有三种锁的级别:页级、表级、行级。

  MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);

  BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;

  InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认状况下是

采用行级锁。

MySQL这3种锁的特性可大体概括以下: 一、表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低。表级锁让多线程能够同时从数据表中读取数据,可是若是另外一个线程想要写数据的话,就必需要先取得排他访问(默认加排他表锁);(共享读锁(Table Read Lock)更新数据时,必需要等到更新完成了,其余线程才能访问(读)这个表。(独占写锁(Table Write Lock))

二、行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。

三、页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常。

原则上数据表有一个读锁时,其它进程没法对此表进行更新操做,但在必定条件下,MyISAM表也支持查询和插入操做的并发进行。

通常MyISAM引擎的表也支持查询和插入操做的并发进行(原则上数据表有一个读锁时,其它进程没法对此表进行更新操做)

MyISAM引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别能够为0、1或2:

a、concurrent_insert为0,不容许并发插入。 &emsp;&emsp;&emsp;&emsp;
b、concurrent_insert为1,若是MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM容许在一个进程读表的同时,另外一个进程从表尾插入记录。这也是MySQL的默认设置。 &emsp;&emsp;&emsp;&emsp;
c、concurrent_insert为2,不管MyISAM表中有没有空洞,都容许在表尾并发插入记录。
复制代码

若是有读写请求同时进行的话,MYSQL将会优先执行写操做。这样MyISAM表在进行大量的更新操做时(特别是更新的字段中存在索引的状况下),会形成查询操做很难得到读锁,从而致使查询阻塞。

咱们还能够调整MyISAM读写的优先级别:

&emsp;&emsp;a、经过指定启动参数low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利。
&emsp;&emsp;b、经过执行命令SET LOW_PRIORITY_UPDATES=1,使该链接发出的更新请求优先级下降。
&emsp;&emsp;c、经过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,下降该语句的优先级。
复制代码

MyISAM使用的是 flock 类的函数,直接就是对整个文件进行锁定(叫作文件锁定),MyISAM的数据表是按照单个文件存储的,能够针对单个表文件进行锁定;

InnoDB使用的是 fcntl 类的函数,能够对文件中局部数据进行锁定(叫作行锁定),InnoDB是一整个文件,把索引、数据、结构所有保存在 ibdata 文件里,因此必须用行锁定。

事物控制语句:

BEGIN或START TRANSACTION;显式地开启一个事务;     
COMMIT;也可使用COMMIT WORK,不过两者是等价的。
COMMIT会提交事务,并使已对数据库进行的全部修改称为永久性的;      
ROLLBACK;有可使用ROLLBACK WORK,不过两者是等价的。回滚会结束用户的事务,并撤销正在进行的全部未提交的修改;      
SAVEPOINT identifier;SAVEPOINT容许在事务中建立一个保存点,一个事务中能够有多个SAVEPOINT;     
RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;     
ROLLBACK TO identifier;把事务回滚到标记点;     
SET TRANSACTION;用来设置事务的隔离级别。
InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。
复制代码

MYSQL 事务处理主要有两种方法:

一、用 BEGIN, ROLLBACK, COMMIT来实现

BEGIN 开始一个事务     
ROLLBACK 事务回滚    
COMMIT 事务确认
复制代码

二、直接用 SET 来改变 My

SQL 的自动提交模式:

SET AUTOCOMMIT=0 禁止自动提交     
SET AUTOCOMMIT=1 开启自动提交
复制代码

注意点

一、若是事务中sql正确运行,后面没有commit,结果是不会更新到数据库的,因此须要手动添加commit。

二、若是事务中部分sql语句出现错误,那么错误语句后面不会执行。而咱们可能会认为正确操做会回滚撤销,可是实际上并无撤销正确的操做,此时若是再无错状况下进行一次commit,以前的正确操做会生效,数据库会进行更新。

相关文章
相关标签/搜索