Database

数据库

关系型数据库

什么是关系型数据库

关系型数据库是依据关系模型来创建的数据库,由表的集合组成,关系模型可以为一对一、一对多、多对多

数据库范式

1NF:每个属性都不可再分

2NF:不存在非主属性对码的部分函数依赖(非主属性完全依赖于主关键字)

3NF:不存在非主属性对码的传递函数依赖

BCNF:主键有可能是由多个主属性组合成的复合主键,那么多个主属性之间不能有传递依赖

连接

自然连接(natural join) 自然连接是一种特殊的等值连接,他要求两个关系表中进行比较的必须是相同的属性列,无须添加连接条件,并且在结果中消除重复的属性列。

内连接(inner join) 内连接基本与自然连接相同,不同之处在于自然连接要求是同名属性列的比较,而内连接则不要求两属性列同名,可以用using或on来指定某两列字段相同的连接条件。

平常from … where … = …虽然没有指明是inner join但实际上就是内连接

如果是join,默认就是inner join

左外连接(left outer join) 左外连接是在两表进行自然连接,只把左表要舍弃的保留在结果集中,右表对应的列上填null。

右外连接(rignt outer join) 右外连接是在两表进行自然连接,只把右表要舍弃的保留在结果集中,左表对应的列上填null。

全外连接(full join) 全外连接是在两表进行自然连接,只把左表和右表要舍弃的都保留在结果集中,相对应的列上填null。

联合查询

联合查询结果是将多个select语句的查询结果合并到一块

比如现在需要查询两个公司的所有员工的信息,这就需要从甲公司查询所有员工信息,再从乙公司查询所有的员工信息,然后将两次的查询结果进行合并。

语法格式如下:

select 语句1
union[union 选项]
select 语句2
union|[union 选项]
select 语句n

其中union选项有两个选项可选

  • all:表示无论重复都输出
  • distinct: 去重(整个重复)(默认的)

应用场景

  • 将同一张表中不同的结果(需要对应多条查询语句来实现),合并到一起展示数据
  • 最常见:在数据量大的情况下,会对表进行分表操作,需要对每张表进行部分数据统计,使用联合查询来将数据存放到一起显示

事务

数据库什么是事务?

事务是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元。

事务有哪四个特性?分别表示什么含义?

A:原子性 事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出全部成功执行或者全部不执行。

C:一致性 事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。

I:隔离性 事务的隔离性是指在并发环境中,并发的事务是相互隔离的,一个事务的执行不能被其他事务干扰。也就是说,不同的事务并发操纵相同的数据时,每个事务都有各自完整的数据空间,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

D:持久性 事务的持久性也被称为永久性,是指一个事务一旦提交,它对数据库中对应数据的状态变更就应该是永久性的。换句话说,一旦某个事务成功结束,那么它对数据库所做的更新就必须被永久保存下来——即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态。

事务的隔离级别

未提交读:允许读取未提交数据

已提交读:只允许读取已提交数据,但不要求可重复读;比如在该事务两次读取一个数据项期间,另一个事务更新了该数据并提交

可重复读:只允许读取已提交数据,而且在一个事务两次读取一个数据项期间,不允许其它事务更新该数据;但不要求可串行化,例如当一个事务在查找满足某些条件的数据时,它可能找到一个已提交事务插入的一些数据(不是改变原有数据所以不会被锁住),但找不到该事务插入的其它数据

可串行化:通常保证可串行化调度,等价于一个串行调度

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就有几列数据是未查询出来的,如果此时插入和另外一个事务插入的数据,就会报错。

image-20200614194013390

Note:不可重复读用MVCC可解;幻读加间隙锁可解

事务是怎么实现的

