索引是帮助MySQL高效获取数据的排好序的数据结构,相当于目录
每一个节点对应了一条数据,一个内存地址
二叉树(Binary Tree)是有限个节点的集合,这个集合可以是空集,也可以是一个根节点和两颗不相交的子二叉树组成的集合,其中一颗树叫根的左子树,另一颗树叫右子树。所以二叉树是一个递归地概念。
属于二叉树的一种,多了自平衡,层级会很高(树的深度会很深)交互会较频繁
而且每次自平衡,会有自身的一个重新排序,插入效率较低
红黑树的特性
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。[注意:这里叶子节点,是指为空(NIL)的虚节点!]
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
MyISAM索引文件和数据文件是分离的(非聚集)
一个文件专门存储索引在另一个文件中的位置,检索较慢
索引最左前缀原理:第一优先根据索引设置的时候字段顺序排序
EXPLAIN SELECT * FROM`user`WHERE id = 1
字段名 | 含义 |
---|---|
id | sql的执行顺序 |
select_type | 查询难度 |
table | 表名 |
type | const(常量)、eqref(通过主键索引查找)、ref(通过非主键索引查找)、range(范围查询)、ALL(全表扫描) |
possible_keys | 可能用到的索引 |
key | 真实用到的索引 |
key_len | 索引的长度(用来判断索引的效率) |
rows | 行(索引扫的行数) |
UUID:随机插入,所有索引在B+数中位置都需要重新排序,导致分裂,效率越来越慢,数据碎片化。
自增:在B+数的最后面存储,仅会影响到它的父节点。
设计表字段类型
不同的int有不同的表示范围
根据表对应的业务场景,主键自增的趋势,用所占磁盘空间最小的类型
数字:
id一般使用bigint,如果仅需要表示几种状态,可以用tinyint(表示范围更小,占得空间也越小)
除了varchar,其他类型设置长度其实没有意义,所占内存是定长的
文字:
char和varchar的区别:char为定长,varchar是可变的
针对大量的文字时使用text/longtest类型存储,优化策略:垂直分表,将Test字段单独分出去,关联主表
时间:
datetime:都可以显示时分秒,占用空间更多
timestamp:都可以显示时分秒,占用空间更少,缺陷有默认数据范围,只能存放1979-2050年的数据
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置从 is_xxx 到
Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含
义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、
字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合
表达习惯。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
说明:在存储的时候,float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的
结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数并分开存储。
说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。gmt_create, gmt_modified
的类型均为 datetime 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。Java 开发手册
正例:alipay_task / force_project / trade_config
正例:各业务线经常冗余存储商品名称,避免查询时需要调用 IC 服务获取。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
正例:无符号值可以避免误存负数,且扩大了表示范围。
对象 | 年龄区间 | 类型 | 字节 | 表示范围 |
---|---|---|---|---|
人 | 150 岁之内 | tinyint unsigned | 1 | 无符号值:0 到 255 |
龟 | 数百岁 | smallint unsigned | 2 | 无符号值:0 到 65535 |
恐龙化石 | 数千万年 | int unsigned | 4 | 无符号值:0 到约 43 亿 |
太阳 | 约 50 亿年 | bigint unsigned | 8 | 无符号值:0 到约 10 的 19 次方 |
说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,
即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
说明:即使双表 join 也要注意表索引、SQL 性能。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90%
以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无
法排序。
说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这
个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效
果,用 explain 的结果,extra 列会出现:using index。
说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当
offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL
改写。
正例:先快速定位需要获取的 id 段,然后再关联:
SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
说明:
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range
还低,与全表扫描是小巫见大巫。
正例:如果 where a=? and b=?,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where c>? and d=?
那么即使 c 的区分度更高,也必须把 d 放在索引的最前列,即建立组合索引 idx_d_c。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
正例:可以使用如下方式来避免 sum 的 NPE 问题:
SELECT IFNULL(SUM(column), 0) FROM table;
说明:NULL 与任何值的直接比较都为 NULL。
反例:在 SQL 语句中,如果在 null 前换行,影响可读性。
select * from table where column1 is null and column3 is not null;
而ISNULL(column)
是一个整体,简洁易懂。从性能数据上分析,ISNULL(column)
执行效率更快一些。
说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且
操作列在多个表中存在时,就会抛异常。
正例:
select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;
反例:在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出 1052 异常:Column ‘name’ in field list is ambiguous。
说明:
正例:
select t1.name from table_first as t1, table_second as t2 where t1.id=t2.id;
说明:
SELECT LENGTH("轻松工作");
返回为 12
SELECT CHARACTER_LENGTH("轻松工作");
返回为 4
如果需要存储表情,那么选择 utf8mb4 来进行存储,注意它与 utf8 编码的区别。
【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE
无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
缓冲池,也称BP。
由缓存数据页(Page)和对缓存数据页进行描述的控制块组成,
控制块中存储着对应缓存页的的所属的表空间、数据页的编号、以及对应缓存页在Buffer Pool中的地址等信息。
默认128M,以Page页为单位,Page页默认大小16K
控制块的大小约为数据页的5%,大概800字节
缓存表数据与索引数据,减少磁盘IO,提升性能。
Mysql中有一个哈希表数据结构,它使用表空间号+数据页号,作为一个key,然后缓冲页对应的控制块作为value。
BP底层采用链表数据结构管理Page。
在InnoDB访问表记录和索引时会在Page页中缓存,减少磁盘OP操作,提升效率。
管理dirty page,内部page按修改时间排序
InnoDB引擎为了提高处理效率,在每次修改缓存页后,并不是立刻把修改刷新到磁盘上,而是在未来的某个时间点进行刷新非操作,所以需要使用flush链表存储脏页,凡是被修改过的缓冲页对应的控制块都作为节点加入到flush链表
flush链表的结构与free链表的结构相似
写缓冲区,针对二级索引(辅助索引)页的更新优化措施。
在进行DML操作(更新操作)时,如果请求的辅助索引(二级索引)没有在缓冲池中时,
并不会立刻将磁盘页加载到缓冲池,而是在change Buffer记录缓冲变更,
等未 来数据被读取时,再将数据合并恢复到DB中。
LRU = Least Recently Used (最近最少使用):末尾淘汰法,新数据从链表头部加入,释放空间时从末尾淘汰。
最近被访问的数据,则其未来被访问的概率较大。
优点:
缺点:
如果该数据页在LRU链表中存在的时间超过1s,就将其移动到链表头部(指整个LRU链表的头部)
如果该数据页在LRU链表中存在的时间短于1s,其位置不变(由于全表扫描有一个特点,它对某个页的频繁访问总耗时会很短)
1s这个时间是由参数innodb_old_blocks_time控制的。
索引就是排好的序,帮助我们进行快速查找的数据结构。
一种将数据库中的记录按照特殊形式存储的数据结构。通过索引,能够显著的提高数据查询的效率,从而提升服务器的性能。
在经常需要搜索的列上创建索引,可以加快搜索的速度。
在作为主键的列上创建索引,强制该列的唯一性和组织表中数据的排列结构。
在经常用连接的列上,这些列主要是一些外键,可以加快连接的速度。
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的
在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度
复合索引一般不超过5个字段
最基本的索引类型,基于普通字段建立,没有任何限制。
CREATE INDEX <索引名称> ON TABLENAME (字段名);
ALTER TABLE TABLENAME ADD INDEX [索引名称](字段名);
CREATE TABLE TABLENAME ([...],INDEX [索引名称](字段名));
与普通索引类似,不同点:索引字段的值必须唯一,允许空值。
CREATE UNIQUE INDEX <索引名称> ON TABLENAME (字段名);
ALTER TABLE TABLENAME ADD UNIQUE INDEX [索引名称](字段名);
CREATE TABLE TABLENAME ([...],UNIQUE [索引名称](字段名));
一种特殊的唯一索引,不允许有空值。创建或修改时追加主键约束即可,每个表只能有一个主键。
CREATE TABLE TABLENAME ([...],PRIMARY KEY [索引名称](字段名));
ALTER TABLE TABLENAME ADD PRIMARY KEY [索引名称](字段名);
用户可以在多个列上建立索引,可以代替多个单一索引,所需开销更小。
CREATE INDEX <索引名称> ON TABLENAME (字段名1,字段名2...);
ALTER TABLE TABLENAME ADD INDEX [索引名称](字段名1,字段名2...);
CREATE TABLE TABLENAME ([...],INDEX [索引名称](字段名1,字段名2...));
注意事项:
查询操作在数据量较小时,可以使用like模糊查询,但是对于大量的文本数据检索,效率很低。如果使用全文索引,查询速度会比like快很多倍。
CREATE FULLTEXT INDEX <索引名称> ON TABLENAME (字段名);
ALTER TABLE TABLENAME ADD FULLTEXT [索引名称](字段名);
CREATE TABLE TABLENAME ([...],FULLTEXT KEY [索引名称](字段名));
全文索引方式有自然语言检索 IN NATURAL LANGUAGE MODE和布尔检索 IN BOOLEAN MODE两种
和常用的like模糊查询不同,全文索引有自己的语法格式,使用 match 和 against 关键字,比如
SELECT * FROM user WHERE MATCH(NAME) AGAINST('aabb');
-- * 表示通配符,只能在词的后面
SELECT * FROM user WHERE MATCH(NAME) AGAINST('aa*' IN BOOLEAN MODE );
注意事项:
全文索引必须在字符串、文本字段上建立。
全文索引字段必须在最小字符和最大字符之间才会有效。(innodb:3-84;myisam:4-84)
简称ICP,Mysql5.6版本推出,用于优化查询。
需求:查询user表中“名字是张开头的,年龄为10岁的所有记录”。
SELECT * FROM user WHERE name LIKE "张%" AND age = 10;
自适应Hash索引简称AHI,是InnoDB的三大特性之一,
另外两大特性是Buffer Pool简称BP、双写缓冲区(Doublewrite Buffer)。
自适应即我们不需要自己处理,当InnoDB引擎根据查询统计发现某一查询满足hash索引的数据结构特点,就会给其建立一个hash索引;
hash索引底层的数据结构是散列表(Hash表),其数据特点就是比较适合在内存中使用,自适应Hash索引存在于InnoDB架构中(不存在与磁盘架构中),见下面的架构图;
自适应hash索引只适合搜索等值的查询,如
SELECT * FROM TABLE WHERE index_col = 'xxx';
而对于其他查找类型,如范围查找,是不能使用的。
AHI是针对B+树Serch Path的优化,因此所有会涉及到Serch Path的操作,均可使用此Hash索引进行优化。
减少B+树从叶子结点–>根节点定位,可以根据索引的键值,快速定位。
根据索引键值(前缀)快速定位到叶子节点满足条件记录的Offset,减少了B+树Search Path的代价,将B+树从Root节点至Leaf节点的路径定位,优化为Hash index的快速查询。
InnoDB的自适应Hash索引是默认开启的,可以通过配置下面的参数设置进行关闭。
innodb_adaptive_hash_index = off;
自适应Hash索引使用分片进行实现的,分片数可以使用配置参数设置。
innodb_adaptive_hash_index_parts = 8;
设置了一个联合索引
通过使用索引覆盖type = index,并且extra = Using index,从全表扫描变成了全索引扫描。
InnoDB:支持事务和外键,具有安全性和完整性,适合大量insert和update操作。
MyISAM:不支持事务和外键,提供告诉存储和检索,适合大量select操作。
InnoDB:支持行级锁,锁定指定记录。基于索引来加锁实现。
MyISAM:支持表级锁,锁定整张表。
InnoDB:使用聚集索引(聚簇索引),索引和记录在一起存储。
MyISAM:使用非聚集索引(非聚簇索引),所以你和记录分开。
InnoDB:读写阻塞可以与隔离级别有关,可以采用多版本并发控制(MVCC)来支持高并发。
MyISAM:使用表锁,会导致写操作并发率低,读之间并不阻塞。
InnoDB:表对应两个文件,一个.frm表结构文件,一个.ibd数据文件。最大支持64TB。
MyISAM:表对应三个文件,一个.frm表结构文件,一个MYD表数据文件,一个.MYI索引文件。从MySQL5.0开始默认限制时256TB。
(1)第一范式:每个列都不可以再拆分;
(2)第二范式:非主键列完全依赖于主键,而不能只依赖主键的一部分;
(3)第三范式:非主键列只依赖于主键,而不依赖于其他非主键。
原子性:要么全部成功,要么全部失败。
一致性:数据库总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态。
隔离性:一个事务在完全提交之前,对其他事务是不可见的。
持久性:一旦事务提交了,那么就永远都不会改变了。
(1)脏读:一个事务对数据进行了修改,还未提交到数据库;另一个事务使用了未修改的数据,依据这个脏数据所做的操作可能是不正确的。
(2)丢失修改:两个事务同时访问并修改同一个数据,那么第一个事务修改的结果就会被丢失。
(3)不可重复读:在一个事务内多次读取同一数据。在这个事务结束之前,另一个事务进修改了数据,那么第一个事务两次读取的数据就会不一样了。
(4)幻读:发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据,在随后的查询中,第一个事务就会发现多了一些原本不存在的数据。
(1)读未提交
(2)读已提交
(3)可重复度
(4)可串行化
性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的
这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)