転載アドレスします。https://mp.weixin.qq.com/s/HNnzAgUtBoDhhJpsA0fjKQ
世界で唯一の二つのことは、人々の心に衝撃を与えることができます。一つは私たちの心の中で高尚な道徳的水準であり、もう一つは、私たち以上の華麗な空です - [カント]
みなさん、こんにちは、今日は、私はあなたと一緒にいくつかのMySQL関連の知識を共有し、そしてこの記事では、私たちの仕事でいくつかの助けを与えることができることを望みます。
インタビューで、インタビュアーは、多くの場合、いくつかのデータベースの最適化を要求します。たとえば、次のクエリをスピードアップする方法。通常、彼らは一般的な答えのようなものです
-
インデックス付き
-
不要なフィールドを減らし、SQLを変更
-
限定
-
サブテーブルサブライブラリー
-
等
答えは非常に表面的です。インデックスがクエリをスピードアップすることができますので、まあ、ここではMySQLのInnoDBストレージエンジンの下にB +インデックスについて話しています。
MySQLのInnoDBエンジンでは、検索を高速化するために、あなたはインデックスを追加することができ、内容のテーブルには、ディレクトリ内をどのページに書籍の内容を検索するために、フィールドでの本のようです。
InnoDBは次のようにインデックスが要約されているサポートされています。
-
B +ツリーインデックス
-
フルテキストインデックス
-
ハッシュインデックス
この記事の著者は、既に述べたように、InnoDBのハッシュインデックスが適応され、ユーザーはここで議論し、ない介入することはできません、この記事では、B +ツリーインデックスに焦点を当てています。
「MySQLのInnoDBストレージエンジンアーキテクチャ - メモリ管理」
https://blog.csdn.net/nuoWei_SenLin/article/details/83034832
-B +ツリーデータ構造01
私は、我々はすべての大学、バイナリツリーとバランスの取れたバイナリツリーの過程で二分探索データ構造を学んだと思います。データの順序集合で、迅速にデータの複雑log2Nを取得するためにバイナリ検索を使用して、バランスの取れたバイナリツリーは、極端な場合ににバイナリ検索ツリーを解決するために、二分探索木をもとに進化していますアンケートリスト。B +木?のは、B +ツリーの構造を見てみましょう
データがリーフノードに大きいから順に格納されているB +ツリーで、図は、B +ツリーを誘導することができ、B +ツリーの高さZhekeは、4つのデータファンアウトを格納することができるそれらの各々は、2であります図5に示すように、最初のページインデックス層、第二層は、データページです。エッセンスデータベースB +ツリーインデックスは、一般的に2-4層、磁気ディスクIO操作だけで2-4時間を必要とする、非常に速く、インデックス内のデータを探しに限定されているB +ツリー達成するためのデータベースであり、B +木の高さです。
02B +ツリーインデックス
。クラスタ化インデックス
ユーザーがいない場合、それは自動的に6を作成し、最初のNULLでない主キーを選択するために、デフォルト一意のインデックスで指定された主キー、InnoDBのテーブルが表示されない場合はInnoDBエンジンでは、クラスタ化インデックス、通常は主キーを持っています主キーとして_rowidバイトサイズ。
図図からクラスタ化インデックスの模式図であり、我々が見ることができるが、ツリーを2層に分割され、第一の層は、同じ索引ページである、第二の層は、実データが格納されたデータ・ページです。我々はまた、データを格納するためのインデックスページを描画ではなく、第2層のデータページに保存されている実際のデータと実際のデータへのオフセット点、そのSQL文のインデックスヒット場合は、単にインデックスページをヒットすることができますインデックスページを通じて、実際のデータページを見つけ、データと。
思考:クラスタ化インデックスは、論理的に、それは二重にリンクされたリストによって維持され、各ページは二重にリンクされたリストを記録し銀行によって維持されているページからページへので、それは、連続的であり、連続物理的に格納されていません。なぜ二重リンクリスト??
これは、オフセットデータページを見つけるためにインデックスを通るように、なぜなら便宜範囲クエリのと選別され、または直接このリストの逆順トラバーサルにリストをトラバースするために、容易に逆の順序と範囲クエリでソートすることができます。そのようなものとして
select * from table where id>10 and id<1000;
b。セカンダリインデックス
別のインデックス、InnoDBのセカンダリインデックスは、セカンダリインデックスは、非クラスタ化インデックスと呼ばれています。セカンダリインデックスのために、葉は、ブックマークは、所望の行を検索する場所にはInnoDBを伝えるために使用されているノードは、キーが含まれている、だけでなく、いわゆる「ブックマーク」なものが含まれている葉に加えて、データ行のすべてが含まれていませんデータ、セカンダリインデックスのSQLクエリ2段階のプロセスを打つかのように、実際のブックマークは、クラスタ化インデックスに格納されるように:
1.インデックスページを探します
図2に示すように、ページデータによってインデックスページを見つけ、データページは、インデックスの集約値を含みます
3、クラスタ化インデックスを通しての行を見つけるために
したがって、二次インデックスは、インデックスとIO回以上一般的にクラスタ化されました。
一个很容易被DBA忽略的问题:如果一条SQL语句命中索引,B+树索引不能找到一个给定查询条件的具体行,只能找到被查询数据行所在的页,然后将这个数据读入内存,然后再内存中遍历所有行找到数据。另外,每一页大小为16k,每一页会包含多行,行与行之间是通过双向链表组织的,所以范围查询或者顺序倒序排序查询时,只需遍历链表就可以了。
03 索引的管理
方便测试,我们创建一张表t,并添加索引
create table t( a int primary key, b varchar(500), c int ); alter table t add key idx_b (b(100)); alter table t add key idx_a_c (a,c); alter table t add key idx_c (c);
表t,a字段是主键,b字段是字符串长度500,在b字段创建索引,索引名是idx_b,并且只对b的前100个字符创建索引,联合s索引idx_a_c,和索引idx_c;
通过命令可以查看某张表索引的创建情况
show index from tG;
我们来分析返回的信息
-
table:索引所在的表名
-
Non_unique:非唯一索引,我们可以看到primary key是0,代表非唯一索引
-
Key_name:索引的名字
-
Seq_in_index:索引中该列的位置,可以看索引idx_a_c就比较直观
-
Column_name:字段名字
-
Collation:一般都是A,此字段不重要
-
Cardinality:非常关键的一个字段,在下面细讲
-
Sub_part:是否是列的部分被索引,b字段长度500,我们只在b的前100长度上创建索引
-
Packed:不重要
-
Null:索引的列是否包含Null值
-
Index_type:索引类型,都是BTREE
-
Comment:注释
-
Index_comment:不重要
返回数据中,有一Cardinality字段,优化器会根据这个字段来选择是否使用这个字段,不过这个字段并不是实时更新的,如果实时更新,代价比较大,如果要更新Cardinality字段的值,可以使用如下命令
analyze table tG;
Cardinality字段代表什么意思呢?表示索引中不重复记录数量的预估值,Cardinality/count(*)的值尽可能接近1(几乎没有重复字段),如果这个比值很小接近0,表示该索引中这个字段的数据大部分都是重复的,那么用户可以考虑是否有必要创建这个索引。
那么InnoDB何时更新Cardinality的值呢?
如果每次更新操作都对Cardinality进行更新统计,那么代价是非常大的,因此InnoDB对Cardinality的更新策略如下:
-
表中1/16的数据已发生过变化
-
start_modified_counter>2000000000 #20亿
如果表中某一行数据频繁的更新,表中数据量没变,变化的只是这一行。
InnoDB如何统计Cardinality的值呢?
-
取得B+数叶子节点的数量,记作A
-
随机取得8个叶子节点,统计每页不同记录得个数,记作p1,p2...p8
Cardinality = (p1+p2+..+p8)*A/8,因为是随机取得8个叶子节点,所以暗示着每次计算出得Cardinality的值有可能不同。
让我们看一下,我们公司测服上的数据库的Cardinality值
在工作中排查过的一个慢查询:
笔者有一个好朋友,在公司遇到一个很简单的单表查询,sql大概是这样的
select * from tb where status=1 and shop_id=1;
这张表数据量并不大,只有14万条,status字段上有索引,而且sql语句很简单,但是查询结果却要将近20s,笔者查询status字段Cardinality值为2,非常小,并没有用到status字段的索引,导致扫描全表。
关于覆盖索引:
-
就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
-
如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫做覆盖索引。
-
当发起一个被索引覆盖的查询(也叫作索引覆盖查询)时,在EXPLAIN的Extra列可以看到“Using index”的信息
举个例子如下,建表t,a是主键,b和c中添加联合索引(b_c),并插入一些数据
create table t( a int primary key auto_increment, b int, c int, d int, key b_c (b,c) ); insert into t(b,c,d) values(1,1,1); insert into t(b,c,d) values(2,2,2); insert into t(b,c,d) values(3,3,3); insert into t(b,c,d) values(4,4,4); insert into t(b,c,d) values(5,5,5);
example1:我们看到,匹配到了主键,在Extra列中,出现Using index的字样;
example2:我们看到,匹配到了(b_c),覆盖索引,key是b_c,在Extra列中,出现Using index的字样
example3:虽然查询条件是b,但是查询到的字段没有b/c而是d,所以key是NULL,没有用到索引;
example4:返回字段b c d,查询条件是b,索引没有完全覆盖到返回的字段。
example5:没有覆盖到索引
example6:索引中就包含c列的值,只用到了覆盖索引,Extra字段有Using index的字样