事务就是一组逻辑操作的集合。实现事务就是要保证可靠性和并发隔离,或者说,能够满足ACID特性的机制。而这些主要是靠日志恢复和并发控制实现的

  • 日志恢复:数据库里有两个日志,一个是redo log,一个是undo log。redo log记录的是已经成功提交的事务操作信息,用来恢复数据,保证事务的持久性。undo log记录的是事务修改之前的数据信息,用来回滚数据
  • 并发控制:并发控制主要靠锁和来实现

redo log & undo log

undolog用于回滚,redolog用于前滚

前滚:事务提交之后,部分数据写入了磁盘,但是还有部分数据存在脏页上,并没有写入磁盘。此时设备宕机,没有写入磁盘的数据丢失。就要依赖redolog来恢复这部分数据。

回滚:事务还未提交,改动并没有完全生效,但是记录已经被修改。此时设备宕机,数据是有问题的,就要依赖undolog回滚改动。

死锁处理

  • 预防死锁
  1. 保证事务之间的等待不会出现环,也就是事务之间的等待图应该是一张有向无环图,没有循环等待的情况

  2. 使用抢占加事务回滚的方式预防死锁,当事务开始执行时会先获得一个时间戳,数据库程序会根据事务的时间戳决定事务应该等待还是回滚

    • wait-die:年老事务等待年轻事务释放对应资源的锁;年轻事务不等待年老事务,而是直接回滚
    • wound-wait:年老事务抢占年轻事务的资源并强制年轻事务回滚;年轻事务等待年老事务释放对应资源的锁
  • 死锁检测和恢复

死锁检测:超时 / 等待图出现环

死锁恢复:全部回滚(回滚整个事务) / 部分回滚(回滚到可以解除死锁的状态)

MVCC(多版本控制机制)

在MySQL中建表时,每个表都会有三列隐藏记录,其中和MVCC有关系的有两列

  • 数据行的版本号 (DB_TRX_ID)
  • 删除版本号 (DB_ROLL_PT)

image-20200922231235923image-20200922231246522image-20200922231258357image-20200922231327307

索引

B+树 B树区别

B+tree 是 B-tree 的变种,B+tree 数据只存储在叶子节点中。这样在B树的基础上每个节点存储的关键字数更多,树的层级更少所以查询数据更快,所有指关键字指针都存在叶子节点,所以每次查找的次数都相同所以查询速度更稳定;

与B树区别

  • 存储的位置不同;B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。
  • 分支结点的构造不同;B+树的分支结点仅仅存储着关键字信息和儿子的指针,内部结点仅仅包含着索引信息。
  • 查询不同;B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。

B+树优点

  • B+树的磁盘读写代价更低

B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了;

  • B+树查询效率更加稳定

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;

  • B+树便于范围查询(最重要的原因,范围查找是数据库的常态)

B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低

B+树增加删除

image-20201017223004030

image-20201017223024604

image-20201017223044715

image-20201017223102719

image-20201017223115728

image-20201017223136366

image-20201017223156522

索引类型

  • 聚集索引(主索引): 数据在物理存储中的顺序跟索引中数据的逻辑顺序相同

  • 非聚集索引(辅助索引):数据在物理存储中的顺序跟索引中数据的逻辑顺序不同

聚簇索引的叶子节点存储了一行完整的数据,而二级索引只存储了主键值,相比于聚簇索引,占用的空间要少。当我们需要为表建立多个索引时,如果都是聚簇索引,那将占用大量内存空间,所以InnoDB中主键所建立的是聚簇索引,而唯一索引、普通索引、前缀索引等都是二级索引。

非聚集索引因为无法定位数据所在的行,所以需要扫描两遍索引树。第一遍扫描非聚集索引的索引树,确定该数据的主键ID,然后到主键索引(聚集索引)中寻找相应的数据。

  • 稠密索引:文件中的每个搜索码值都对应一个索引值

  • 稀疏索引:只为搜索码的某些值建立索引项

聚集索引可以是稀疏索引,非聚集索引必须是稠密索引(不然找不到,非聚集索引不按顺序)

  • 哈希索引

