@@ -8,12 +8,13 @@ categories:
8
8
tags :
9
9
- 数据库
10
10
- 关系型数据库
11
+ - 面试
11
12
permalink : /pages/9bb28f/
12
13
---
13
14
14
15
# 关系型数据库面试
15
16
16
- ## 一、 索引和约束
17
+ ## 索引和约束
17
18
18
19
### 什么是索引
19
20
@@ -252,7 +253,7 @@ MySQL 会一直向右匹配直到遇到范围查询 `(>,<,BETWEEN,LIKE)` 就停
252
253
- ` FOREIGN KEY ` - 在一个表中存在的另一个表的主键称此表的外键。用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
253
254
- ` CHECK ` - 用于控制字段的值范围。
254
255
255
- ## 二、 并发控制
256
+ ## 并发控制
256
257
257
258
### 乐观锁和悲观锁
258
259
@@ -336,7 +337,7 @@ MVCC 不能解决幻读问题,**Next-Key 锁就是为了解决幻读问题**
336
337
337
338
当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。发生死锁后,` InnoDB ` 一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。
338
339
339
- ## 三、 事务
340
+ ## 事务
340
341
341
342
> 事务简单来说:** 一个 Session 中所进行所有的操作,要么同时成功,要么同时失败** 。具体来说,事务指的是满足 ACID 特性的一组操作,可以通过 ` Commit ` 提交一个事务,也可以使用 ` Rollback ` 进行回滚。
342
343
@@ -441,7 +442,7 @@ T<sub>1</sub> 读取某个范围的数据,T<sub>2</sub> 在这个范围内插
441
442
| 并发性能 | 无影响 | 严重衰退 | 略微衰退 |
442
443
| 适合场景 | 业务方处理不一致 | 短事务 & 低并发 | 长事务 & 高并发 |
443
444
444
- ## 四、 分库分表
445
+ ## 分库分表
445
446
446
447
### 什么是分库分表
447
448
@@ -577,7 +578,7 @@ T<sub>1</sub> 读取某个范围的数据,T<sub>2</sub> 在这个范围内插
577
578
578
579
来自淘宝综合业务平台团队,它利用对 2 的倍数取余具有向前兼容的特性(如对 4 取余得 1 的数对 2 取余也是 1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了 Sharding 扩容的难度。
579
580
580
- ## 五、 集群
581
+ ## 集群
581
582
582
583
> 这个专题需要根据熟悉哪个数据库而定,但是主流、成熟的数据库都会实现一些基本功能,只是实现方式、策略上有所差异。由于本人较为熟悉 Mysql,所以下面主要介绍 Mysql 系统架构问题。
583
584
@@ -609,7 +610,7 @@ MySQL 读写分离能提高性能的原因在于:
609
610
610
611
![ img] ( https://raw.githubusercontent.com/dunwu/images/dev/cs/database/mysql/master-slave-proxy.png )
611
612
612
- ## 六、 数据库优化
613
+ ## 数据库优化
613
614
614
615
数据库优化的路线一般为:SQL 优化、结构优化、配置优化、硬件优化。前两个方向一般是普通开发的考量点,而后两个方向一般是 DBA 的考量点。
615
616
@@ -842,7 +843,7 @@ SQL 关键字尽量大写,如:Oracle 默认会将 SQL 语句中的关键字
842
843
843
844
数据库扩容、使用高配设备等等。核心就是一个字:钱。
844
845
845
- ## 七、 数据库理论
846
+ ## 数据库理论
846
847
847
848
### 函数依赖
848
849
@@ -960,7 +961,7 @@ Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门
960
961
| 学院-1 | 院长-1 |
961
962
| 学院-2 | 院长-2 |
962
963
963
- ## 八、Mysql 存储引擎
964
+ ## 存储引擎
964
965
965
966
Mysql 有多种存储引擎,** 不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在 Mysql 服务层统一处理的** 。
966
967
@@ -986,7 +987,7 @@ InnoDB 和 MyISAM 是目前使用的最多的两种 Mysql 存储引擎。
986
987
- InnoDB 支持故障恢复。
987
988
- MyISAM 不支持故障恢复。
988
989
989
- ## 九、 数据库比较
990
+ ## 数据库比较
990
991
991
992
### 常见数据库比较
992
993
@@ -1070,6 +1071,78 @@ where rr>5 and rr<=10;
1070
1071
1071
1072
> 数据类型对比表摘自 [ SQL 通用数据类型] ( https://www.runoob.com/sql/sql-datatypes-general.html ) 、[ SQL 用于各种数据库的数据类型] ( https://www.runoob.com/sql/sql-datatypes.html )
1072
1073
1074
+ ## SQL FAQ
1075
+
1076
+ ### SELECT COUNT(\* )、SELECT COUNT(1) 和 SELECT COUNT(具体字段) 性能有差别吗?
1077
+
1078
+ 在 MySQL InnoDB 存储引擎中,` COUNT(*) ` 和 ` COUNT(1) ` 都是对所有结果进行 ` COUNT ` 。因此` COUNT(*) ` 和` COUNT(1) ` 本质上并没有区别,执行的复杂度都是 ` O(N) ` ,也就是采用全表扫描,进行循环 + 计数的方式进行统计。
1079
+
1080
+ 如果是 MySQL MyISAM 存储引擎,统计数据表的行数只需要` O(1) ` 的复杂度,这是因为每张 MyISAM 的数据表都有一个 meta 信息存储了` row_count ` 值,而一致性则由表级锁来保证。因为 InnoDB 支持事务,采用行级锁和 MVCC 机制,所以无法像 MyISAM 一样,只维护一个` row_count ` 变量,因此需要采用扫描全表,进行循环 + 计数的方式来完成统计。
1081
+
1082
+ 需要注意的是,在实际执行中,` COUNT(*) ` 和` COUNT(1) ` 的执行时间可能略有差别,不过你还是可以把它俩的执行效率看成是相等的。
1083
+
1084
+ 另外在 InnoDB 引擎中,如果采用` COUNT(*) ` 和` COUNT(1) ` 来统计数据行数,要尽量采用二级索引。因为主键采用的索引是聚簇索引,聚簇索引包含的信息多,明显会大于二级索引(非聚簇索引)。对于` COUNT(*) ` 和` COUNT(1) ` 来说,它们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。
1085
+
1086
+ 然而如果想要查找具体的行,那么采用主键索引的效率更高。如果有多个二级索引,会使用 key_len 小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引来进行统计。
1087
+
1088
+ 这里我总结一下:
1089
+
1090
+ 1 . 一般情况下,三者执行的效率为 ` COUNT(*) ` = ` COUNT(1) ` > ` COUNT(字段) ` 。我们尽量使用` COUNT(*) ` ,当然如果你要统计的是某个字段的非空数据行数,则另当别论,毕竟比较执行效率的前提是结果一样才可以。
1091
+ 2 . 如果要统计` COUNT(*) ` ,尽量在数据表上建立二级索引,系统会自动采用` key_len ` 小的二级索引进行扫描,这样当我们使用` SELECT COUNT(*) ` 的时候效率就会提升,有时候可以提升几倍甚至更高。
1092
+
1093
+ > ——摘自[ 极客时间 - SQL 必知必会] ( https://time.geekbang.org/column/intro/192 )
1094
+
1095
+ ### ORDER BY 是对分的组排序还是对分组中的记录排序呢?
1096
+
1097
+ ORDER BY 就是对记录进行排序。如果你在 ORDER BY 前面用到了 GROUP BY,实际上这是一种分组的聚合方式,已经把一组的数据聚合成为了一条记录,再进行排序的时候,相当于对分的组进行了排序。
1098
+
1099
+ ### SELECT 语句内部的执行步骤
1100
+
1101
+ 一条完整的 SELECT 语句内部的执行顺序是这样的:
1102
+
1103
+ 1 . FROM 子句组装数据(包括通过 ON 进行连接);
1104
+ 2 . WHERE 子句进行条件筛选;
1105
+ 3 . GROUP BY 分组 ;
1106
+ 4 . 使用聚集函数进行计算;
1107
+ 5 . HAVING 筛选分组;
1108
+ 6 . 计算所有的表达式;
1109
+ 7 . SELECT 的字段;
1110
+ 8 . ORDER BY 排序;
1111
+ 9 . LIMIT 筛选。
1112
+
1113
+ > ——摘自[ 极客时间 - SQL 必知必会] ( https://time.geekbang.org/column/intro/192 )
1114
+
1115
+ ### 解哪种情况下应该使用 EXISTS,哪种情况应该用 IN
1116
+
1117
+ 索引是个前提,其实选择与否还是要看表的大小。你可以将选择的标准理解为小表驱动大表。在这种方式下效率是最高的。
1118
+
1119
+ 比如下面这样:
1120
+
1121
+ ```
1122
+ SELECT * FROM A WHERE cc IN (SELECT cc FROM B)
1123
+ SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE B.cc=A.cc)
1124
+ ```
1125
+
1126
+ 当 A 小于 B 时,用 EXISTS。因为 EXISTS 的实现,相当于外表循环,实现的逻辑类似于:
1127
+
1128
+ ```
1129
+ for i in A
1130
+ for j in B
1131
+ if j.cc == i.cc then ...
1132
+ ```
1133
+
1134
+ 当 B 小于 A 时用 IN,因为实现的逻辑类似于:
1135
+
1136
+ ```
1137
+ for i in B
1138
+ for j in A
1139
+ if j.cc == i.cc then ...
1140
+ ```
1141
+
1142
+ 哪个表小就用哪个表来驱动,A 表小就用 EXISTS,B 表小就用 IN。
1143
+
1144
+ > ——摘自[ 极客时间 - SQL 必知必会] ( https://time.geekbang.org/column/intro/192 )
1145
+
1073
1146
## 参考资料
1074
1147
1075
1148
- [ 数据库面试题(开发者必看)] ( https://juejin.im/post/5a9ca0d6518825555c1d1acd )
@@ -1081,4 +1154,4 @@ where rr>5 and rr<=10;
1081
1154
- [ ShardingSphere 分布式事务] ( https://shardingsphere.apache.org/document/current/cn/features/transaction/ )
1082
1155
- [ mysql 和 oracle 的区别] ( https://zhuanlan.zhihu.com/p/39651803 )
1083
1156
- [ RUNOOB SQL 教程] ( https://www.runoob.com/sql/sql-tutorial.html )
1084
- - [ 如果有人问你数据库的原理,叫他看这篇文章] ( https://gameinstitute.qq.com/community/detail/107154 )
1157
+ - [ 如果有人问你数据库的原理,叫他看这篇文章] ( https://gameinstitute.qq.com/community/detail/107154 )
0 commit comments