--- title: 个人吐血系列-总结Mysql date: 2020-03-30 15:13:08 tags: db categories: java-interview --- > MySQL这一块的知识还是挺多的,问深度的话, 一般都是如何调优的,当然少不了MySQL的基础等知识。 ![MySQL面试常见问题](http://media.dreamcat.ink/uPic/Mysql面试常见问题.png) ### 数据库引擎innodb与myisam的区别 #### 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是如何执行一条SQL的 ![SQL执行的全部过程](http://media.dreamcat.ink/uPic/SQL执行的全部过程.png) **MySQL内部可以分为服务层和存储引擎层两部分:** 1. **服务层包括连接器、查询缓存、分析器、优化器、执行器等**,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 2. **存储引擎层负责数据的存储和提取。**其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认的存储引擎。 **Server层按顺序执行sql的步骤为:** 客户端请求->连接器(验证用户身份,给予权限) -> 查询缓存(存在缓存则直接返回,不存在则执行后续操作)->分析器(对SQL进行词法分析和语法分析操作) -> 优化器(主要对执行的sql优化选择最优的执行方案方法) -> 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)->去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果) **简单概括**: - **连接器**:管理连接、权限验证; - **查询缓存**:命中缓存则直接返回结果; - **分析器**:对SQL进行词法分析、语法分析;(判断查询的SQL字段是否存在也是在这步) - **优化器**:执行计划生成、选择索引; - **执行器**:操作引擎、返回结果; - **存储引擎**:存储数据、提供读写接口。 ### mysql的acid原理 **ACID嘛,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)!** 我们以从A账户转账50元到B账户为例进行说明一下ACID,四大特性。 #### 原子性 根据定义,原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。即要么转账成功,要么转账失败,是不存在中间的状态! **如果无法保证原子性会怎么样?** OK,就会出现**数据不一致**的情形,A账户减去50元,而B账户增加50元操作失败。系统将无故丢失50元~ #### 隔离性 根据定义,隔离性是指多个事务并发执行的时候,**事务内部的操作与其他事务是隔离的**,并发执行的各个事务之间不能互相干扰。 **如果无法保证隔离性会怎么样?** OK,假设A账户有200元,B账户0元。A账户往B账户转账两次,金额为50元,分别在两个事务中执行。如果无法保证隔离性,会出现下面的情形 ![事务隔离性](http://media.dreamcat.ink/uPic/事务隔离性.png) 如图所示,如果不保证隔离性,A扣款两次,而B只加款一次,凭空消失了50元,依然出现了**数据不一致**的情形! #### 持久性 根据定义,**持久性是指事务一旦提交,它对数据库的改变就应该是永久性的**。接下来的其他操作或故障不应该对其有任何影响。 **如果无法保证持久性会怎么样?** 在MySQL中,为了解决CPU和磁盘速度不一致问题,MySQL是将磁盘上的数据加载到内存,对内存进行操作,然后再回写磁盘。好,假设此时宕机了,在内存中修改的数据全部丢失了,持久性就无法保证。 设想一下,系统提示你转账成功。但是你发现金额没有发生任何改变,此时数据出现了不合法的数据状态,我们将这种状态认为是**数据不一致**的情形。 #### 一致性 根据定义,一致性是指事务执行前后,数据处于一种合法的状态,这种状态是语义上的而不是语法上的。 那什么是合法的数据状态呢? oK,这个状态是满足预定的约束就叫做合法的状态,再通俗一点,这状态是由你自己来定义的。**满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的**! **如果无法保证一致性会怎么样?** - 例一:A账户有200元,转账300元出去,此时A账户余额为-100元。你自然就发现了此时数据是不一致的,为什么呢?因为你定义了一个状态,余额这列必须大于0。 - 例二:A账户200元,转账50元给B账户,A账户的钱扣了,但是B账户因为各种意外,余额并没有增加。你也知道此时数据是不一致的,为什么呢?因为你定义了一个状态,要求A+B的余额必须不变。 #### mysql怎么保证一致性? OK,这个问题分为两个层面来说。 **从数据库层面**,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。 但是,如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。例如,你在转账的例子中,你的代码里故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。 **从应用层面**,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据! #### mysql怎么保证原子性 OK,是利用Innodb的`undo log`。 `undo log`名为回滚日志,是实现原子性的关键,当事务回滚时能够**撤销所有已经成功执行的sql语句**,他需要记录你要回滚的相应日志信息。 例如 - (1)当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据 - (2)当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作 - (3)当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作 `undo log`记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。 #### mysql怎么保证持久性的 OK,是利用Innodb的`redo log`。 正如之前说的,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。 *怎么解决这个问题?* 简单啊,事务提交前直接把数据写入磁盘就行啊。 *这么做有什么问题?* - 只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。 - 毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。 于是,决定采用`redo log`解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在`redo log`中记录这次操作。当事务提交的时候,会将`redo log`日志进行刷盘(`redo log`一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将`redo log`中的内容恢复到数据库中,再根据`undo log`和`binlog`内容决定回滚数据还是提交数据。 **采用redo log的好处?** 其实好处就是将`redo log`进行刷盘比对数据页刷盘效率高,具体表现如下 - `redo log`体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。 - `redo log`是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。 #### mysql怎么保证隔离性 利用的是锁和MVCC机制。 ### 并发事务带来的问题 #### 脏读 ![脏读](https://www.pdai.tech/_images/pics/dd782132-d830-4c55-9884-cfac0a541b8e.png) #### 丢弃修改 T1 和 T2 两个事务都对一个数据进行修改,**T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改**。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。 ![丢弃修改](https://www.pdai.tech/_images/pics/88ff46b3-028a-4dbb-a572-1f062b8b96d3.png) #### 不可重复读 **T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同**。 ![不可重复读](https://www.pdai.tech/_images/pics/c8d18ca9-0b09-441a-9a0c-fb063630d708.png) #### 幻读 **T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同**。 ![幻读](https://www.pdai.tech/_images/pics/72fe492e-f1cb-4cfc-92f8-412fb3ae6fec.png) #### 不可重复读和幻读区别 **不可重复读的重点是修改,幻读的重点在于新增或者删除**。 例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操 作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。 例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所 有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记 录就变为了5条,这样就导致了幻读。 ### 数据库的隔离级别 1. 未提交读,事务中发生了修改,即使没有提交,其他事务也是可见的,比如对于一个数A原来50修改为100,但是我还没有提交修改,另一个事务看到这个修改,而这个时候原事务发生了回滚,这时候A还是50,但是另一个事务看到的A是100.**可能会导致脏读、幻读或不可重复读** 2. 提交读,对于一个事务从开始直到提交之前,所做的任何修改是其他事务不可见的,举例就是对于一个数A原来是50,然后提交修改成100,这个时候另一个事务在A提交修改之前,读取的A是50,刚读取完,A就被修改成100,这个时候另一个事务再进行读取发现A就突然变成100了;**可以阻止脏读,但是幻读或不可重复读仍有可能发生** 3. 可重复读,就是对一个记录读取多次的记录是相同的,比如对于一个数A读取的话一直是A,前后两次读取的A是一致的;**可以阻止脏读和不可重复读,但幻读仍有可能发生。** 4. 可串行化读,在并发情况下,和串行化的读取的结果是一致的,没有什么不同,比如不会发生脏读和幻读;**该级别可以防止脏读、不可重复读以及幻读。** | 隔离级别 | 脏读 | 不可重复读 | 幻影读 | | ---------------- | ---- | ---------- | ------ | | READ-UNCOMMITTED | √ | √ | √ | | READ-COMMITTED | × | √ | √ | | REPEATABLE-READ | × | × | √ | | SERIALIZABLE | × | × | × | MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。 **这里需要注意的是**:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别 下使用的是**Next-Key Lock 锁**算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以 说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要 求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内 容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)并不会有任何性能损失**。 InnoDB 存储引擎在分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。 ### 为什么使用索引 - 通过创建**唯一性索引**,可以保证数据库表中每一行数据的唯一性。 - 可以大大**加快数据的检索速度**,这也是创建索引的最主要的原因。 - 帮助服务器**避免排序和临时表** - 将**随机IO变为顺序IO**。 - 可以加速**表和表之间的连接**,特别是在实现数据的参考完整性方面特别有意义。 ### 索引这么多优点,为什么不对表总的每一列创建一个索引 - 当对表中的数据进行增加、删除和修改的时候,**索引也要动态的维护**,这样就降低了数据的维护速度。 - **索引需要占物理空间**,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立簇索引,那么需要的空间就会更大。 - **创建索引和维护索引要耗费时间**,这种时间随着数据量的增加而增加 ### 索引如何提高查询速度的 将无序的数据变成相对有序的数据(就像查有目的一样) ### 使用索引的注意事项 - 在经常需要搜索的列上,可以加快搜索的速度; - 在经常使用在where子句中的列上面创建索引,加快条件的判断速度。 - 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间 - 在中到大型表索引都是非常有效的,但是特大型表的维护开销会很大,不适合建索引 - 在经常用到连续的列上,这些列主要是由一些外键,可以加快连接的速度 - 避免where子句中对字段施加函数,这会造成无法命中索引 - 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。 - **将打算加索引的列设置为NOT NULL,否则将导致引擎放弃使用索引而进行全表扫描** - 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 - 在使用limit offset查询缓存时,可以借助索引来提高性能。 ### MySQL索引主要使用的两种数据结构 - **哈希索引**,对于哈希索引来说,底层的数据结构肯定是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引 - **BTree索引**,Mysql的BTree索引使用的是B树中的B+Tree但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。 ### myisam和innodb实现btree索引方式的区别 - MyISAM,**B+Tree叶节点的data域存放的是数据记录的地址**,在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读区相应的数据记录,这被称为“非聚簇索引” - InnoDB,其数据文件本身就是索引文件,相比MyISAM,**索引文件和数据文件是分离的**,**其表数据文件本身就是按B+Tree组织的一个索引结构,树的节点data域保存了完整的数据记录**,这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引”或者聚集索引,而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方,在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,在走一遍主索引。因此,在设计表的时候,不建议使用过长的字段为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。 ### 数据库结构优化 - **范式优化**: 比如消除冗余(节省空间。。) - **反范式优化**:比如适当加冗余等(减少join) - **限定数据的范围**: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。 - **读/写分离**: 经典的数据库拆分方案,主库负责写,从库负责读; - **拆分表**:分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。 **拆分其实又分垂直拆分和水平拆分:** - 案例: 简单购物系统暂设涉及如下表: - 1.产品表(数据量10w,稳定) - 2.订单表(数据量200w,且有增长趋势) - 3.用户表 (数据量100w,且有增长趋势) - 以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万 - **垂直拆分:** - 解决问题:表与表之间的io竞争 - 不解决问题:单表中数据量增长出现的压力 - 方案: 把产品表和用户表放到一个server上 订单表单独放到一个server上 - **水平拆分:** - 解决问题:单表中数据量增长出现的压力 - 不解决问题:表与表之间的io争夺 - 方案:**用户表** 通过性别拆分为男用户表和女用户表,**订单表** 通过已完成和完成中拆分为已完成订单和未完成订单,**产品表** 未完成订单放一个server上,已完成订单表盒男用户表放一个server上,女用户表放一个server上(女的爱购物 哈哈)。 ### 主键超键候选键外键是什么 - **超键**:在关系中能唯一标识**元组的属性集**称为关系模式的超键 - **候选键**:不含有**多余属性的超键**称为候选键。也就是在候选键中,若再删除属性,就不是键了! - **主键**:**用户选作元组标识的一个候选键程序主键** - **外键**:如果关系模式**R中属性K是其它模式的主键**,那么**k在模式R中称为外键**。 **举例**: | 学号 | 姓名 | 性别 | 年龄 | 系别 | 专业 | | -------- | ------ | ---- | ---- | ------ | -------- | | 20020612 | 李辉 | 男 | 20 | 计算机 | 软件开发 | | 20060613 | 张明 | 男 | 18 | 计算机 | 软件开发 | | 20060614 | 王小玉 | 女 | 19 | 物理 | 力学 | | 20060615 | 李淑华 | 女 | 17 | 生物 | 动物学 | | 20060616 | 赵静 | 男 | 21 | 化学 | 食品化学 | | 20060617 | 赵静 | 女 | 20 | 生物 | 植物学 | 1. 超键:于是我们从例子中可以发现 学号是标识学生实体的唯一标识。那么该元组的超键就为学号。除此之外我们还可以把它跟其他属性组合起来,比如:(`学号`,`性别`),(`学号`,`年龄`) 2. 候选键:根据例子可知,学号是一个可以唯一标识元组的唯一标识,因此学号是一个候选键,实际上,候选键是超键的子集,比如 (学号,年龄)是超键,但是它不是候选键。因为它还有了额外的属性。 3. 主键:简单的说,例子中的元组的候选键为学号,但是我们选定他作为该元组的唯一标识,那么学号就为主键。 4. 外键是相对于主键的,比如在学生记录里,主键为学号,在成绩单表中也有学号字段,因此学号为成绩单表的外键,为学生表的主键。 **主键为候选键的子集,候选键为超键的子集,而外键的确定是相对于主键的。** ### drop,delete与truncate的区别 - drop直接删掉表; - truncate删除表中数据,再插入时自增长id又从1开始 ; - delete删除表中数据,可以加where字句。 1. DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。 2. 表和索引所占空间。当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小,而DELETE操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。 3. 一般而言,drop > truncate > delete 4. 应用范围。TRUNCATE 只能对TABLE;DELETE可以是table和view 5. TRUNCATE 和DELETE只删除数据,而DROP则删除整个表(结构和数据)。 6. truncate与不带where的delete :只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。 7. delete语句为DML(Data Manipulation Language),这个操作会被放到 rollback segment中,事务提交后才生效。如果有相应的 tigger,执行的时候将被触发。 8. truncate、drop是DDL(Data Define Language),操作立即生效,原数据不放到 rollback segment中,不能回滚 9. 在没有备份情况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,如果于事务无关,用truncate即可实现。如果和事务有关,或老是想触发trigger,还是用delete。 10. Truncate table 表名 速度快,而且效率高,因为: truncate table 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。 11. TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。 12. 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。 ### 视图的作用,视图可以更改吗 视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。 视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by 则对视图再次order by将被覆盖。 创建视图:`create view xxx as xxxx` 对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。 ### 数据库范式 #### 第一范式 在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。 所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。简而言之,**第一范式就是无重复的列**。 #### 第二范式 第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。 第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,**第二范式就是非主属性非部分依赖于主关键字**。 #### 第三范式 满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,**存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。**简而言之,第三范式就是属性不依赖于其它非主属性。(我的理解是消除冗余) ### 什么是覆盖索引 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称 之为“覆盖索引”。我们知道在InnoDB存储引 擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就 会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作! ### 各种树 > 这里就不多介绍了,有兴趣可以在这里看[各种树](http://dreamcat.ink/2019/11/08/shu-ju-jie-gou-shu-ji-chu/) > 创作不易哇,觉得有帮助的话,给个小小的star呗。github地址😁😁😁