第一次写博客,不足之处,多谢指正。
什么是索引
索引是为了加速对表中数据行的检索而创建的一种分散的存储结构。索引是针对表而建立的,它是由数据页面以外的索引页面组成的,每个索引页面中的行都会含有逻辑指针,以便加速检索物理数据
索引的作用
在数据库系统中建立索引主要有以下作用:
-
快速取数据;
-
保证数据记录的唯一性;
-
实现表与表之间的参照完整性;
-
在使用ORDER by、group by子句进行数据检索时,利用索引可以减少排序和分组的时间。
索引的优缺点
优点
-
大大加快数据的检索速度;
-
创建唯一性索引,保证数据库表中每一行数据的唯一性;
-
加速表和表之间的连接;
-
在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
缺点
-
索引需要占物理空间。
-
当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。 MySQL索引是在存储引擎中实现的,而不是在服务器层中实现的。所以,每种存储引擎的索引都不一定完全相同,并不是所有的存储引擎都支持所有的索引类型。
MySQL支持的索引类型
-
B-Tree索引
-
Hash索引
-
全文索引
-
空间数据索引(R-Tree)
MySQL支持的存储引擎
-
MyISAM
-
InnoDB
-
MERGE
-
MEMORY
-
Archive等。
各存储引擎的比较
我们数据库用的存储引擎主要是InnoDB,而InnoDB使用B+Tree进行索引的存储,所以我们主要来研究下InnoDB存储引擎和B+Tree索引。
InnoDB存储引擎
InnoDB优点
InnoDB是一种可靠性高的高性能MySQl数据库存储引擎,主要优点包括:
-
它遵循ACID模式设计,具有与事务(Transactions),回滚和保护用户数据的崩溃恢复能力。
-
InnoDB 提供行级锁(Locking on row level),增加了多用户并发性和性能表现。
-
InnoDB表基于主键在磁盘上安排数据,优化了常见的查询功能。每一个InnoDB表都有一个叫做聚集索引的主键索引,能尽可能减少数据查询次数。
-
InnoDB支持外键完整性约束。
-
可以与其它MySQL存储引擎混合使用InnoDB表。
-
InnoDB是针对提高CPU效率而设计的,并且在处理大数据时表现最佳。
InnoDB存储结构
InnoDB存储引擎中的每张表必须得有主键,如果表在创建时没有显示定义主键,则根据以下原则自动创建主键:
-
如果有非空的唯一索引,则该索引所在的列为主键;
-
如果不符合上述条件,自动创建一个6个字节的指针为主键。
InnoDB存储引擎的逻辑存储结构从大到小分别为:表空间、段、区、页,它们的关系如下图所示:
InnoDB索引结构
InnoDB使用B+Tree的方式存储索引。为了更好的理解B+Tree索引,我们简单了解下 二叉树、B-树、B+树
二叉树
二叉树即二叉搜索树:
-
所有非叶子结点至多拥有两个儿子(Left和Right);
-
所有结点存储一个关键字;
-
非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;
B-树
B-树是一种多路搜索树(并不是二叉的):
-
定义任意非叶子结点最多只有M个儿子;且M>2;
-
根结点的儿子数为[2, M];
-
除根结点以外的非叶子结点的儿子数为[M/2, M];
-
每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
-
非叶子结点的关键字个数=指向儿子的指针个数-1;
-
非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
-
非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的 子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
-
所有叶子结点位于同一层。
特性
-
关键字集合分布在整颗树中;
-
任何一个关键字出现且只出现在一个结点中;
-
搜索有可能在非叶子结点结束;
-
其搜索性能等价于在关键字全集内做一次二分查找;
-
自动层次控制。
B+树
B+树是B-树的变体,也是一种多路搜索树:
-
其定义基本与B-树同,除了:
-
非叶子结点的子树指针与关键字个数相同;
-
非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
-
为所有叶子结点增加一个链指针;
-
所有关键字都在叶子结点出现;
特性
-
所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
-
不可能在非叶子结点命中;
-
非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
-
更适合文件索引系统。
B+Tree索引
Innodb的一个表可能包含多个索引,每个索引都使用B+树来存储。索引包括聚集索引和二级索引,聚集索引使用表的主键作为索引键,包含表的所有字段。二级索引只包含索引键和聚集索引键(主键)的内容,不包括其他字段。每一个索引都是一棵B+树,每棵B+树由很多页面组成,而每个页面大小一般为16K。 从B+树的组织结构来看,B树的页面可分为:
-
叶子节点:B树层次为0的页面,存储记录的所有内容
-
非叶子节点:B树层次大于0的页面,只存储索引键和页面指针。 一棵典型的B+树索引结构如下所示: 从上图可知,相同层次的页面是用一个双向链表连接起来的。一般情况下,从B+树的最左边叶子节点开始,一直向右扫描,就可以得到B+树的从小到大的所有数据。 因此,对于叶子节点,有如下特征:页内数据是按索引键排序的;页面的任一记录的索引键值不小于其左兄弟页面的任何记录。
int、timestamp,datetime类型的区别与优劣
int
-
占用4个字节
-
建立索引之后,查询速度快
-
条件范围搜索可以使用使用between
-
不能使用mysql提供的时间函数
结论 适合需要进行大量时间范围查询的数据表
timestamp
-
占用4个字节
-
允许为空值,但是不可以自定义值,所以为空值时没有任何意义。
-
TIMESTAMP值不能早于1970或晚于2037。这说明一个日期,例如'1968-01-01',虽然对于DATETIME或DATE值是有效的,但对于TIMESTAMP值却无效,如果分配给这样一个对象将被转换为0。
-
值以UTC格式保存( it stores the number of milliseconds)
-
时区转化 ,存储时对当前的时区进行转换,检索时再转换回当前的时区。
-
默认值为CURRENT_TIMESTAMP(),其实也就是当前的系统时间。
-
数据库会自动修改其值,所以在插入记录时不需要指定timestamp字段的名称和timestamp字段的值,你只需要在设计表的时候添加一个timestamp字段即可,插入后该字段的值会自动变为当前系统时间。
-
以后任何时间修改表中的记录时,对应记录的timestamp值会自动被更新为当前的系统时间。
结论 timestamp类型适合用来记录数据的最后修改时间,因为只要你更改了记录中其他字段的值,timestamp字段的值都会被自动更新。
datetime
-
占用8个字节
-
允许为空值,可以自定义值,系统不会自动修改其值。
-
实际格式储存(Just stores what you have stored and retrieves the same thing which you have stored.)
-
与时区无关(It has nothing to deal with the TIMEZONE and Conversion.)
-
不可以设定默认值,所以在不允许为空值的情况下,必须手动指定datetime字段的值才可以成功插入数据。
-
可以在指定datetime字段的值的时候使用now()变量来自动插入系统的当前时间。
结论 datetime类型适合用来记录数据的原始的创建时间,因为无论你怎么更改记录中其他字段的值,datetime字段的值都不会改变,除非你手动更改它。
MySql索引对NULL的处理
空值与NULL
-
空值是不占用空间的
-
MySQL中的NULL其实是占用空间的
尽量避免NULL: 应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,而且对表索引时不会存储NULL值的,所以如果索引的字段可以为NULL,索引的效率会下降很多。因为它们使得索引、索引的统计信息以及比较运算更加复杂。应该用0、一个特殊的值或者一个空串代替空值。
建立索引的几个原则
-
选择唯一性索引;
-
为经常需要排序、分组和联合操作的字段建立索引;
-
为常作为查询条件的字段建立索引
-
尽量使用数据量少的索引
-
限制索引的数目;
-
尽量选择区分度高的列作为索引[区分度的公式是count(distinct col)/count(*),表示字段不重复的比例];
-
尽量的扩展索引,不要新建索引。
使用索引的几个原则
-
索引列要保持“干净”,不要进行函数、算术运算或其他表达式运算;
-
尽量不用IN、NOT IN、OR 子句,会使索引失效;
-
不鼓励使用like操作,如果非使用不可,应like 'aaa%'而不是like '%aaa%',前者可以使用索引;
-
如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引;
-
最左前缀匹配原则。