只有memory(内存)存储引擎支持哈希索引,哈希索引用索引列的值计算该值的hashCode,然后在hashCode相应的位置存该值所在行数据的物理位置,因为使用散列算法,因此访问速度非常快,但是一个值只能对应一个hashCode,而且是散列的分布方式,因此哈希索引不支持范围查找和排序的功能

  • 全文索引

仅可用于MyISAM和InnoDB,针对较大的数据,生成全文索引非常的消耗时间和空间。对于文本的大对象,或者较大的CHAR类型的数据,如果使用普通索引,那么匹配文本前几个字符还是可行的,但是想要匹配文本中间的几个单词,那么就要使用LIKE %word%来匹配,这样需要很长的时间来处理,响应时间会大大增加,这种情况,就可使用时FULLTEXT索引了

  • B+树索引(见下文)

什么情况下不适合建立索引

  • 对于在查询过程中很少使用或参考的列,不应该创建索引。
  • 对于那些只有很少数据值的列,不应该创建索引。
  • 对于那些定义为image,text和bit数据类型的列,不应该创建索引。
  • 当修改性能远大于检索性能,不应该建立索引。

主键和索引比较

1.主键为一种约束,唯一索引为一种索引,本质上就不同。

2.主键在表中只能有一个,唯一索引可以有多个。

3.主键创建后一定包含唯一性索引,而唯一索引不一定就是主键。

4.主键不能为null,唯一索引可以为null.

5.主键可以被其它表引用,唯一索引不能。

6.主键和索引都是键,主键是逻辑键,索引为物理键,即主键不实际存在。

索引作用

正面影响

快速访问数据表中的特定信息,提高检索速度;创建唯一性索引,保证数据库表中每一行数据的唯一性;加速表和表之间的连接;使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间

负面影响:

创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。

索引失效

  • 违背最左匹配
  • 在索引上做计算、函数、自动/手动类型转换等操作
  • 使用范围条件(bettween、<、>、in等)
  • 索引字段上使用!,=,<,>,is null / is not null,like判断时
  • 使用 or 并且一个字段没加索引时

联合索引&最左匹配

对于复合索引(多列b+tree,使用多列值组合而成的b+tree索引)。遵循最左侧原则,从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c). 可以支持a a,b a,b,c 3种组合进行查找,但不支持 b,c进行查找。当使用最左侧字段时,索引就十分有效。

比如(a,b,c)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(a=? and b=? and c=?)这样的数据来检索的时候,b+树会优先比较a列来确定下一步的所搜方向,如果a列相同再依次比较b列和c列,最后得到检索的数据;但当(b=? and c=?)这样的没有a列的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候a列就是第一个比较因子,必须要先根据a列来搜索才能知道下一步去哪里查询。比如当(a=? and c=?)这样的数据来检索时,b+树可以用a列来指定搜索方向,但下一个字段b列的缺失,所以只能把a列的数据找到,然后再匹配c列的数据了, 这个是非常重要的性质,即索引的最左匹配特性

联合索引遇到范围查询也会停止,例如a=1, c<2,a仍然可以命中索引,但bc就不行了

Mysql

Mysql引擎

image-20200906173116788

  • InnoDB

是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。

实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。

主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。

内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。

支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。

  • MyISAM

设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。

提供了大量的特性,包括压缩表、空间数据索引等。

不支持事务。

不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。

可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。

如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。

比较

  • 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。
  • 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
  • 外键:InnoDB 支持外键。
  • 备份:InnoDB 支持在线热备份。
  • 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
  • 其它特性:MyISAM 支持压缩表和空间数据索引。

Mysql锁

切分

image-20201017222332505

image-20201017222349230

Sharding 存在的问题

  1. 事务问题

使用分布式事务来解决,比如 XA 接口。

  1. 连接

可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。

  1. ID 唯一性

使用全局唯一 ID(GUID)

