[TOC]
事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
MySQL 默认采用自动提交模式。也就是说,如果不显式使用START TRANSACTION语句来开始一个事务,那么每个查询都会被当做一个事务自动提交
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
数据库在事务执行前后都保持一致性状态。
在一致性状态下,所有事务对一个数据的读取结果都是相同的。
一个事务所做的修改在最终提交以前,对其它事务是不可见的。
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
可以通过数据库备份和恢复来实现,在系统发生奔溃时,使用备份的数据库进行数据恢复。
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
- 只有满足一致性,事务的执行结果才是正确的。
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时要只要能满足原子性,就一定能满足一致性。
- 在并发的情况下,多个事务并发执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
- 事务满足持久化是为了能应对数据库奔溃的情况。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。
范式的包含关系。一个数据库设计如果符合第二范式,一定也符合第一范式。如果符合第三范式,一定也符合第二范式…
- 1NF :符合1NF的关系中的每个属性都不可再分
- 2NF:属性完全依赖于主键 [消除部分子函数依赖]
- 3NF:属性不依赖于其它非主属性[消除传递依赖]
- BCNF:在1NF基础上,任何非主属性不能对主键子集依赖[在3NF基础上消除对主码子集的依赖]
- 4NF:要求把同一表内的多对多关系删除。
- 5NF:从最终结构重新建立原始结构。
范式理论是为了解决以上提到四种异常。
高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
(针对未提交数据)如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以“看到”该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。 (脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。 )
e.g.
Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
而财务发现操作有误,回滚了事务,Mary的工资又变为了1000
像这样,Mary记取的工资数8000是一个脏数据。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(同时操作,事务一分别读取事务二操作时和提交后的数据,读取的记录内容不一致。不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果。 )
例子:
(1)在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
con1 = getConnection();
select salary from employee empId ="Mary"; (2)在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
con2 = getConnection();
update employee set salary = 2000;
con2.commit(); (3)在事务1中,Mary 再次读取自己的工资时,工资变为了2000
//con1
select salary from employee empId ="Mary"; 在一个事务中前后两次读取的结果并不致,导致了不可重复读。
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。把数据库的事务隔离级别调整到REPEATABLE_READ
事务T1读取一条指定的Where子句所返回的结果集,然后T2事务新插入一行记录,这行记录恰好可以满足T1所使用的查询条件。然后T1再次对表进行检索,但又看到了T2插入的数据。 (和可重复读类似,但是事务T2的数据操作仅仅是插入和删除,不是修改数据,读取的记录数量前后不一致)
幻读的重点在于新增或者删除 (数据条数变化)
同样的条件, 第1次和第2次读出来的记录数不一样
例子:
目前工资为1000的员工有10人。 (1)事务1,读取所有工资为1000的员工(共读取10条记录 )
con1 = getConnection();
Select * from employee where salary =1000; (2)这时另一个事务向employee表插入了一条员工记录,工资也为1000
con2 = getConnection();
Insert into employee(empId,salary) values("Lili",1000);
con2.commit(); 事务1再次读取所有工资为1000的员工(共读取到了11条记录,这就像产生了幻读)
//con1
select * from employee where salary =1000; **解决办法:**如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。把数据库的事务隔离级别调整到SERIALIZABLE_READ
- 串行化(Serializable):所有事务一个接着一个的执行,这样可以避免幻读(phantom read),对于基于锁来实现并发控制的数据库来说,串行化要求在执行范围查询的时候,需要获取范围锁,如果不是基于锁实现并发控制的数据库,则检查到有违反串行操作的事务时,需回滚该事务。
- 可重复读(Repeated Read):所有被Select获取的数据都不能被修改,这样就可以避免一个事务前后读取不一致的情况。但是没有办法控制幻读,因为这个时候其他事务不能更改所选的数据,但是可以增加数据,因为强恶意事务没有范围锁
- 读已提交(Read Committed):被读取的数据可以被其他事务修改,这样可能导致不可重复读。也就是说,事务读取的时候获取读锁,但是在读完之后立即释放(不需要等事务结束),而写锁则是事务提交之后才释放,释放读锁之后,就可能被其他事务修改数据。该等级也是SQL Server默认的隔离等级
- 读未提交(Read Uncommitted):最低的隔离等级,允许其他事务看到没有提交的数据,会导致脏读
- 总结
- 四个级别逐渐增强,每个级别解决一个问题,每个级别解决一个问题,事务级别遇到,性能越差,大多数环境(Read committed 就可以用了)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 串行化 | NO | NO | NO |
| 可重复读 | NO | NO | YES |
| 读已提交 | NO | YES | YES |
| 读未提交 | YES | YES | YES |
在MySQL中有很多存储引擎,每种存储引擎大相径庭,那么又改如何选择呢?
MySQL 5.5以前的默认存储引擎是MyISAM, MySQL 5.5之后的默认存储引擎是InnoDB
| 特点 | MyISAN | BDB | Memory | InnoDB | Archive |
|---|---|---|---|---|---|
| 存储限制 | 没有 | 没有 | 有 | 64TB | 没有 |
| 事务安全 | 支持 | 支持 | |||
| 锁机制 | 表锁 | 页锁 | 表锁 | 行锁 | 行锁 |
| B树索引 | 支持 | 支持 | 支持 | 支持 | |
| 哈希索引 | 支持 | 支持 | |||
| 全文索引 | 支持 | ||||
| 集群索引 | 支持 | ||||
| 数据缓存 | 支持 | 支持 | |||
| 索引缓存 | 支持 | 支持 | 支持 | ||
| 数据可压缩 | 支持 | 支持 | |||
| 空间使用 | 低 | 低 | N/A | 高 | 非常低 |
| 内存使用 | 低 | 低 | 中等 | 高 | 低 |
| 批量插入的速度 | 高 | 高 | 高 | 低 | 非常高 |
| 支持外键 | 支持 |
MySQL 5.5版本之前的默认存储引擎,在5.0以前最大表存储空间最大4G,5.0以后最大256TB。
Myisam存储引擎由.myd(数据)和 .myi(索引文件)组成,.frm文件存储表结构(所以存储引擎都有)
特性
- 并发性和锁级别 (对于读写混合的操作不好,为表级锁,写入和读互斥)
- 表损坏修复
- myisam表支持的索引类型(全文索引)
- myisam支持表压缩(压缩后,此表为只读,不可以写入。使用myisampack压缩)
应用场景
- 没有事务
- 只读类应用(插入不频繁,查询非常频繁)
- 空间类应用(唯一支持空间函数的引擎)
- 做很多count 的计算
MySQL 5.5及之后版本的默认存储引擎
特性
- Innodb为事务性存储引擎
- 完全支持事物的ACID特性
- Redo log (实现事务的持久性) 和Undo log(为了实现事务的原子性,存储未完成事务log,用于回滚)
- Innodb支持行级锁
- 行级锁可以最大程度的支持并发
- 行级锁是由存储引擎层实现的
应用场景
- 可靠性要求比较高,或者要求事务
- 表更新和查询都相当的频繁,并且行锁定的机会比较大的情况。
两者比较
- 系统表空间:无法简单的收缩大小(这很恐怖,会导致ibdata1一直增大,即使删除了数据也不会变小)
- 独立表空间:可以通过optimize table 命令收缩系统文件
- 系统表空间:会产生I/O瓶颈(因为只有一个文件)
- 独立表空间:可以向多个文件刷新数据
总结
强烈建议:对Innodb引擎使用独立表空间(mysql5.6版本以后默认是独立表空间)
系统表转移为独立表的步骤(非常繁琐)
- 使用mysqldump导出所有数据库表数据
- 停止mysql服务,修改参数,并且删除Innodb相关文件
- 重启mysql服务,重建mysql系统表空间
- 重新导入数据
文件系统存储特点
- 数据以文本方式存储在文件中
.csv文件存储表内容.csm文件存储表的元数据,如表状态和数据量.frm存储表的结构
CSV存储引擎特点
- 以CSV格式进行数据存储
- 所有列必须都是不能为NULL
- 不支持索引
- 可以对数据文件直接编辑(其他引擎是二进制存储,不可编辑)
引用场景
- 作为数据交换的中间表
特性
- 以zlib对表数据进行压缩,磁盘I/O更少
- 数据存储在ARZ为后缀的文件中(表文件为
a.arz,a.frm) - 只支持insert 和 select 操作(不可以delete 和update,会提示没有这个功能)
- 只允许在自增ID列上加索引
应用场景
- 日志和数据采集类应用
特性
-
也称为HEAP存储引擎,所以数据保存在内存中(数据库重启后会导致数据丢失)
-
支持HASH索引(等值查找应选择HASH)和BTree索引(范围查找应选择)
-
所有字段都为固定长度,varchar(10) == char(10)
-
不支持BLOG和TEXT等大字段
-
Memory存储使用表级锁(性能可能不如innodb)
-
最大大小由
max_heap_table_size参数决定 -
Memory存储引擎默认表大小只有
16M,可以通过调整max_heap_table_size参数
应用场景
- 用于查找或是映射表,例如右边和地区的对应表
- 用于保存数据分析中产生的中间表
- 用于缓存周期性聚合数据的结果表
注意: Memory数据易丢失,所以要求数据可再生
特性
- 提供了访问远程mysql服务器上表的方法
- 本地不存储数据,数据全部放在远程服务器上
使用 Federated
默认是禁止的。如果需要启用,需要在启动时增加Federated参数
参考条件:
- 是否需要事务
- 是否可以热备份
- 崩溃恢复
- 存储引擎的特有特性
重要一点: 不要混合使用存储引擎
强烈推荐: Innodb
区别:
- MyISAM不支持外键,而InnoDB支持
- MyISAM是非事务安全型的,而InnoDB是事务安全型的。
- MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
- MyISAM支持全文类型索引,而InnoDB不支持全文索引。
- MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。
- MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。
- InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。
应用场景:
-
MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
-
InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT 或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。
INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。
FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。
FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。
VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。
MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。
能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
它与时区无关。
默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATATIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。
和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。
它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。
MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。
默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。
索引能够轻易将查询性能提升几个数量级。
对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构:
- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
- 一个节点中的 key 从左到右非递减排列;
- 如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
查找算法:首先在根节点进行二分查找,如果找到则返回对应节点的 data,否则在相应区间的指针指向的节点递归进行查找。
由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作以保持 B-Tree 性质。
与 B-Tree 相比,B+Tree 有以下不同点:
- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度);
- 内节点不存储 data,只存储 key;
- 叶子节点不存储指针。
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B Tree 作为索引结构,主要有以下两个原因:
(一)更少的检索次数
平衡树检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(logdN),其中 d 为每个节点的出度。
红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,因此检索的次数也就更多。
B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。
(二)利用计算机预读特性
为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。
操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,相邻的节点也能够被预先载入。
更多内容请参考:MySQL 索引背后的数据结构及算法原理
B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。
因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。除了用于查找,还可以用于排序和分组。
可以指定多个列作为索引列,多个索引列共同组成键。
B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。
如果不是按照索引列的顺序进行查找,则无法使用索引。
InnoDB 的 B+Tree 索引分为主索引和辅助索引。
主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。
InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
哈希索引能以 O(1) 时间进行查找,但是失去了有序性,它具有以下限制:
- 无法用于排序与分组;
- 只支持精确查找,无法用于部分查找和范围查找;
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
MyISAM 存储引擎支持空间数据索引,可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
必须使用 GIS 相关的函数来维护数据。
- 大大减少了服务器需要扫描的数据行数。
- 帮助服务器避免进行排序和创建临时表(B+Tree 索引是有序的,可以用来做 ORDER BY 和 GROUP BY 操作);
- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的数据都存储在一起)。
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
例如下面的查询不能使用 actor_id 列的索引:
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
SELECT film_id, actor_ id FROM sakila.film_actor
WHERE actor_id = 1 AND film_id = 1;
让选择性最强的索引列放在前面,索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
对于前缀长度的选取需要根据索引选择性来确定。
索引包含所有需要查询的字段的值。
具有以下优点:
- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
- Mysql的limit用法
- SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
- LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1)
- 最基本的分页方式:SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...
- 常用的数据库有哪些?Redis用过吗?
- 常用的数据库
- MySQL
- SQLServer
- Redis
- Redis是一个速度非常快的非关系型数据库,他可以存储键(key)与5种不同类型的值(value)之间的映射,可以将存储在内存中的键值对数据持久化到硬盘中。
- 与Memcached相比
- 两者都可用于存储键值映射,彼此性能也相差无几
- Redis能够自动以两种不同的方式将数据写入硬盘
- Redis除了能存储普通的字符串键之外,还可以存储其他4种数据结构,memcached只能存储字符串键
- Redis既能用作主数据库,由可以作为其他存储系统的辅助数据库
- Redis应用场景
- 缓存、任务队列、应用排行榜、网站访问统计、数据过期处理、分布式集群架构中的session分离
- Redis特点
- (1)高并发读写;(2)海量数据的高效存储和访问;(3)高可扩展性和高可用性
- 常用的数据库
- Redis的数据结构
- STRING:可以是字符串、整数或者浮点数
- LIST:一个链表,链表上的每个节点都包含了一个字符串
- SET:包含字符串的无序收集器(unordered collection),并且被包含的每个字符串都是独一无二、各不相同的
- HAST:包含键值对的无序散列表
- ZSET:字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定
- 索引的特点
- 可以加快数据库的检索速度
- 降低数据库插入、修改、删除等维护的速度
- 只能创建在表上,不能创建到视图上
- 既可以直接创建又可以间接创建
- 可以在优化隐藏中使用索引
- 使用查询处理器执行SQL语句,在一个表上,一次只能使用一个索引
- 索引的优点
- 创建唯一性索引,保证数据库表中每一行数据的唯一性
- 大大加快数据的检索速度,这是创建索引的最主要的原因
- 加速数据库表之间的连接,特别是在实现数据的参考完整性方面特别有意义
- 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
- 通过使用索引,可以在查询中使用优化隐藏器,提高系统的性能
- 索引的缺点
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
- 索引需要占用物理空间,除了数据表占用数据空间之外,每一个索引还要占一定的物理空间,如果建立聚簇索引,那么需要的空间就会更大
- 当对表中的数据进行增加、删除和修改的时候,索引也需要维护,降低数据维护的速度
- 索引分类
- 直接创建索引和间接创建索引
- 普通索引和唯一性索引
- 单个索引和复合索引
- 聚簇索引和非聚簇索引
- 索引失效
- 如果条件中有or,即使其中有条件带索引也不会使用(这就是问什么尽量少使用or的原因)
- 对于多列索引,不是使用的第一部分,则不会使用索引
- like查询是以%开头
- 如果列类型是字符串,那一定要在条件中使用引号引起来,否则不会使用索引
- 如果MySQL估计使用全表扫秒比使用索引快,则不适用索引。
- 各引擎支持索引
缺图
-
数据库中的索引结构
- 因为在使用二叉树的时候,由于二叉树的深度过大而造成I/O读写过于频繁,进而导致查询效率低下。因此采用多叉树结构。B树的各种操作能使B树能保持较低的高度。
-
B 树又叫平衡多路查找树,一棵 m 阶的 B 树的特性如下
- 树中每个结点最多含有 m 个孩子(m>=2);
- 除根结点和叶子结点外,其他每个结点至少有[ceil(m / 2)]个孩子(其中 ceil(x)是一个取上限的函数);
- 根结点至少有 2 个孩子(除非 B 树只包含一个结点:根结点);
- 所有叶子结点都出现在同一层, 叶子结点不包含任何关键字信息 (可以看做是外部结点或查询失败的结
点,指向这些结点的指针都为 null);(注:叶子节点只是没有孩子和指向孩子的指针,这些节点也存
在,也有元素。类似红黑树中,每一个 NULL 指针即当做叶子结点,只是没画出来而已)。
- 每个非终端结点中包含有 n 个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
a) Ki (i=1...n)为关键字,且关键字按顺序升序排序 K(i-1)< Ki。
b) Pi 为指向子树根的结点,且指针 P(i-1)指向子树种所有结点的关键字均小于 Ki,但都大于 K(i-1)。
c) 关键字的个数 n 必须满足: [ceil(m / 2)-1]<= n <= m-1。比如有 j 个孩子的非叶结点恰好有 j-1 个关
键码。
B+树
在什么情况下适合建立索引
- 为经常出现在关键字order by、group by、distinct后面的字段,建立索引。
- 在union等集合操作的结果集字段上,建立索引。其建立索引的目的同上。
- 为经常用作查询选择的字段,建立索引。
- 在经常用作表连接的属性上,建立索引。
- 考虑使用索引覆盖。对数据很少被更新的表,如果用户经常只查询其中的几个字段,可以考虑在这几个字段上建立索引,从而将表的扫描改变为索引的扫描。







