あなたは、インデックスとロック(ターン)でMySQLを開始したステップバイステップ

出典:  あなたは、インデックスやロックでMySQLを開始したステップバイステップ

 

指数

インデックス一般的なタイプ

インデックス一般的なタイプのハッシュインデックス、整然とした配列インデックスは、インデックスのバイナリツリー、テーブルをジャンプように、と。この記事では、デフォルトのMySQLのストレージエンジンについて説明したInnoDBインデックス構造を。

InnoDBはインデックス構造

多重化は、InnoDB内で探索木によって達成される- B +木インデックス構造を達成するために。B +ツリーであるのみリーフノードデータを格納し、及び全ての葉ノードは、リンクされたリストを形成しますそして、InnoDB内で維持することは二重にリンクされたリストです。

 

あなたは質問、バイナリツリーまたはBツリーを使用せずにB +木のなぜ使用を有することができますか?

まず、指定されたブロックにアクセスするために、ディスクにアクセスするために知っておく必要があり、そのブロックは、指定されたアクセスするのに必要なディスクの回転磁気腕の動きならば、比較的時間のかかるプロセスであり、木の高さの増加は、それはあなたがより多くをする必要があることを意味しますディスクアクセス時間なので、n型ツリーの使用がなります。B +ツリーを使用しているために使用されるB +ツリーは、リーフノードのリンクされたリストを利用することができるが、その都度再ルックアップ検索の時間範囲の間にBツリーを

あなたが複数のインデックスを追加するかもしれない、とInnDBがしますテーブルの構成ではB +ツリーインデックスを構築各インデックスのために保存されています

たとえば、今回は簡単なテストテーブルを作成しました

