-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathmysql.md
More file actions
6376 lines (4097 loc) · 301 KB
/
mysql.md
File metadata and controls
6376 lines (4097 loc) · 301 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: MySQL面试题,83道MySQL八股文(5.5万字331张手绘图),面渣逆袭必看👍
shortTitle: 面渣逆袭-MySQL
description: 下载次数超 1 万次,5.5 万字 331 张手绘图,详解 83 道 MySQL 面试高频题(让天下没有难背的八股),面渣背会这些 MySQL 八股文,这次吊打面试官,我觉得稳了(手动 dog)。
date: 2026-03-29
author: 三分恶
category:
- 面渣逆袭
tag:
- 面渣逆袭
head:
- - meta
- name: keywords
content: MySQL面试题,MySQL,mysql,面试题,八股文
---

## 前言
5.5 万字 331 张手绘图,详解 83 道 MySQL 面试高频题(让天下没有难背的八股),面渣背会这些 MySQL 八股文,这次吊打面试官,我觉得稳了(手动 dog)。整理:沉默王二,戳[转载链接](https://mp.weixin.qq.com/s/JFjFs_7xduCmHOegbJ-Gbg),作者:三分恶,戳[原文链接](https://mp.weixin.qq.com/s/zSTyZ-8CFalwAYSB0PN6wA)。
亮白版本更适合拿出来打印,这也是很多学生党喜欢的方式,打印出来背诵的效率会更高。

2025 年 02 月 27 日开始着手第二版更新。
- 对于高频题,会标注在《[Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)》中出现的位置,哪家公司,原题是什么,并且会加🌟,目录一目了然;如果你想节省时间的话,可以优先背诵这些题目,尽快做到知彼知己,百战不殆。
- 区分八股精华回答版本和原理底层解释,让大家知其然知其所以然,同时又能做到面试时的高效回答。
- 结合项目([技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)、[pmhub](https://javabetter.cn/zhishixingqiu/pmhub.html))来组织语言,让面试官最大程度感受到你的诚意,而不是机械化的背诵。
- 修复第一版中出现的问题,包括球友们的私信反馈,网站留言区的评论,以及 [GitHub 仓库](https://github.com/itwanger/toBeBetterJavaer/issues)中的 issue,让这份面试指南更加完善。
- 增加[二哥编程星球](https://javabetter.cn/zhishixingqiu/)的球友们拿到的一些 offer,对面渣逆袭的感谢,以及对简历修改的一些认可,以此来激励大家,给大家更多信心。
- 优化排版,增加手绘图,重新组织答案,使其更加口语化,从而更贴近面试官的预期。

由于 PDF 没办法自我更新,所以需要最新版的小伙伴,可以微信搜【**沉默王二**】,或者扫描/长按识别下面的二维码,关注二哥的公众号,回复【**222**】即可拉取最新版本。
<div style="text-align: center; margin: 20px 0;">
<img src="https://cdn.paicoding.com/tobebetterjavaer/images/gongzhonghao.png" alt="微信扫码或者长按识别,或者微信搜索“沉默王二”" style="max-width: 100%; height: auto; border-radius: 10px;" />
</div>
当然了,请允许我的一点点私心,那就是星球的 PDF 版本会比公众号早一个月时间,毕竟星球用户都付费过了,我有必要让他们先享受到一点点福利。相信大家也都能理解,毕竟在线版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精力了,大家觉得有帮助还请给个口碑,让你身边的同事、同学都能受益到。

我把二哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有面渣逆袭的版本都放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机网络、操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个大的主题,共有 40 多万字,2000+张手绘图,可以说是诚意满满。
展示一下暗黑版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服一点。

## MySQL 基础
### 0.🌟什么是 MySQL?
MySQL 是一个开源的关系型数据库,现在隶属于 Oracle 公司。是我们国内使用频率最高的一种数据库,我在本地安装的是最新的 8.3 版本。

#### 怎么删除/创建一张表?
可以使用 `DROP TABLE` 来删除表,使用 `CREATE TABLE` 来创建表。
创建表的时候,可以通过 `PRIMARY KEY` 设定主键。
```sql
CREATE TABLE users (
id INT AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100),
PRIMARY KEY (id)
);
```
#### 请写一个升序/降序的 SQL 语句?
在 SQL 中,可以使用 `ORDER BY` 子句来对查询结果进行升序或者降序。默认情况下,查询结果是升序的,如果需要降序,可以通过 `DESC` 关键字来实现。
比如说在员工表中,我们要按工资降序,就可以使用 `ORDER BY salary DESC` 来完成:
```sql
SELECT id, name, salary
FROM employees
ORDER BY salary DESC;
```
如果需对多个字段进行排序,例如按工资降序,按名字升序,就可以 `ORDER BY salary DESC, name ASC` 来完成:
```sql
SELECT id, name, salary
FROM employees
ORDER BY salary DESC, name ASC;
```
#### MySQL出现性能差的原因有哪些?
可能是 SQL 查询使用了全表扫描,也可能是查询语句过于复杂,如多表 JOIN 或嵌套子查询。
也有可能是单表数据量过大。
通常情况下,添加索引就能解决大部分性能问题。对于一些热点数据,还可以通过增加 Redis 缓存,来减轻数据库的访问压力。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:你平时用到的数据库
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的腾讯云智面经同学 16 一面面试原题:数据库用过哪些,对哪个比较熟?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 360 面经同学 3 Java 后端技术一面面试原题:用过哪些数据库
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的招商银行面经同学 6 招银网络科技面试原题:了解 MySQL、Redis 吗?
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的国企零碎面经同学 9 面试原题:数据库用什么多(说了 Mysql 和 Redis)
> 6. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的vivo 面经同学 10 技术一面面试原题:怎么删除/创建一张表和设定主键
,举例用sql实现升序降序
> 7. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的滴滴面经同学 3 网约车后端开发一面原题:MySQL性能慢的原因
### 1.两张表怎么进行连接?
可以通过内连接 `inner join`、外连接 `outer join`、交叉连接 `cross join` 来合并多个表的查询结果。
#### 什么是内连接?
内连接用于返回两个表中有匹配关系的行。假设有两张表,用户表和订单表,想查询有订单的用户,就可以使用内连接 `users INNER JOIN orders`,按照用户 ID 关联就行了。
```sql
SELECT users.name, orders.order_id
FROM users
INNER JOIN orders ON users.id = orders.user_id;
```
只有那些在两个表中都存在 user_id 的记录才会出现在查询结果中。
#### 什么是外连接?
和内连接不同,外连接不仅返回两个表中匹配的行,还返回没有匹配的行,用 `null` 来填充。
外连接又分为左外连接 `left join` 和右外连接 `right join`。
left join 会保留左表中符合条件的所有记录,如果右表中有匹配的记录,就返回匹配的记录,否则就用 null 填充,常用于某表中有,但另外一张表中可能没有的数据的查询场景。
假设要查询所有用户及他们的订单,即使用户没有下单,就可以使用左连接:
```sql
SELECT users.id, users.name, orders.order_id
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
```
查询前:
users| orders
---|---
id| name| user_id
1| 王二| 1
2| 张三| 2
3| 李四| 无
查询后:
id| name| order_id
---|---|---
1| 王二| 10
2| 张三| 20
3| 李四| null
右连接就是左连接的镜像,right join 会保留右表中符合条件的所有记录,如果左表中有匹配的记录,就返回匹配的记录,否则就用 null 填充。
#### 什么是交叉连接?
交叉连接会返回两张表的笛卡尔积,也就是将左表的每一行与右表的每一行进行组合,返回的行数是两张表行数的乘积。
假设有 A 表和 B 表,A 表有 2 行数据,B 表有 3 行数据,那么交叉连接的结果就是 2 ✖️ 3 = 6 行。
```sql
SELECT A.id, B.id
FROM A
CROSS JOIN B;
```
笛卡尔积是数学中的一个概念,例如集合 `A={a,b}`,集合 `B={0,1,2}`,那么 A✖️B=`{<a,0>,<a,1>,<a,2>,<b,0>,<b,1>,<b,2>,}`。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的用友面试原题:两张表怎么进行连接
### 2.内连接、左连接、右连接有什么区别?
MySQL 的连接主要分为内连接和外连接,外连接又可以分为左连接和右连接。

内连接可以用来找出两个表中共同的记录,相当于两个数据集的交集。
左连接和右连接可以用来找出两个表中不同的记录,相当于两个数据集的并集。两者的区别是,左连接会保留左表中符合条件的所有记录,右连接则刚好相反。
拿[技术派实战项目](https://javabetter.cn/zhishixingqiu/paicoding.html)的表为例来详细验证下。
有三张表,一张文章表 article,主要存文章标题 title, 一张文章详情表 article_detail,主要存文章的内容 content,一张文章评论表 comment,主要存评论 content,三个表通过文章 id 关联。
先来看内连接:
```sql
SELECT LEFT(a.title, 20) AS ArticleTitle, LEFT(c.content, 20) AS CommentContent
FROM article a
INNER JOIN comment c ON a.id = c.article_id
LIMIT 2;
```

返回至少有一条评论的文章标题和评论内容(前 20 个字符),只返回符合条件的前 2 条记录。
再来看做连接:
```sql
SELECT LEFT(a.title, 20) AS ArticleTitle, LEFT(c.content, 20) AS CommentContent
FROM article a
LEFT JOIN comment c ON a.id = c.article_id
LIMIT 2;
```

返回所有文章的标题和文章评论,即使某些文章没有评论(填充为 NULL)。
最后来看右连:
```sql
SELECT LEFT(a.title, 20) AS ArticleTitle, LEFT(c.content, 20) AS CommentContent
FROM comment c
RIGHT JOIN article a ON a.id = c.article_id
LIMIT 2;
```

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的腾讯 Java 后端实习一面原题:请说说 MySQL 的内联、左联、右联的区别。
memo:2025 年 2 月 27 日修改至此。给大家看[一条球友的面经](https://t.zsxq.com/1g4gI),基本上都是面渣逆袭中常见的八股,所以只要能把面渣中的高频题拿下,面试 OC 的概率真的很大,真心话。

### 3.说一下数据库的三大范式?

第一范式,确保表的每一列都是不可分割的基本数据单元,比如说用户地址,应该拆分成省、市、区、详细地址等 4 个字段。

第二范式,要求表中的每一列都和主键直接相关。比如在订单表中,商品名称、单位、商品价格等字段应该拆分到商品表中。

然后新建一个订单商品关联表,用订单编号和商品编号进行关联就好了。

第三范式,非主键列应该只依赖于主键列。比如说在设计订单信息表的时候,可以把客户名称、所属公司、联系方式等信息拆分到客户信息表中,然后在订单信息表中用客户编号进行关联。

#### 建表的时候需要考虑哪些问题?
首先需要考虑表是否符合数据库的三大范式,确保字段不可再分,消除非主键依赖,确保字段仅依赖于主键等。
然后在选择字段类型时,应该尽量选择合适的数据类型。
在字符集上,尽量选择 utf8mb4,这样不仅可以支持中文和英文,还可以支持表情符号等。
当数据量较大时,比如上千万行数据,需要考虑分表。比如订单表,可以采用水平分表的方式来分散单表存储压力。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 13 Java 后端二面面试原题:什么是三大范式,为什么要有三大范式,什么场景下不用遵循三大范式,举一个场景
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 5 Java 后端技术一面面试原题:建表考虑哪些问题
### 4.varchar 与 char 的区别?
varchar 是可变长度的字符类型,原则上最多可以容纳 65535 个字符,但考虑字符集,以及 MySQL 需要 1 到 2 个字节来表示字符串长度,所以实际上最大可以设置到 65533。
>latin1 字符集,且列属性定义为 NOT NULL。

char 是固定长度的字符类型,当定义一个 `CHAR(10)` 字段时,不管实际存储的字符长度是多少,都只会占用 10 个字符的空间。如果插入的数据小于 10 个字符,剩余的部分会用空格填充。
值| CHAR(4)| 存储需求(字节)| VARCHAR(4)| 存储需求(字节)
---|---|---|---|---
''| ' '| 4| ''| 1
'ab'| 'ab '| 4| 'ab'| 3
'abcd'| 'abcd'| 4| 'abcd'| 5
'abcdefgh'| 'abcd'| 4| 'abcd'| 5
### 5.blob 和 text 有什么区别?
blob 用于存储二进制数据,比如图片、音频、视频、文件等;但实际开发中,我们都会把这些文件存储到 OSS 或者文件服务器上,然后在数据库中存储文件的 URL。
text 用于存储文本数据,比如文章、评论、日志等。
>memo:2025 年 2 月 28 日修改至此。今天有球友反馈拿到了理想汽车的补录 offer, 真的恭喜了!

### 6.DATETIME 和 TIMESTAMP 有什么区别?
DATETIME 直接存储日期和时间的完整值,与时区无关。
TIMESTAMP 存储的是 Unix 时间戳,1970-01-01 00:00:01 UTC 以来的秒数,受时区影响。

另外,DATETIME 的默认值为 null,占用 8 个字节;TIMESTAMP 的默认值为当前时间——CURRENT_TIMESTAMP,占 4 个字节,实际开发中更常用,因为可以自动更新。

### 7.in和exists的区别?
当使用 IN 时,MySQL 会首先执行子查询,然后将子查询的结果集用于外部查询的条件。这意味着子查询的结果集需要全部加载到内存中。
而 EXISTS 会对外部查询的每一行,执行一次子查询。如果子查询返回任何行,则 `EXISTS` 条件为真。`EXISTS` 关注的是子查询是否返回行,而不是返回的具体值。
```sql
-- IN 的临时表可能成为性能瓶颈
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
-- EXISTS 可以利用关联索引
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o
WHERE o.user_id = u.id AND o.amount > 100);
```
`IN` 适用于子查询结果集较小的情况。如果子查询返回大量数据,`IN` 的性能可能会下降,因为它需要将整个结果集加载到内存。
而 EXISTS 适用于子查询结果集可能很大的情况。由于 `EXISTS` 只需要判断子查询是否返回行,而不需要加载整个结果集,因此在某些情况下性能更好,特别是当子查询可以使用索引时。
#### NULL值陷了解吗?
`IN`: 如果子查询的结果集中包含 `NULL` 值,可能会导致意外的结果。例如,`WHERE column IN (subquery)`,如果 `subquery` 返回 `NULL`,则 `column IN (subquery)` 永远不会为真,除非 `column` 本身也为 `NULL`。
`EXISTS`: 对 `NULL` 值的处理更加直接。`EXISTS` 只是检查子查询是否返回行,不关心行的具体值,因此不受 `NULL` 值的影响。
memo:2025 年 3 月 1 日修改至此。
### 8.记录货币用什么类型比较好?
如果是电商、交易、账单等涉及货币的场景,建议使用 DECIMAL 类型,因为 DECIMAL 类型是精确数值类型,不会出现浮点数计算误差。
例如,`DECIMAL(19,4)` 可以存储最多 19 位数字,其中 4 位是小数。
```sql
CREATE TABLE orders (
id INT AUTO_INCREMENT,
amount DECIMAL(19,4),
PRIMARY KEY (id)
);
```
如果是银行,涉及到支付的场景,建议使用 BIGINT 类型。可以将货币金额乘以一个固定因子,比如 100,表示以“分”为单位,然后存储为 `BIGINT`。这种方式既避免了浮点数问题,同时也提供了不错的性能。但在展示的时候需要除以相应的因子。
#### 为什么不推荐使用 FLOAT 或 DOUBLE?
因为 FLOAT 和 DOUBLE 都是浮点数类型,会存在精度问题。
在许多编程语言中,`0.1 + 0.2` 的结果会是类似 `0.30000000000000004` 的值,而不是预期的 `0.3`。
### 9.🌟怎么存储 emoji?
因为 emoji(😊)是 4 个字节的 UTF-8 字符,而 MySQL 的 utf8 字符集只支持最多 3 个字节的 UTF-8 字符,所以在 MySQL 中存储 emoji 时,需要使用 utf8mb4 字符集。
```sql
ALTER TABLE mytable CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
MySQL 8.0 已经默认支持 utf8mb4 字符集,可以通过 `SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';` 查看。

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 13 Java 后端二面面试原题:mysql 怎么存 emoji,怎么编码
### 10.drop、delete 与 truncate 的区别?
DROP 是物理删除,用来删除整张表,包括表结构,且不能回滚。
DELETE 支持行级删除,可以带 WHERE 条件,可以回滚。
TRUNCATE 用于清空表中的所有数据,但会保留表结构,不能回滚。
memo:2025 年 3 月 4 日修改至此。给大家传递一个喜报,一位球友拿到了科大讯飞的 offer,这薪资在合肥真的会很香。

### 11.UNION 与 UNION ALL 的区别?
UNION 会自动去除合并后结果集中的重复行。UNION ALL 不会去重,会将所有结果集合并起来。
### 12.count(1)、count(\*) 与 count(列名) 的区别?
在 InnoDB 引擎中,`COUNT(1)` 和 `COUNT(*)` 没有区别,都是用来统计所有行,包括 NULL。
如果表有索引,`COUNT(*)` 会直接用索引统计,而不是全表扫描,而 `COUNT(1)` 也会被 MySQL 优化为 `COUNT(*)`。
`COUNT(列名)` 只统计列名不为 NULL 的行数。
```
-- 假设 users 表:
+----+-------+------------+
| id | name | email |
+----+-------+------------+
| 1 | 张三 | zhang@xx.com |
| 2 | 李四 | NULL |
| 3 | 王二 | wang@xx.com |
+----+-------+------------+
-- COUNT(*)
SELECT COUNT(*) FROM users;
-- 结果:3 (统计所有行)
-- COUNT(1)
SELECT COUNT(1) FROM users;
-- 结果:3 (统计所有行)
-- COUNT(email)
SELECT COUNT(email) FROM users;
-- 结果:2 (NULL 不计入统计)
```
这里解释一下,假设有这样一张表:
```sql
CREATE TABLE t1 (
id INT,
name VARCHAR(50),
value INT
);
```
插入的数据为:
```sql
INSERT INTO t1 VALUES
(1, 'A', 10),
(2, 'B', NULL), -- NULL in value column
(3, 'C', 30),
(4, NULL, 40), -- NULL in name column
(5, 'E', NULL); -- NULL in value column
```
因为 id 列没有索引,所以 `select count(*)` 是全表扫描。

然后我们给 id 列加上索引。
```sql
alter table t1 add primary key (id);
```

再来看一下 `select count(*)`,发现用了索引(MySQL 默认为给主键添加索引)。

另外,MySQL 8.0 官方手册有明确说明,InnoDB 引擎对 `SELECT COUNT(*)` 和 `SELECT COUNT(1)` 的处理方式完全一致,性能并无差异。

memo:2025 年 3 月 5 日修改至此。再晒一个喜报给正在刷八股的你,[一位球友](https://javabetter.cn/zhishixingqiu/)拿到了咪咕的大模型应用开发,很不错的方向,恭喜了!给你也加加好运🍀buff,你也加把劲。

### 13.SQL 查询语句的执行顺序了解吗?
了解。先执行 FROM 确定主表,再执行 JOIN 连接,然后 WHERE 进行过滤,接着 GROUP BY 进行分组,HAVING 过滤聚合结果,SELECT 选择最终列,ORDER BY 排序,最后 LIMIT 限制返回行数。
WHERE 先执行是为了减少数据量,HAVING 只能过滤聚合数据,ORDER BY 必须在 SELECT 之后排序最终结果,LIMIT 最后执行以减少数据传输。

执行顺序| SQL 关键字| 作用
---|---|---
①| FROM| 确定主表,准备数据
②| ON| 连接多个表的条件
③| JOIN| 执行 INNER JOIN / LEFT JOIN 等
④| WHERE| 过滤行数据(提高效率)
⑤| GROUP BY| 进行分组
⑥| HAVING| 过滤聚合后的数据
⑦| SELECT| 选择最终返回的列
⑧| DISTINCT| 进行去重
⑨| ORDER BY| 对最终结果排序
⑩| LIMIT| 限制返回行数
这个执行顺序与编写 SQL 语句的顺序不同,这也是为什么有时候在 SELECT 子句中定义的别名不能在 WHERE 子句中使用得原因,因为 WHERE 是在 SELECT 之前执行的。
#### LIMIT 为什么在最后执行?
因为 LIMIT 是在最终结果集上执行的,如果在 WHERE 之前执行 LIMIT,那么就会先返回所有行,然后再进行 LIMIT 限制,这样会增加数据传输的开销。
#### ORDER BY 为什么在 SELECT 之后执行?
因为排序需要基于最终返回的列,如果 ORDER BY 早于 SELECT 执行,计算 `COUNT(*)` 之类的聚合函数就会出问题。
```sql
SELECT name, COUNT(*) AS order_count
FROM orders
GROUP BY name
ORDER BY order_count DESC;
```
### 14.介绍一下 MySQL 的常用命令(补充)
> 2024 年 03 月 13 日增补。

MySQL 的常用命令主要包括数据库操作命令、表操作命令、行数据 CRUD 命令、索引和约束的创建修改命令、用户和权限管理的命令、事务控制的命令等。
#### 说说数据库操作命令?
`CREATE DATABASE database_name;` 用于创建数据库;`DROP DATABASE database_name;` 用于删除数据库;`SHOW DATABASES;` 用于显示所有数据库;`USE database_name;` 用于切换数据库。
#### 说说表操作命令?
`CREATE TABLE table_name (列名1 数据类型1, 列名2 数据类型2,...);` 用于创建表;`DROP TABLE table_name;` 用于删除表;`SHOW TABLES;` 用于显示所有表;`DESCRIBE table_name;` 用于查看表结构;`ALTER TABLE table_name ADD column_name datatype;` 用于修改表。
#### 说说行数据的 CRUD 命令?
`INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);` 用于插入数据;`SELECT column_names FROM table_name WHERE condition;` 用于查询数据;`UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition;` 用于更新数据;`DELETE FROM table_name WHERE condition;` 用于删除数据。
#### 说说索引和约束的创建修改命令?
`CREATE INDEX index_name ON table_name (column_name);` 用于创建索引;`ALTER TABLE table_name ADD PRIMARY KEY (column_name);` 用于添加主键;`ALTER TABLE table_name ADD CONSTRAINT fk_name FOREIGN KEY (column_name) REFERENCES parent_table (parent_column_name);` 用于添加外键。
#### 说说用户和权限管理的命令?
`CREATE USER 'username'@'host' IDENTIFIED BY 'password';` 用于创建用户;`GRANT ALL PRIVILEGES ON database_name.table_name TO 'username'@'host';` 用于授予权限;`REVOKE ALL PRIVILEGES ON database_name.table_name FROM 'username'@'host';` 用于撤销权限;`DROP USER 'username'@'host';` 用于删除用户。
#### 说说事务控制的命令?
`START TRANSACTION;` 用于开始事务;`COMMIT;` 用于提交事务;`ROLLBACK;` 用于回滚事务。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的用友金融一面原题:介绍一下 MySQL 的常用命令
### 15.MySQL bin 目录下的可执行文件了解吗(补充)
> 2024 年 03 月 13 日增补
推荐阅读:[MySQL bin 目录下的一些可执行文件](https://javabetter.cn/mysql/bin.html)
了解的。MySQL 的 bin 目录下有很多可执行文件,主要用于管理 MySQL 服务器、数据库、表、数据等。比如说:
- mysql:用于连接 MySQL 服务器
- mysqldump:用于数据库备份,对数据备份、迁移或恢复时非常有用
- mysqladmin:用来执行一些管理操作,比如说创建数据库、删除数据库、查看 MySQL 服务器的状态等。
- mysqlcheck:用于检查、修复、分析和优化数据库表,对数据库的维护和性能优化非常有用。
- mysqlimport:用于从文本文件中导入数据到数据库表中,适合批量数据导入。
- mysqlshow:用于显示 MySQL 数据库服务器中的数据库、表、列等信息。
- mysqlbinlog:用于查看 MySQL 二进制日志文件的内容,可以用于恢复数据、查看数据变更等。
### 16.MySQL 第 3-10 条记录怎么查?(补充)
> 2024 年 03 月 30 日增补
可以使用 limit 语句,结合偏移量和行数来实现。
```sql
SELECT * FROM table_name LIMIT 2, 8;
```
limit 语句用于限制查询结果的数量,偏移量表示从哪条记录开始,行数表示返回的记录数量。
- 2:偏移量,表示跳过前两条记录,从第三条记录开始。
- 8:行数,表示从偏移量开始,返回 8 条记录。
偏移量是从 0 开始的,即第一条记录的偏移量是 0;如果想从第 3 条记录开始,偏移量就应该是 2。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 16 暑期实习一面面试原题:MySQL 第 3-10 条记录怎么查?
### 17.用过哪些 MySQL 函数?(补充)
> 2024 年 04 月 12 日增补
用过挺多的,比如说处理字符串的函数:
- `CONCAT()`: 用于连接两个或多个字符串。
- `LENGTH()`: 用于返回字符串的长度。
- `SUBSTRING()`: 从字符串中提取子字符串。
- `REPLACE()`: 替换字符串中的某部分。
- `TRIM()`: 去除字符串两侧的空格或其他指定字符。
实测数据:
```sql
-- 连接字符串
SELECT CONCAT('沉默', ' ', '王二') AS concatenated_string;
-- 获取字符串长度
SELECT LENGTH('沉默 王二') AS string_length;
-- 提取子字符串
SELECT SUBSTRING('沉默 王二', 1, 5) AS substring;
-- 替换字符串内容
SELECT REPLACE('沉默 王二', '王二', 'MySQL') AS replaced_string;
-- 去除字符串两侧的空格
SELECT TRIM(' 沉默 王二 ') AS trimmed_string;
```
处理数字的函数:
- `ABS()`: 返回一个数的绝对值。
- `ROUND()`: 四舍五入到指定的小数位数。
- `MOD()`: 返回除法操作的余数。
实测数据:
```sql
-- 返回绝对值
SELECT ABS(-123) AS absolute_value;
-- 四舍五入
SELECT ROUND(123.4567, 2) AS rounded_value;
-- 余数
SELECT MOD(10, 3) AS modulus;
```
日期和时间处理函数:
- `NOW()`: 返回当前的日期和时间。
- `CURDATE()`: 返回当前的日期。
实测数据:
```sql
-- 返回当前日期和时间
SELECT NOW() AS current_date_time;
-- 返回当前日期
SELECT CURDATE() AS current_date;
```
汇总函数:
- `SUM()`: 计算数值列的总和。
- `AVG()`: 计算数值列的平均值。
- `COUNT()`: 计算某列的行数。
实测数据:
```sql
-- 创建一个表并插入数据进行聚合查询
CREATE TABLE sales (
product_id INT,
sales_amount DECIMAL(10, 2)
);
INSERT INTO sales (product_id, sales_amount) VALUES (1, 100.00);
INSERT INTO sales (product_id, sales_amount) VALUES (1, 150.00);
INSERT INTO sales (product_id, sales_amount) VALUES (2, 200.00);
-- 计算总和
SELECT SUM(sales_amount) AS total_sales FROM sales;
-- 计算平均值
SELECT AVG(sales_amount) AS average_sales FROM sales;
-- 计算总行数
SELECT COUNT(*) AS total_entries FROM sales;
```
逻辑函数:
- `IF()`: 如果条件为真,则返回一个值;否则返回另一个值。
- `CASE`: 根据一系列条件返回值。
```sql
-- IF函数
SELECT IF(1 > 0, 'True', 'False') AS simple_if;
-- CASE表达式
SELECT CASE WHEN 1 > 0 THEN 'True' ELSE 'False' END AS case_expression;
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为 OD 面经同学 1 一面面试原题:用过哪些 MySQL 函数?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 小公司面经合集好未来测开面经同学 3 测开一面面试原题:知道 MySQL 的哪些函数,如 order by count()
### 18.说说 SQL 的隐式数据类型转换?(补充)
> 2024 年 04 月 25 日增补
当一个整数和一个浮点数相加时,整数会被转换为浮点数。
```sql
SELECT 1 + 1.0; -- 结果为 2.0
```
当一个字符串和一个整数相加时,字符串会被转换为整数。
```sql
SELECT '1' + 1; -- 结果为 2
```
隐式转换会导致意想不到的结果,最好通过显式转换来规避。
```sql
SELECT CAST('1' AS SIGNED INTEGER) + 1; -- 结果为 2
```
实际验证结果:

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小公司面经合集同学 1 Java 后端面试原题:说说 SQL 的隐式数据类型转换?
memo:2025 年 3 月 6 日修改至此。
### 19. 说说 SQL 的语法树解析?(补充)
> 2024 年 09 月 19 日增补
SQL 语法树解析是将 SQL 查询语句转换成抽象语法树 —— AST 的过程,是数据库引擎处理查询的第一步,也是防止 SQL 注入的重要手段。
通常分为 3 个阶段。
第一个阶段,词法分析:拆解 SQL 语句,识别关键字、表名、列名等。
---这部分是帮助大家理解 start,面试中可不背---
比如说:
```sql
SELECT id, name FROM users WHERE age > 18;
```
将会被拆解为:
```
[SELECT] [id] [,] [name] [FROM] [users] [WHERE] [age] [>] [18] [;]
```
---这部分是帮助大家理解 end,面试中可不背---
第二个阶段,语法分析:检查 SQL 是否符合语法规则,并构建抽象语法树。
---这部分是帮助大家理解 start,面试中可不背---
比如说上面的语句会被构建成如下的语法树:
```
SELECT
/ \
Columns FROM
/ \ |
id name users
|
WHERE
|
age > 18
```
或者这样表示:
```
SELECT
├── COLUMNS: id, name
├── FROM: users
├── WHERE
│ ├── CONDITION: age > 18
```
---这部分是帮助大家理解 end,面试中可不背---
第三个阶段,语义分析:检查表、列是否存在,进行权限验证等。
---这部分是帮助大家理解 start,面试中可不背---
比如说执行:
```sql
SELECT id, name FROM users WHERE age > 'eighteen';
```
会报错:
```
ERROR: Column 'age' is INT, but 'eighteen' is STRING.
```
---这部分是帮助大家理解 end,面试中可不背---
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 21 抖音商城一面面试原题:sql的语法树解析
memo:2025 年 3 月 7 日 修改至此。再晒一个 offer,一位球友拿到了经纬恒润的实习 offer,并且直言面试了很多场,我说超过 5 次的题目基本上都碰到了,啥都别说了,面渣逆袭 YYDS。

## 数据库架构
### 20.说说 MySQL 的基础架构?
MySQL 采用分层架构,主要包括连接层、服务层、和存储引擎层。

①、连接层主要负责客户端连接的管理,包括验证用户身份、权限校验、连接管理等。可以通过数据库连接池来提升连接的处理效率。
②、服务层是 MySQL 的核心,主要负责查询解析、优化、执行等操作。在这一层,SQL 语句会经过解析、优化器优化,然后转发到存储引擎执行,并返回结果。这一层包含查询解析器、优化器、执行计划生成器、日志模块等。
③、存储引擎层负责数据的实际存储和提取。MySQL 支持多种存储引擎,如 InnoDB、MyISAM、Memory 等。
#### binlog写入在哪一层?
binlog 在服务层,负责记录 SQL 语句的变化。它记录了所有对数据库进行更改的操作,用于数据恢复、主从复制等。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 21 抖音商城一面面试原题:mysql分为几层?binlog写入在哪一层
### 21.🌟一条查询语句是如何执行的?
当我们执行一条 SELECT 语句时,MySQL 并不会直接去磁盘读取数据,而是经过 6 个步骤来解析、优化、执行,然后再返回结果。

第一步,客户端发送 SQL 查询语句到 MySQL 服务器。
第二步,MySQL 服务器的连接器开始处理这个请求,跟客户端建立连接、获取权限、管理连接。
第三步,解析器对 SQL 语句进行解析,检查语句是否符合 SQL 语法规则,确保数据库、表和列都是存在的,并处理 SQL 语句中的名称解析和权限验证。
第四步,优化器负责确定 SQL 语句的执行计划,这包括选择使用哪些索引,以及决定表之间的连接顺序等。
第五步,执行器会调用存储引擎的 API 来进行数据的读写。
第六步,存储引擎负责查询数据,并将执行结果返回给客户端。客户端接收到查询结果,完成这次查询请求。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 2 Java 后端技术一面面试原题:MySQL 执行语句的整个过程了解吗?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 18 成都到家面试原题:mysql一条数据的查询过程
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学19番茄小说一面面试原题:MySQL中一条SQL的执行流程
memo:2025 年 3 月 8 日修改至此。
### 22.一条更新语句是如何执行的?
总的来说,一条 UPDATE 语句的执行过程包括读取数据页、加锁解锁、事务提交、日志记录等多个步骤。

拿 `update test set a=1 where id=2` 举例来说:
在事务开始前,MySQL 需要记录undo log,用于事务回滚。
操作|id|旧值|新值
---|---|---|---
update|2|N|1
除了记录 undo log,存储引擎还会将更新操作写入 redo log,状态标记为 prepare,并确保 redo log 持久化到磁盘。这一步可以保证即使系统崩溃,数据也能通过 redo log 恢复到一致状态。
写完 redo log 后,MySQL 会获取行锁,将 a 的值修改为 1,标记为脏页,此时数据仍然在内存的 buffer pool 中,不会立即写入磁盘。后台线程会在适当的时候将脏页刷盘,以提高性能。
最后提交事务,redo log 中的记录被标记为 committed,行锁释放。
如果 MySQL 开启了 binlog,还会将更新操作记录到 binlog 中,主要用于主从复制。
以及数据恢复,可以结合 redo log 进行点对点的恢复。binlog 的写入通常发生在事务提交时,与 redo log 共同构成“两阶段提交”,确保两者的一致性。
注意,redo log 的写入有两个阶段的提交,一是 binlog 写入之前`prepare` 状态的写入,二是 binlog 写入之后 `commit` 状态的写入。
memo:2025 年 3 月 9 日修改至此。
### 23.说说 MySQL 的段区页行(补充)
> 2024 年 04 月 26 日增补
推荐阅读:[了解 MySQL的数据行、行溢出机制吗?](https://www.cnblogs.com/ZhuChangwu/p/14035330.html)
MySQL 是以表的形式存储数据的,而表空间的结构则由段、区、页、行组成。

①、段:表空间由多个段组成,常见的段有数据段、索引段、回滚段等。
创建索引时会创建两个段,数据段和索引段,数据段用来存储叶子节点中的数据;索引段用来存储非叶子节点的数据。
回滚段包含了事务执行过程中用于数据回滚的旧数据。
②、区:段由一个或多个区组成,区是一组连续的页,通常包含 64 个连续的页,也就是 1M 的数据。
使用区而非单独的页进行数据分配可以优化磁盘操作,减少磁盘寻道时间,特别是在大量数据进行读写时。
③、页:页是 InnoDB 存储数据的基本单元,标准大小为 16 KB,索引树上的一个节点就是一个页。
也就意味着数据库每次读写都是以 16 KB 为单位的,一次最少从磁盘中读取 16KB 的数据到内存,一次最少写入 16KB 的数据到磁盘。
④、行:InnoDB 采用行存储方式,意味着数据按照行进行组织和管理,行数据可能有多个格式,比如说 COMPACT、REDUNDANT、DYNAMIC 等。
MySQL 8.0 默认的行格式是 DYNAMIC,由COMPACT 演变而来,意味着这些数据如果超过了页内联存储的限制,则会被存储在溢出页中。
可以通过 `show table status like '%article%'` 查看行格式。

<MZNXQRcodeBanner />
## 存储引擎
### 24.🌟MySQL 有哪些常见存储引擎?
MySQL 支持多种存储引擎,常见的有 MyISAM、InnoDB、MEMORY 等。

---这部分是帮助大家理解 start,面试中可不背---
我来做一个表格对比:
| 功能 | InnoDB | MyISAM | MEMORY |
| ------------- | ------ | ------ | ------ |
| 支持事务 | Yes | No | No |
| 支持全文索引 | Yes | Yes | No |
| 支持 B+树索引 | Yes | Yes | Yes |
| 支持哈希索引 | Yes | No | Yes |
| 支持外键 | Yes | No | No |
---这部分是帮助大家理解 end,面试中可不背---
除此之外,我还了解到:
①、MySQL 5.5 之前,默认存储引擎是 MyISAM,5.5 之后是 InnoDB。
②、InnoDB 支持的哈希索引是自适应的,不能人为干预。
③、InnoDB 从 MySQL 5.6 开始,支持全文索引。
④、InnoDB 的最小表空间略小于 10M,最大表空间取决于页面大小。

#### 如何切换 MySQL 的数据引擎?
可以通过 alter table 语句来切换 MySQL 的数据引擎。
```sql
ALTER TABLE your_table_name ENGINE=InnoDB;
```
不过不建议,应该提前设计好到底用哪一种存储引擎。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:MySQL 支持哪些存储引擎?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的用友面试原题:innodb 引擎和 hash 引擎有什么区别
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的国企零碎面经同学 9 面试原题:MySQL 的存储引擎
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东同学 4 云实习面试原题:mysql的数据引擎有哪些, 区别(innodb,MyISAM,Memory)
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的阿里系面经同学 19 饿了么面试原题:存储引擎介绍
memo:2025 年 3 月 10 日修改至此。
### 25.存储引擎应该怎么选择?
大多数情况下,使用默认的 InnoDB 就可以了,InnoDB 可以提供事务、行级锁、外键、B+ 树索引等能力。
MyISAM 适合读多写少的场景。
MEMORY 适合临时表,数据量不大的情况。因为数据都存放在内存,所以速度非常快。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手同学 2 一面面试原题:MySQL的InnoDB特点?为什么用B+树?而不是B树,区别?
### 26.InnoDB 和 MyISAM 主要有什么区别?
InnoDB 和 MyISAM 的最大区别在于事务支持和锁机制。InnoDB 支持事务、行级锁,适合大多数业务系统;而 MyISAM 不支持事务,用的是表锁,查询快但写入性能差,适合读多写少的场景。

另外,从存储结构上来说,MyISAM 用三种格式的文件来存储,.frm 文件存储表的定义;.MYD 存储数据;.MYI 存储索引;而 InnoDB 用两种格式的文件来存储,.frm 文件存储表的定义;.ibd 存储数据和索引。
从索引类型上来说,MyISAM 为非聚簇索引,索引和数据分开存储,索引保存的是数据文件的指针。

InnoDB 为聚簇索引,索引和数据不分开。

更细微的层面上来讲,MyISAM 不支持外键,可以没有主键,表的具体行数存储在表的属性中,查询时可以直接返回;InnoDB 支持外键,必须有主键,具体行数需要扫描整个表才能返回,有索引的情况下会扫描索引。
#### InnoDB的内存结构了解吗?
> 2025 年 04 月 04 日增补
InnoDB 的内存区域主要有两块,buffer pool 和 log buffer。
buffer pool 用于缓存数据页和索引页,提升读写性能;log buffer 用于缓存 redo log,提升写入性能。

#### 数据页的结构了解吗?
InnoDB 的数据页由 7 部分组成,其中文件头、页头和文件尾的大小是固定的,分别为 38、56 和 8 个字节,用来标记该页的一些信息。行记录、空闲空间和页目录的大小是动态的,为实际的行记录存储空间。

来个表格总结下: