导图社区 Mysql数据库
Mysql数据库知识梳理,包括数据类型、三大泛式、索引、存储、事务、锁等等,需要的自取。
编辑于2022-11-16 01:17:49 河南Mysql数据库
概念
什么是数据库? 数据库是“按照数据结构组织,存储和管理数据库的仓库。是一个长期储存在计算机中的,有组织的,可共享的,统一管理的大量数据的集合
数据类型
三大泛式
第一范式(1NF)
字段(或属性)是不可被分割的最小单元.也不会有重复的列,, 提现了原子性
第二范式(2NF)
满足 1NF 前提下,存在一个候选码,非主属性全部依赖该候选码,即存在主键,体现唯一性,专业术语则是消除部分函数依赖
第三范式(3NF)
满足 2NF 前提下,非主属性必须互不依赖,消除传递依赖
范式和反范式
范式
范式 符合某一种级别的关系模式的集合。 构造数据库必须遵循一定的规则。在关系数据库中,这种规则就是范式
优点
范式化的表减少了数据冗余,数据表更新操作快、占用存储空间少。
缺点
查询时通常需要多表关联查询,更难进行索引优化
反范式
优点
反范式的过程就是通过冗余数据来提高查询性能,可以减少表关联和更好进行索引优化
缺点
存在大量冗余数据,并且数据的维护成本更高
索引
概念
索引是对数据库表中一列或多列的值进行排序的数据结构,用于快速访问数据库表中的特定信息
分类
从物理结构上分
聚簇索引
索引的键值的逻辑顺序和表中相应行的物理顺序保存一致 即每张表中只能有一个聚簇索引,相当于表中的主键索引
非聚簇索引
非聚簇索引的逻辑顺序与表中数据的物理顺序不保持一致
从应用分
普通索引
MySQL 中的基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了提高查询效率。 通过 ALTER TABLE table_name ADD INDEX index_name (column) 创建 通过alter table 表名 add index 索引名 (column 列)创建
唯一索引
索引列中的值必须是唯一的,但是允许为空值。 通过 ALTER TABLE table_name ADD UNIQUE index_name (column) 创建;
主键索引
特殊的唯一索引,也成聚簇索引,不允许有空值,并由数据库帮我们自动创建;
全文索引
只有在 MyISAM 引擎上才能使用。 同时只支持 CHAR、VARCHAR、TEXT 类型字段上使用。
组合索引
组合表中多个字段创建的索引,遵守最左前缀匹配规则
特点
优点
创建索引可以大大提高系统的性能。 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
缺点
1.创建和维护索引需要耗费时间,这种时间随着数据量的增加而增加,这样就降低了数据的维护速度。 2.索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。
索引存在问题
索引失效问题
查询条件包含or,可能导致索引失效 如何字段类型是字符串,where时一定用引号括起来,否则索引失效 like通配符可能导致索引失效。 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。 在索引列上使用mysql的内置函数,索引失效。 对索引列运算(如,+、-、*、/),索引失效。 索引字段上使用(!= 或者 \u003C >,not in)时,可能会导致索引失效。 索引字段上使用is null, is not null,可能导致索引失效。 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。 mysql估计使用全表扫描要比使用索引快,则不使用索引。
索引不适合场景
数据量少的不适合加索引 更新比较频繁的也不适合加索引 区分度低的字段不适合加索引(如性别)
索引潜规则
覆盖索引 回表 索引数据结构(B+树) 最左前缀原则 索引下推
索引设计原理
选择唯一性索引
唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
为常作为查询条件的字段建立索引
如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。
为经常需要排序、分组和联合操作的字段建立索引;
经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION 等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。
限制索引的数目
每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大,修改表时,对索引的重构和更新很麻烦。
小表不建议索引(如数量级在百万以内);
每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大,修改表时,对索引的重构和更新很麻烦。
尽量使用数据量少的索引
每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大,修改表时,对索引的重构和更新很麻烦。
删除不再使用或者很少使用的索引。
索引数据结构
HASH
1)Hash 进行等值查询更快,但无法进行范围查询。因为经过 Hash 函数建立索引之后,索引的顺序与原顺序无法保持一致,故不能支持范围查询。同理,也不支持使用索引进行排序。 2)Hash 不支持模糊查询以及多列索引的最左前缀匹配,因为 Hash 函数的值不可预测,如 AA 和 AB 的算出的值没有相关性。 3)Hash 任何时候都避免不了回表查询数据. 4)虽然在等值上查询效率高,但性能不稳定,因为当某个键值存在大量重复时,产生 Hash 碰撞,此时查询效率反而可能降低。
B+树索引(B+tree)
1)B+ 树本质是一棵查找树,自然支持范围查询和排序 2)在符合某些条件(聚簇索引、覆盖索引等)时候可以只通过索引完成查询,不需要回表。 3)查询效率比较稳定,因为每次查询都是从根节点到叶子节点,且为树的高度。
使用 B+ 树而非二叉查找树做索引原因
我们知道二叉树的查找效率为 O(logn),当树过高时,查找效率会下降。另外由于我们的索引文件并不小,所以是存储在磁盘上的。 文件系统需要从磁盘读取数据时,一般以页为单位进行读取,假设一个页内的数据过少,那么操作系统就需要读取更多的页,涉及磁盘随机 I/O 访问的次数就更多。将数据从磁盘读入内存涉及随机 I/O 的访问,是数据库里面成本最高的操作之一。 因而这种树高会随数据量增多急剧增加,每次更新数据又需要通过左旋和右旋维护平衡的二叉树,不太适合用于存储在磁盘上的索引文件。
使用 B+ 树而非 B 树做索引原因
为何使用 B+ 树而非 B 树做索引 B 树非叶子结点和叶子结点都存储数据,因此查询数据时,时间复杂度最好为 O(1),最坏为 O(log n)。而 B+ 树只在叶子结点存储数据,非叶子结点存储关键字,且不同非叶子结点的关键字可能重复,因此查询数据时,时间复杂度固定为 O(log n)。 B+ 树叶子结点之间用链表相互连接,因而只需扫描叶子结点的链表就可以完成一次遍历操作,B 树只能通过中序遍历。
B+ 树比 B 树更适合应用于数据库索引的原因
B+ 树减少了 IO 次数
由于索引文件很大因此索引文件存储在磁盘上,B+ 树的非叶子结点只存关键字不存数据,因而单个页可以存储更多的关键字,即一次性读入内存的需要查找的关键字也就越多,磁盘的随机 I/O 读取次数相对就减少了。
B+ 树查询效率更稳定
由于数据只存在在叶子结点上,所以查找效率固定为 O(log n),所以 B+ 树的查询效率相比B树更加稳定。
B+ 树更加适合范围查找
B+ 树叶子结点之间用链表有序连接,所以扫描全部数据只需扫描一遍叶子结点,利于扫库和范围查询;B 树由于非叶子结点也存数据,所以只能通过中序遍历按序来扫。也就是说,对于范围查询和有序遍历而言,B+ 树的效率更高。
最左匹配原则
顾名思义,最左优先,以最左边为起点任何连续的索引都能匹配上。同时遇到范围查询(>、\u003C、between、like)就会停止匹配。 如建立 (a,b,c,d) 索引,查询条件 b = 2 是匹配不到索引的,但是如果查询条件是 a = 1 and b = 2 或 a=1 又或 b = 2 and a = 1 就可以,因为优化器会自动调整 a,b 的顺序。 再比如 a = 1 and b = 2 and c > 3 and d = 4,其中 d 是用不到索引的,因为 c 是一个范围查询,它之后的字段会停止匹配。
覆盖索引
在 B+ 树的索引中,叶子节点可能存储了当前的键值,也可能存储了当前的键值以及整行的数据,这就是聚簇索引和非聚簇索引。 在 InnoDB 中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引。 当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。
索引下推
索引下推(Index condition pushdown) 简称 ICP,在 Mysql 5.6 版本上推出的一项用于优化查询的技术。 在不使用索引下推的情况下,在使用非主键索引进行查询时,存储引擎通过索引检索到数据,然后返回给 MySQL 服务器,服务器判断数据是否符合条件 而有了索引下推之后,如果存在某些被索引列的判断条件时,MySQL 服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合 MySQL 服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给 MySQL 服务器。 索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少 MySQL 服务器从存储引擎接收数据的次数。
存储
存储引擎
常见的存储引擎
MyISAM
InnoDB
BDB
MEMORY
MERGE
EXAMPLE
NDB Cluster
ARCHIVE
CSV
BLACKHOLE
FEDERATED
MyISAM 和 InnoDB 的区别
1)InnoDB 支持事务,而 MyISAM 不支持 2)InnoDB 支持外键,而 MyISAM 不支持。因此将一个含有外键的 InnoDB 表 转为 MyISAM 表会失败。 3)InnoDB 和 MyISAM 均支持 B+ Tree 数据结构的索引。但 InnoDB 是聚集索引,而 MyISAM 是非聚集索引。 4)InnoDB 不保存表中数据行数,执行 select count(*) from table 时需要全表扫描。而 MyISAM 用一个变量记录了整个表的行数,速度相当快(注意不能有 WHERE 子句)。 那为什么 InnoDB 没有使用这样的变量呢?因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的。 5)InnoDB 支持表、行(默认)级锁,而 MyISAM 支持表级锁。 nnoDB 的行锁是基于索引实现的,而不是物理行记录上。即访问如果没有命中索引,则也无法使用行锁,将要退化为表锁。 6)InnoDB 必须有唯一索引(如主键),如果没有指定,就会自动寻找或生产一个隐藏列 Row_id 来充当默认主键,而 Myisam 可以没有主键。
InnoDB 的四大特性
插入缓冲insert buffer)
二次写(double write)
自适应哈希索引(ahi)
预读(read ahead)
InnoDB 为何推荐使用自增主键
自增 ID 可以保证每次插入时 B+ 树索引是从右边扩展的,因此相比自定义 ID (如 UUID)可以避免 B+ 树的频繁合并和分裂。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差。
如何选择存储引擎
默认使用 InnoDB,MyISAM 适用以插入为主的程序,比如博客系统、新闻门户。
存储结构
什么是 InnoDB 的页、区、段
页page
首先,InnoDB 将物理磁盘划分为页(page),每页的大小默认为 16 KB,页是最小的存储单位。页根据上层应用的需要,如索引、日志等,分为很多的格式。我们主要说数据页,也就是存储实际数据的页。
区(Extent)
如果只有页这一个层次的话,页的个数是非常多的,存储空间的分配和回收都会很麻烦,因为要维护这么多的页的状态是非常麻烦的。 所以,InnoDB 又引入了区(Extent) 的概念。一个区默认是 64 个连续的页组成的,也就是 1MB。通过 Extent 对存储空间的分配和回收就比较容易了。
段Setment
为什么要引入段呢,这要从索引说起。我们都知道索引的目的是为了加快查找速度,是一种典型的用空间换时间的方法 B+ 树的叶子节点存放的是我们的具体数据,非叶子结点是索引页。所以 B+ 树将数据分为了两部分,叶子节点部分和非叶子节点部分,也就我们要介绍的段 Segment,也就是说 InnoBD 中每一个索引都会创建两个 Segment 来存放对应的两部分数据。 Segment 是一种逻辑上的组织,其层次结构从上到下一次为 Segment、Extent、Page。
页的数据结构
File Header
用于描述数据页的外部信息,比如属于哪一个表空间、前后页的页号等。
Page Header
用来描述数据页中的具体信息,比如存在多少条纪录,第一条纪录的位置等。
infimum 和 supremum 纪录
infimum 和 supremum 是系统生成的纪录,分别为最小和最大纪录值,infimum 的下一条是用户纪录中键值最小的纪录,supremum 的上一条是用户纪录中键值最大的纪录,通过 next_record 字段来相连
User Records
用户纪录,也就是数据库表中对应的数据,这里我们说常用的 Compact 格式。 InnoDB 除了我们插入的数据外,还有一些隐藏列,transaction_id(事务ID)、roll_pointer(回滚指针)是一定添加的。 row_id 则不一定,根据以下策略生成:优先使用用户建表时指定的主键,若用户没有指定主键,则使用unique键。若unique键都没有,则系统自动生成row_id,为隐藏列。
Free Space
页中目前空闲的存储,可以插入纪录。
Page Dictionary
类似于字典的目录结构,根据主键大小,每隔 4-8 个纪录设置一个槽,用来纪录其位置,当根据主键查找数据时,首先一步到位找到数据所在的槽,然后在槽中线性搜素。这种方法比从前到后遍历页的链表的效率更快。
Page Tailer
File Header存储刷盘前内存的校验和,Page Tailer储存刷盘后的校验和。当刷盘的时候,出现异常,Page Tailer和File Header中的校验和不一致,则说明出现刷盘错误。
页中插入记录的过程
1)如果 Free Space 的空间足够的话,直接分配空间来添加纪录,并将插入前最后一条纪录的 next_record 指向当前插入的纪录,将当前插入纪录的 next_record 指向 supremum 纪录。 2)如果 Free Space的 空间不够的话,则首先将之前删除造成的碎片重新整理之后,按照上述步骤插入纪录。 3)如果当前页空间整理碎片之后仍然不足的话,则重新申请一个页,将页初始化之后,按照上述步骤插入纪录
事务
数据库事务概念
数据库的事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。 事务的典型应用场景,如转账。
事务的四大特性(ACID)
原子性
事务最小执行单元,不允许分割。 确保动作要么全部完成,要么完全不起作用
隔离性
并发访问数据库时,一个用户的事务不被其他事务所干扰,合并发的事务的数据库是独立。
持久性
一个事务被提交后,他对数据库的数据改变是持久的,即使数据库发生故障也不对其有任何影响
一致性
事务执行前后,数据保持一致,多个事务对同一数据读取的结果是相同的
事务的并发问题
脏读
一个事务读取到另一个事务尚未提交的数据。 事务 A 读取事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据。
幻读
一个事务中两次读取的数据量不一致。 系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
不可重复读
一个事务中两次读取的数据的内容不一致。 事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果 不一致。
概要
不可重复读的和幻读很容易混淆, 不可重复读侧重于修改, 幻读侧重于新增或删除。 解决不可重复读的问题只需锁住满足条件的行, 解决幻读需要锁表。
事务的隔离等级
读未提交(Read uncommitted)
读已提交(Read committed)
可重复读(Serializeble)
可串行化(Repeatable read )
锁
作用
当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。 即锁的作用是解决并发问题。
分类
从锁的粒度划分
行级锁
是锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。 行级锁开销大,加锁慢,且会出现死锁。但锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
表级锁
是粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。
页级锁
是粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。
概要
开销和加锁时间界于表锁和行锁之间,会出现死锁。锁定粒度界于表锁和行锁之间,并发度一般。
从使用性质划分
共享锁(Share Lock)S锁
S 锁,又称读锁,用于所有的只读数据操作。 S 锁并非独占,允许多个并发事务对同一资源加锁,但加 S 锁的同时不允许加 X 锁,即资源不能被修改。S 锁通常读取结束后立即释放,无需等待事务结束。
排它锁(Exclusive Lock)X锁
X 锁,又称写锁,表示对数据进行写操作。 X 锁仅允许一个事务对同一资源加锁,且直到事务结束才释放,其他任何事务必须等到 X 锁被释放才能对该页进行访问。 使用 select * from table_name for update; 语句产生 X 锁。
更新锁 U锁
U 锁,用来预定要对资源施加 X 锁,允许其他事务读,但不允许再施加 U 锁或 X 锁。 当被读取的页将要被更新时,则升级为 X 锁,U 锁一直到事务结束时才能被释放。故 U 锁用来避免使用共享锁造成的死锁现象
从主观上划分
乐观锁(Optimistic Lock)
从主观上认定资源是不会被修改的,所以不加锁读取数据,仅当更新时用版本号机制等确认资源是否被修改。 乐观锁适用于多读的应用类型,可以系统提高吞吐量。
悲观锁(Pessimistic Lock)
正如其名,具有强烈的独占和排它特性,每次读取数据时都会认为会被其它事务修改,所以每次操作都需要加上锁。
隔离级别和锁的关系
1)在 Read Uncommitted 级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突; 2)在 Read Committed 级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁; 3)在 Repeatable Read 级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁; 4)在 SERIALIZABLE 级别下,限制性最强,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。
InnoDB 中的锁算法
Record lock:单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record + gap 锁定一个范围,包含记录本身
什么是快照读和当前读
快照读
快照读就是读取的是快照数据,不加锁的简单 Select 都属于快照读。 SELECT * FROM player WHERE .
当前读
当前读就是读的是最新数据,而不是历史的数据。加锁的 SELECT,或者对数据进行增删改都会进行当前读。 SELECT * FROM player LOCK IN SHARE MODE; SELECT FROM player FOR UPDATE; INSERT INTO player values ... DELETE FROM player WHERE ... UPDATE player SET ...
MVCC 以及实现
MVCC 的英文全称是 Multiversion Concurrency Control,中文意思是多版本并发控制,可以做到读写互相不阻塞,主要用于解决不可重复读和幻读问题时提高并发效率。 其原理是通过数据行的多个版本管理来实现数据库的并发控制,简单来说就是保存数据的历史版本。可以通过比较版本号决定数据是否显示出来。读取数据的时候不需要加锁可以保证事务的隔离效果。
主题