为每个分片指定一个 ID 范围

分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)

Mysql不同引擎锁的区别

  • MyISAM 和 MEMORY 存储引擎采用的是表级锁(table-level locking)
  • InnoDB 存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

  • 这些存储引擎通过总是一次性同时获取所有需要的锁以及总是按相同的顺序获取表锁来避免死锁。

  • 表级锁更适合于以查询为主,并发用户少,只有少量按索引条件更新数据的应用,如Web 应用

行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

  • 最大程度的支持并发,同时也带来了最大的锁开销。

  • 在 InnoDB 中,除单个 SQL 组成的事务外,锁是逐步获得的,这就决定了在 InnoDB 中发生死锁是可能的。

  • 行级锁只在存储引擎层实现,而Mysql服务器层没有实现。 行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统

Mysql有哪些锁

  • 读写锁(行锁)

读锁,又称共享锁(Share locks,简称 S 锁),加了读锁的记录,所有的事务都可以读取,但是不能修改,并且可同时有多个事务对记录加读锁。

写锁,又称排他锁(Exclusive locks,简称 X 锁),或独占锁,对记录加了排他锁之后,只有拥有该锁的事务可以读取和修改,其他事务都不可以读取和修改,并且同一时间只能有一个事务加写锁。

  • 读写意向锁(表锁)

由于表锁和行锁虽然锁定范围不同,但是会相互冲突。所以当你要加表锁时,势必要先遍历该表的所有记录,判断是否加有排他锁。这种遍历检查的方式显然是一种低效的方式,MySQL 引入了意向锁,来检测表锁和行锁的冲突。

意向锁也是表级锁,也可分为读意向锁(IS 锁)和写意向锁(IX 锁)。当事务要在记录上加上读锁或写锁时,要首先在表上加上意向锁。这样判断表中是否有记录加锁就很简单了,只要看下表上是否有意向锁就行了。

意向锁之间是不会产生冲突的,也不和 AUTO_INC 表锁冲突,它只会阻塞表级读锁或表级写锁,另外,意向锁也不会和行锁冲突,行锁只会和行锁冲突。

  • 自增锁(表锁)

AUTOINC 锁又叫自增锁(一般简写成 AI 锁),是一种表锁,当表中有自增列(AUTOINCREMENT)时出现。当插入表中有自增列时,数据库需要自动生成自增值,它会先为该表加 AUTOINC 表锁,阻塞其他事务的插入操作,这样保证生成的自增值肯定是唯一的。AUTOINC 锁具有如下特点:

AUTO_INC 锁互不兼容,也就是说同一张表同时只允许有一个自增锁;

自增值一旦分配了就会 +1,如果事务回滚,自增值也不会减回去,所以自增值可能会出现中断的情况。

  • 记录锁(行锁)

记录锁是最简单的行锁,并没有什么好说的。上边描述 InnoDB 加锁原理中的锁就是记录锁,只锁住 id = 49 或者 name = ‘Tom’ 这一条记录。

当 SQL 语句无法使用索引时,会进行全表扫描,这个时候 MySQL 会给整张表的所有数据行加记录锁,再由 MySQL Server 层进行过滤。但是,在 MySQL Server 层进行过滤的时候,如果发现不满足 WHERE 条件,会释放对应记录的锁。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。

所以更新操作必须要根据索引进行操作,没有索引时,不仅会消耗大量的锁资源,增加数据库的开销,还会极大的降低了数据库的并发性能。

  • 间隙锁

还是最开始更新用户年龄的例子,如果 id = 49 这条记录不存在,这个 SQL 语句还会加锁吗?答案是可能有,这取决于数据库的隔离级别。这种情况下,在 RC 隔离级别不会加任何锁,在 RR 隔离级别会在 id = 49 前后两个索引之间加上间隙锁。

