【备战秋招冲击大厂】Java 面试题系列—数据库
INNODB 还需要维护 MVCC 一致;虽然你的场景没有,但他还是需要去检查和维护,MYISAM 表锁牺牲了写性能,提高了读性能。
4. 数据库保证并发性(MVCC)
MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
MVCC 带来的好处是?
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
同时还可以解决脏读,不可重复读,幻读等事务隔离问题,但不能解决更新丢失问题
小结
总之,MVCC 就是为了解决数据库仅仅采用悲观锁这样性能不佳的形式去解决读-写冲突问题,所以在数据库中我们可以形成两个组合:
MVCC + 悲观锁:MVCC 解决读-写冲突,悲观锁解决写-写冲突
MVCC + 乐观锁:MVCC 解决读-写冲突,乐观锁解决写-写冲突
这种组合的方式就可以最大程度的提高数据库并发性能,并解决读写冲突,和写写冲突导致的问题
5. mysql 的主从复制
MySQL 主从复制是其最重要的功能之一。主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中。对于多级复制,数据库服务器即可充当主机,也可充当从机。MySQL 主从复制的基础是主服务器对数据库修改记录二进制日志,从服务器通过主服务器的二进制日志自动执行更新。MySQL 主从复制的两种情况:同步复制和异步复制,实际复制架构中大部分为异步复制。
6. 索引
定义:在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构。遵循最左匹配原则,不然索引失效
(https://blog.csdn.net/qq_42253147/article/details/90730573)
目的:
1)数据库索引其实就是为了使查询数据效率快:通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。
2)创建唯一性索引,保证数据库表中每一行数据的唯一性;
3)加速表和表之间的连接;
缺点:
1)索引需要占用数据表以外的物理存储空间
2)创建索引和维护索引要花费一定的时间
3)当对表进行更新操作时,索引需要被重建,这样降低了数据的维护速度。
最左匹配原则:以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配,例如 abc 使用索引的为 a、ab、abc、ac
类型:
1)聚集索引(主键索引):以主键作为 B+ 树索引的键值而构建的 B+ 树索引,一个表只能包含一个聚集索引。在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。在数据库里,所有行数都会按照主键索引进行排序。表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,类似整个表就变成了一个索引,也就是所谓的「聚集索引」。
2)非聚集索引:以主键以外的列值作为键值构建的 B+ 树索引,通过聚集索引可以查到需要查找的数据,而通过非聚集索引可以查到记录对应的主键值 ,再使用主键的值通过聚集索引查找到需要的数据。非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表。
3)联合索引:就是多个字段组成的索引,称为联合索引。联合索引(a,b,c),用到索引的有 a,ab,abc,ac 因为优化器会自动调整 and 前后的顺序,所以 ba,cba,bca,ca 都会用到索引,其他的都不会用到该索引。ac 这一组仅仅是 a 用到索引。
底层实现方式:B+树
聚簇索引:索引页+数据页组成的 B+树,是 MySQL 基于主键索引结构创建的
索引在 MySQL 数据库中分四类:B+ 树索引、Hash 索引、全文索引、R-Tree 索引(GIS 数据)
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似 B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
7. Mysql 索引分类
从逻辑角度:主键索引、唯一索引、普通索引(单列)、联合索引(多列)、空间索引
从数据结构角度:B+ 树索引、Hash 索引、全文索引、R-Tree 索引(GIS 数据)
从物理存储角度:聚集索引和非聚集索引
8. Hash 索引和 B+树索引的区别
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引也没办法利用索引完成排序,以及 like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
B+树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
9. 回表和覆盖索引
回表定义:数据库根据索引(非主键)找到了指定的记录所在行后,还需要根据主键再次到数据块里获取数据。
如何避免回表:将需要的字段放在索引中去。查询的时候就能避免回表。
覆盖索引定义:指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。当一条查询语句符合覆盖索引条件时,MySQL 只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少 I/O、提高效率。 如表 covering_index_sample 中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过 SQL 语句:select key2 from covering_index_sample where key1 = ‘keytest’;的时候,就可以通过覆盖索引查询,无需回表。
10. 索引创建原则
经常被用来过滤记录的字段
1)primary key 字段,系统自动创建主键的索引;
2)unique key 字段,系统自动创建对应的索引;
3)foreign key 约束所定义的作为外键的字段;
4)在查询中用来连接表的字段;
5)经常用来作为排序(order by 的字段)基准的字段;
内容变动多的字段谨慎创建,索引维护消耗性能
索引会额外占用磁盘空间,不是越多越好
一个表中只能有一个主键,可以有多个 unique key
避免选择大型数据类型的列作为索引
应该避免对具有较少值的字段进行索引
11. 索引失效的情况
有 or 必全有索引:要想使用 or,又想让索引生效,只能将 or 条件中的每个列都加上索引
复合索引未用左列字段;
like 以 %开头;
需要类型转换;
where 中索引列有运算;
where 中索引列使用了函数;
如果 mysql 觉得全表扫描更快时(数据少);
12. B+树
MySQL 中最常用的索引的数据结构是 B+ 树,有以下特点:
1)在 B+ 树中,所有数据记录节点都是按照键值的大小存放在同一层的叶子节点上,而非叶子结点只存储 key 的信息,这样可以大大减少每个节点的存储的 key 的数量,降低 B+ 树的高度
2)B+ 树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。
B+树和 B 树的区别:
1)B+ 树的层级更少:相较于 B 树 B+ 每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快
2)B+ 树查询速度更稳定:B+ 所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比 B 树更稳定;
3)B+ 树天然具备排序功能:B+ 树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比 B 树高。
4)B+ 树全节点遍历更快:B+ 树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像 B 树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
13. 为什么用 B+树而不是 B 树?
在 B 树中,你可以将键和值存放在内部节点和叶子节点,但在 B+树中,内部节点都是键,没有值。
B+树的叶子节点有一条链相连,而 B 树的叶子节点各自独立。
B 树和 B+树区别:
关键字数量不同:B+树分支结点 M 个关键字,叶子节点也有 M 个;B 树分支结点则存在 k-1 个关键码
数据存储位置不同:B+树数据存储在叶子结点上,而且数据是按照顺序排列的;B 树存储在每个结点上;
查询不同:B+树是从根节点到叶子节点的路径;B 树是只需要找到数据就可以
分支节点存储信息不同:B+树存索引信息;B 树存的是数据关键字
14. B 树、B-树、B+树、B*树
B 树:每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
B-树:多路搜索树,每个结点存储 M/2 到 M 个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整棵树中出现,且只出现一次,非叶子结点可以命中;
B+树:在 B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树: 在 B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从 1/2 提高到 2/3;
15. 乐观锁和悲观锁
悲观锁:假定会发生并发冲突,屏蔽一切违反数据完整性的操作。每次取数据的时候都会上锁,想要拿数据就会 block 直到拿到锁。适用于写多读少的场景。如 synchorized 就是悲观锁的一种实现,适用于写多读少场景。
行锁、表锁、页锁
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。只在提交更新时判断别人有没有更新数据。适用于读多写少的场景。乐观锁的实现方式一般包括使用版本号和时间戳。如 CAS,实现方式:
1)数据版本:需要乐观锁控制的 table 中增加一个字段 version
2)时间戳:需要乐观锁控制的 table 中增加一个字段 timestamp
16. 事务
定义:访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
事务的 ACID 特性:
1)原子性 Atomicity:一个事务时不可分割的单位,事务中的操作要么都做,要么都不做。
2)一致性 Consistency:事务操作前后数据库完整性一致。
3)隔离性 Isolation:一个事务的执行不能被其他事务干扰
4)永久性 Durability:事务完成后对数据库的改变是永久性的。
事务隔离级别
1)未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交
事务修改的数据。
2)已提交读(Read Committed):只能读取到已经提交的数据。Oracle 等多数数据库默认都是该级别 (不重复读)。
3)可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB 默认级别。在 SQL 标准中,该隔离级别消除了不可重复读,但是还存在幻象读,即后一次查询看到了前一次查询没有看到的行。
4)串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
17. 事务的实现原理
原子性:使用 undo log ,从而达到回滚
持久性:使用 redo log,从而达到故障后恢复
隔离性:使用锁以及 MVCC,运用的优化思想有读写分离,读读并行,读写并行
一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
18. 脏读、不可重复读、幻读
脏读:脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
不可重复读:不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。例如:事务 T1 会读取两次数据,在第一次读取某一条数据后,事务 T2 修改了该数据并提交了事务,T1 此时再次读取该数据,两次读取便得到了不同的结果。不可重复读的重点是修改
幻读:幻觉读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。幻读的重点在于新增或者删除
19. 为什么实际开发中使用已提交读更多?
在可重复读隔离级别下,存在间隙锁,导致出现死锁的几率比已提交读大的多!
在可重复读隔离级别下,条件列未命中索引会锁表!而在已提交读隔离级别下,只锁行
在已提交读隔离级别下,半一致性读(semi-consistent)特性增加了 update 操作的并发性!
20. 间隙锁(Gap)
定义:一个在索引记录之间的间隙上的锁。
作用:保证某个间隙内的数据在锁定情况下不会发生任何变化。比如 mysql 默认隔离级别下的可重复读(RR)。
对主键索引或者唯一索引会使用间隙锁吗?
如果 where 条件全部命中,则不会使用 gap 锁,只会加记录锁
如果 where 条件部分命中或全不命中,则会加 gap 锁
Gap 锁会用在非唯一索引或不走索引的当前读中
21. 快照读和当前读
快照读:读取的是记录数据的可见版本(可能是过期的数据),不用加锁,第一次执行事务中 select 时生成快照。
当前读:读取的是记录数据的最新版本,并且当前读返回的记录都会加上锁,保证其他事务不会再并发的修改这条记录,当你执行 update、insert、delete 这几个操作的时候默认会执行当前读。
22. 对象属性与数据库字段不一致
1)Sql 语句起别名
2)Mapper.xml 中的 resultMap 自定义映射,<result column=”” property=””>
3)配置文件中开启驼峰命名规则(对象属性为驼峰命名)
23. 数据库优化方法
(1)选取最适用的字段属性
MySQL 可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为 CHAR(255),显然给数据库增加了不必要的空间,甚至使用 VARCHAR 这种类型也是多余的,因为 CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用 MEDIUMINT 而不是 BIGIN 来定义整型字段。另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为 NOT NULL,这样在将来执行查询的时候,数据库不用去比较 NULL 值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为 ENUM 类型。因为在 MySQL 中,ENUM 类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
(2)使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL 从 4.1 开始支持 SQL 的子查询。这个技术可以使用 SELECT 语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户 ID 取出来,然后将结果传递给主查询
(3)使用联合(UNION)来代替手动创建的临时表
MySQL 从 4.0 的版本开始支持 union 查询,它可以把需要使用临时表的两条或更多的 select 查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用 union 来创建查询的时候,我们只需要用 UNION 作为关键字把多个 select 语句连接起来就可以了,要注意的是所有 select 语句中的字段数目要想同。
(4)事务
尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条 SQL 语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以 BEGIN 关键字开始,COMMIT 关键字结束。在这之间的一条 SQL 操作失败,那么,ROLLBACK 命令就可以把数据库恢复到 BEGIN 开始之前的状态。
24. 慢查询优化方式
https://blog.csdn.net/qq_35571554/article/details/82800463
数据库中设置 SQL 慢查询
方式一:修改配置文件 在 my.ini 增加几行: 主要是慢查询的定义时间(超过 2 秒就是慢查询),以及慢查询 log 日志记录( slow_query_log)
方法二:通过 MySQL 数据库开启慢查询:
分析慢查询日志
直接分析 mysql 慢查询日志 ,利用 explain 关键字可以模拟优化器执行 SQL 查询语句,来分析 sql 慢查询语句,具体记录了:是哪条语句导致慢查询(sql_text),该慢查询语句的查询时间(query_time),锁表时间(Lock_time),以及扫描过的行数(rows_examined)等信息。
常见的慢查询优化
索引没起作用的情况
优化数据库结构
将字段很多的表分解成多个表
增加中间表
分解关联查询
优化 LIMIT 分页
筛选字段加索引
先查出主键 ID
关延迟联
建立复合索引
分析具体的 SQL 语句
具体步骤
第一步:根据慢日志定位慢查询 SQL
首先检查 SQL 中是否使用函数,隐式类型转换(字符串转数字),隐式函数或者传入的值超过索引长度
SQL 中字段的字符集是否一致
如果使用 count 计数,尽量使用 count(*)或者 count(1),count(字段)会涉及到回表操作,count(id)会全表扫描,并且 count(字段)和 count(id)都需要判空操作,也可以按照具体的业务选择 MyISAM 引擎,直接取值
查看当前语句的状态,是否是在等 MDL 锁,数据页 flush,其他线程占用了行锁
如果是热点数据,需要控制访问资源的并发事务量,可以将一行数据改成逻辑上的多行数据
第二步:使用 explain 等工具分析 SQL
查看 SQL 是否按照理想的状态检索最少的数据行,如果没有则查看是否走了指定索引
判断优化器是否选错索引,可以通过强行选择索引或者重新统计索引信息
第三步:修改 SQL 或者尽量让 SQL 走索引
评论