作成 テーブルテスト(
  ID INT  プライマリー キーINT  ない ヌル
  名前VARCHAR インデックス(A) エンジン= InnoDBは、

私たちのために2件のB +ツリーインデックスを構築しますInnDBこの時間

一つは、主キークラスタ化インデックス他方は、通常のインデックスセカンダリインデックス、そしてここで私は貼り付け直接MySQLのディスカッション(インデックス、ロック)マップ上の記事(私は描きたくないので、怠惰を...)

これは、補助リーフノードの上記インデックス値に見ることができる唯一の主キー値を格納され、そして主キー値にリーフノードクラスタ化インデックスは、全体のレコードに格納されています

戻る表へ

そこでここでは、と呼ばれる概念から出てくるのテーブルに戻って今回のように、我々はクエリ操作して、

select name from test where a = 30;

我们知道因为条件 MySQL 是会走 a 的索引的,但是 a 索引上并没有存储 name 的值,此时我们就需要拿到相应 a 上的主键值,然后通过这个主键值去走 聚簇索引 最终拿到其中的name值,这个过程就叫回表。

我们来总结一下回表是什么?MySQL在辅助索引上找到对应的主键值并通过主键值在聚簇索引上查找所要的数据就叫回表

索引维护

我们知道索引是需要占用空间的,索引虽能提升我们的查询速度但是也是不能滥用。

比如我们在用户表里用身份证号做主键,那么每个二级索引的叶子节点占用约20个字节,而如果用整型做主键,则只要4个字节,如果是长整型(bigint)则是8个字节。也就是说如果我用整型后面维护了4个g的索引列表,那么用身份证将会是20个g。

所以我们可以通过缩减索引的大小来减少索引所占空间

当然B+树为了维护索引的有序性会在删除,插入的时候进行一些必要的维护(在InnoDB中删除会将节点标记为“可复用”以减少对结构的变动)。

比如在增加一个节点的时候可能会遇到数据页满了的情况,这个时候就需要做页的分裂,这是一个比较耗时的工作,而且页的分裂还会导致数据页的利用率变低,比如原来存放三个数据的数据页再次添加一个数据的时候需要做页分裂,这个时候就会将现有的四个数据分配到两个数据页中,这样就减少了数据页利用率。

覆盖索引

上面提到了 回表,而有时候我们查辅助索引的时候就已经满足了我们需要查的数据,这个时候 InnoDB 就会进行一个叫 覆盖索引 的操作来提升效率,减少回表。

比如这个时候我们进行一个 select 操作

select id from test where a = 1;

这个时候很明显我们走了 a 的索引直接能获取到 id 的值,这个时候就不需要进行回表,我们这个时候就使用了 覆盖索引

简单来说 覆盖索引 就是当我们走辅助索引的时候能获取到我们所需要的数据的时候不需要再次进行回表操作的操作

联合索引

这个时候我们新建一个学生表

CREATE TABLE `stu` (
  `id` int(11) NOT NULL,
  `class` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `class_name` (`class`,`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

我们使用 class(班级号) 和 name 做一个 联合索引,你可能会问这个联合索引有什么用呢?我们可以结合着上面的 覆盖索引 去理解,比如这个时候我们有一个需求,我们需要通过班级号去找对应的学生姓名

select name from stu where class = 102;

这个时候我们就可以直接在 辅助索引 上查找到学生姓名而不需要再次回表。

总的来说,设计好索引,充分利用覆盖索引能很大提升检索速度

最左前缀原则

这个是以 联合索引 作为基础的,是一种联合索引的匹配规则。

这个时候,我们将上面的需求稍微变动一下,这时我们有个学生迟到,但是他在门卫记录信息的时候只写了自己的名字张三而没有写班级,所以我们需要通过学生姓名去查找相应的班级号。

select class from stu where name = '张三';

这个时候我们就不会走我们的联合索引了,而是进行了全表扫描

为什么?因为 最左匹配原则。我们可以画一张简单的图来理解一下。

我们可以看到整个索引设计就是这么设计的,所以我们需要查找的时候也需要遵循着这个规则,如果我们直接使用name,那么InnoDB是不知道我们需要干什么的。

 

当然最左匹配原则还有这些规则

  • 全值匹配的时候优化器会改变顺序,也就是说你全值匹配时的顺序和原先的联合索引顺序不一致没有关系,优化器会帮你调好。
  • 索引匹配从最左边的地方开始,如果没有则会进行全表扫描,比如你设计了一个(a,b,c)的联合索引,然后你可以使用(a),(a,b),(a,b,c) 而你使用 (b),(b,c),(c)就用不到索引了。
  • 遇到范围匹配会取消索引。比如这个时候你进行一个这样的 select 操作
select * from stu where class > 100 and name = '张三';

这个时候 InnoDB 就会放弃索引而进行全表扫描,因为这个时候 InnoDB 会不知道怎么进行遍历索引,所以进行全表扫描。

索引下推

我给你挖了个坑。刚刚的操作在 MySQL5.6 版本以前是需要进行回表的,但是5.6之后的版本做了一个叫 索引下推 的优化。

select * from stu where class > 100 and name = '张三';

如何优化的呢?因为刚刚的最左匹配原则我们放弃了索引,后面我们紧接着会通过回表进行判断 name,这个时候我们所要做的操作应该是这样的

但是有了索引下推之后就变成这样了,此时 "李四" 和 "小明" 这两个不会再进行回表。

因为这里匹配了后面的name = 张三,也就是说,如果最左匹配原则因为范围查询终止了,InnoDB还是会索引下推来优化性能。

一些最佳实践

哪些情况需要创建索引?

  • 频繁作为查询条件的字段应创建索引。
  • 多表关联查询的时候,关联字段应该创建索引。
  • 查询中的排序字段,应该创建索引。
  • 统计或者分组字段需要创建索引。

哪些情况不需要创建索引

  • 表记录少。
  • 经常增删改查的表。
  • 频繁更新的字段。
  • where 条件使用不高的字段。
  • 字段很大的时候。

其他

  • 尽量选择区分度高的列作为索引。
  • 不要对索引进行一些函数操作,还应注意隐式的类型转换和字符编码转换。
  • 尽可能的扩展索引,不要新建立索引。比如表中已经有了a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
  • 多考虑覆盖索引,索引下推,最左匹配。

全局锁

MySQL提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。

一般会在进行 全库逻辑备份 的时候使用,这样就能确保 其他线程不能对该数据库做更新操作

在 MVCC 中提供了获取 一致性视图 的操作使得备份变得非常简单,如果想了解 MVCC 可以参考我的另一篇文章 你真的懂MVCC吗?来手动实践一下?

表锁

MDL(Meta Data Lock)元数据锁

MDL锁用来保证只有一个线程能对该表进行表结构更改

怎么说呢?MDL分为 MDL写锁MDL读锁,加锁规则是这样的

  • 当线程对一个表进行 CRUD 操作的时候会加 MDL读锁
  • 当线程对一个表进行 表结构更改 操作的时候会加 MDL写锁
  • 写锁和读锁,写锁和写锁互斥,读锁之间不互斥

lock tables xxx read/write;

这是给一个表设置读锁和写锁的命令,如果在某个线程A中执行lock tables t1 read, t2 write; 这个语句,则其他线程写t1、读写t2的语句都会被阻塞。同时,线程A在执行unlock tables之前,也只能执行读t1、读写t2的操作。连写t1都不允许,自然也不能访问其他表。

这种表锁是一种处理并发的方式,但是在InnoDB中常用的是行锁

行锁

我们知道在5.5版本以前 MySQL 的默认存储引擎是 MyISAM,而 MyISAM 和 InnoDB 最大的区别就是两个

  • 事务
  • 行锁

其中行锁是我们今天的主题,如果不了解事务可以去补习一下。

其实行锁就是两个锁,你可以理解为 写锁(排他锁 X锁)和读锁(共享锁 S锁)

  • 共享锁(S锁):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。 也叫做读锁:读锁是共享的,多个客户可以同时读取同一个资源,但不允许其他客户修改。

  • 排他锁(X锁):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。也叫做写锁:写锁是排他的,写锁会阻塞其他的写锁和读锁。

而行锁还会引起一个一个很头疼的问题,那就是死锁

トランザクショントランザクションは、あなたがBはそれが所有するに至っように行を変更し、デッドロックを待つしたいライン101および100を修正したい行100プラス書き込みロック、ライン101プラス書き込みロック上のトランザクションB、オンの場合、トランザクションA、唯一のデッドロック検出および防止の顔で。

次のキー锁

MVCCと行ロックを解決することはできないファントムを読み取るのInnoDBと呼ばれるこの時間は使用し、問題をGAPロック(ロックギャップ)を有するもの、行ロックを形成するネクストキーロックファントム読み取りの問題を解決するために。

それはルールをロックしていますので、しかし、それはデータベースのロックの同時実行の数を減らすために、範囲の拡大につながっています。特定のロックルールを次のように

  • 基本ユニットは関節技とGAPロック行ネクストキーロックでロックされています。
  • 訪問中のオブジェクトをロックすることになりますして下さい。
  • インデックス上の同等のクエリは、インデックスのみが行ロックにするとき、ネクストキーロック縮重をロックします。
  • インデックスの同等のクエリ、右トラバースと最後の値が空白にすると、次のキーロックロック縮退同等の条件を満たしていないながら。
  • 値まで照会アクセスに一意索引の範囲は、最初の条件を満たしていません。

ここで読むより複雑なファントムを解決するために、MVCCのアイデアがあまりにも多くの検証を行うことではありません。

概要

MySQLのインデックスが、私はInnoDBが実際にB +ツリー構造でありながら、これらのベストプラクティスは、原則から実際にある、ベスト・プラクティスの多くを与えただけでなく、指数改良版を格納します。これらの便利な、あなたはそれを理解します。

MySQLのロックのために、主な局面は、InnoDBが実際され、ラインロックされ、MVCCならびにネクストキーロックがトランザクションの同時実行制御を実装するために行ロックを使用します

 

おすすめ

転載: www.cnblogs.com/myseries/p/11791259.html