间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。这个间隙可以跨一个索引记录,多个索引记录,甚至是空的。使用间隙锁可以防止其他事务在这个范围内插入或修改记录,保证两次读取这个范围内的记录不会变,从而不会出现幻读现象。

值得注意的是,间隙锁和间隙锁之间是互不冲突的,间隙锁唯一的作用就是为了防止其他事务的插入,所以加间隙 S 锁和加间隙 X 锁没有任何区别。

  • 临键锁

Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。假设一个索引包含 15、18、20 ,30,49,50 这几个值,可能的 Next-key 锁如下:

(-∞, 15],(15, 18],(18, 20],(20, 30],(30, 49],(49, 50],(50, +∞)

通常我们都用这种左开右闭区间来表示 Next-key 锁,其中,圆括号表示不包含该记录,方括号表示包含该记录。前面四个都是 Next-key 锁,最后一个为间隙锁。和间隙锁一样,在 RC 隔离级别下没有 Next-key 锁,只有 RR 隔离级别才有。还是之前的例子,如果 id 不是主键,而是二级索引,且不是唯一索引,那么这个 SQL 在 RR 隔离级别下就会加如下的 Next-key 锁 (30, 49](49, 50)

此时如果插入一条 id = 31 的记录将会阻塞住。之所以要把 id = 49 前后的间隙都锁住,仍然是为了解决幻读问题,因为 id 是非唯一索引,所以 id = 49 可能会有多条记录,为了防止再插入一条 id = 49 的记录。

  • 插入意向锁

插入意向锁是一种特殊的间隙锁(简写成 II GAP)表示插入的意向,只有在 INSERT 的时候才会有这个锁。注意,这个锁虽然也叫意向锁,但是和上面介绍的表级意向锁是两个完全不同的概念,不要搞混了。

插入意向锁和插入意向锁之间互不冲突,所以可以在同一个间隙中有多个事务同时插入不同索引的记录。譬如在上面的例子中,id = 30 和 id = 49 之间如果有两个事务要同时分别插入 id = 32 和 id = 33 是没问题的,虽然两个事务都会在 id = 30 和 id = 50 之间加上插入意向锁,但是不会冲突。

插入意向锁只会和间隙锁或 Next-key 锁冲突,正如上面所说,间隙锁唯一的作用就是防止其他事务插入记录造成幻读,正是由于在执行 INSERT 语句时需要加插入意向锁,而插入意向锁和间隙锁冲突,从而阻止了插入操作的执行。

Mysql加锁

意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。

共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。

排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。

InnoDB行锁实现方式

InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁

查询性能优化

  1. 优化数据访问
  • 减少请求的数据量:

只返回必要的列:最好不要使用 SELECT * 语句。

只返回必要的行:使用 LIMIT 语句来限制返回的数据。

缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。

  • 减少服务器端扫描的行数

使用索引来覆盖查询。

  1. 重构查询方式
  • 切分大查询

一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。

DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);

改成

rows_affected = 0
do {
    rows_affected = do_query(
    "DELETE FROM messages WHERE create  < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
} while rows_affected > 0
  • 分解大连接查询

将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有:

  1. 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
  2. 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
  3. 减少锁竞争;
  4. 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。

InnoDB如何建立主键

  1. 如果一个主键被定义了,那么这个主键就是作为聚集索引

  2. 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引

  3. 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入自增。

为什么主键最好是自增主键

image-20201017215529486

image-20201017215552811

MySQL执行一个查询的过程

  1. 客户端发送一个查询给服务器。
  2. 服务器先检查查询缓存,如果命中,则直接返回缓存中的结果。如果没有没有命中,则进入下一阶段(解析器)。
  3. 服务器由解析器检查sql语法是否正确,然后由预处理器检查sql中的表和字段是否存在,最后由查询优器生成执行计划。这一步很耗资源。
  4. mysql根据优化器生成的执行计划,调用存储引擎的API来执行查询。
  5. 将结果返回给客户端。