-
Notifications
You must be signed in to change notification settings - Fork 199
/
MySql.md
2136 lines (1083 loc) · 89.9 KB
/
MySql.md
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
# MySql
# Interview
## 1.数据库连接
**executeUpdate(sql)**
用于执行给定 SQL 语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句。
返回值是更新的条数。
**executeQuery(sql)**
这个方法被用来执行 SELECT 语句,返回代表查询结果的ResultSet对象。
## 2.关系型数据库和非关系数据库的区别?(建议可以通过举例子介绍)
**什么是关系型数据库?**
- 关系型数据库是依据**关系模型**来创建的数据库。
- 所谓关系模型就是“一对一、一对多、多对多”等关系模型,关系模型就是指二维表格模型,因而一个关系型数据库就是由二维表及其之间的联系组成的一个数据组织。
- 关系型数据可以很好地存储一些关系模型的数据,比如一个老师对应多个学生的数据(“多对多”),一本书对应多个作者(“一对多”),一本书对应一个出版日期(“一对一”)
- 关系模型是我们生活中能经常遇见的模型,存储这类数据一般用关系型数据库
- 关系模型包括数据结构(数据存储的问题,二维表)、操作指令集合(SQL语句)、完整性约束(表内数据约束、表与表之间的约束)。
数据的存储形式:
![img](../media/pictures/MySql.assets/940884-20190129111427216-1965281810.png)
关系的处理形式:
![img](../media/pictures/MySql.assets/940884-20190129111443667-1089405966.png)
下面是一个E-R图,是多个模型一起用的情况:
![img](../media/pictures/MySql.assets/940884-20190129111500436-1212656786.png)
**常见的关系型数据库:**
Oracle、DB2、PostgreSQL、Microsoft SQL Server、Microsoft Access、MySQL
**关系型数据库的特点:**
安全(因为存储在磁盘中,不会说突然断电数据就没有了)、
容易理解(建立在关系模型上)、
但不节省空间(因为建立在关系模型上,就要遵循某些规则,好比数据中某字段值即使为空仍要分配空间)
**什么是非关系型数据库?**
- 非关系型数据库主要是基于“**非关系模型**”的数据库(由于关系型太大,所以一般用“非关系型”来表示其他类型的数据库)
- 非关系型模型比如有:
- 列模型:存储的数据是一列列的。关系型数据库以一行作为一个记录,列模型数据库以一列为一个记录。(这种模型,数据即索引,IO很快,主要是一些分布式数据库)
- 键值对模型:存储的数据是一个个“键值对”,比如name:liming,那么name这个键里面存的值就是liming
- 文档类模型:以一个个文档来存储数据,有点类似“键值对”。
**常见非关系模型数据库:**
列模型:Hbase
![img](../media/pictures/MySql.assets/940884-20190129111702299-1368712722.png)
键值对模型:redis,MemcacheDB
![img](../media/pictures/MySql.assets/940884-20190129111711788-545661817.png)
文档类模型:mongoDB
**非关系型数据库的特点:**
效率高(因为存储在内存中)、
但不安全(断电丢失数据,但其中redis可以同步数据到磁盘中),现在很多非关系型数据库都开始支持转存到磁盘中。
参考:https://www.cnblogs.com/ranyonsue/p/10333203.html
## 3.数据库命名规范:
- 所有数据库对象名称必须使用小写字母并用下划线分割
- 所有数据库对象名称禁止使用 MySQL 保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)
- 数据库对象的命名要能做到见名识意,并且最后不要超过 32 个字符
- 临时库表必须以 tmp_为前缀并以日期为后缀,备份表必须以 bak_为前缀并以日期 (时间戳) 为后缀
- 所有存储相同数据的列名和列类型必须一致(一般作为关联列,如果查询时关联列类型不一致会自动进行数据类型隐式转换,会造成列上的索引失效,导致查询效率降低
## 4.SQL索引是什么,索引优化,索引为什么会失效?
#### 首先说什么是索引?
**索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B树, B+树和Hash。**
索引的作用就相当于目录的作用。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。
那索引为什么会失效?可以用下面第一条来回答。
30%这个范围。 其他的就不知道啦,后续仔细研究(***********)
#### 索引优化规则
王道当时有一个文档:Mysql索引及其优化(王道的文档,有些粗略,没讲模糊查询左右之分)
**1)如果MySQL估计使用索引比全表扫描还慢,则不会使用索引。**
返回数据的比例是重要的指标,比例越低越容易命中索引。记住这个范围值——30%,后面所讲的内容都是建立在返回数据的比例在30%以内的基础上。
**2)前导模糊查询不能命中索引。**
name列创建普通索引:
![img](../media/pictures/MySql.assets/169fab4ad1e262c7)
前导模糊查询不能命中索引:
EXPLAIN SELECT * FROM user WHERE name LIKE '%s%';
![img](../media/pictures/MySql.assets/169fab4ae00ad805)
非前导模糊查询则可以使用索引,可优化为使用非前导模糊查询:
EXPLAIN SELECT * FROM user WHERE name LIKE 's%';
![img](../media/pictures/MySql.assets/169fab4ae1b1e740)
**3)数据类型出现隐式转换的时候不会命中索引,特别是当列类型是字符串,一定要将字符常量值用引号引起来。**
EXPLAIN SELECT * FROM user WHERE name=1;
![img](../media/pictures/MySql.assets/169fab4aeeea3142)
EXPLAIN SELECT * FROM user WHERE name='1';
![img](../media/pictures/MySql.assets/169fab4af64502e8)
**4)复合索引的情况下,查询条件不包含索引列最左边部分(不满足最左原则),不会命中符合索引。**
(是不是相当于没有B+树的上面一层,自然找不到下面一层? 有待考究 )
name,age,status列创建复合索引:
ALTER TABLE user ADD INDEX index_name (name,age,status);
![img](../media/pictures/MySql.assets/169fab4ae468dd5f)
user表索引详情:
SHOW INDEX FROM user;
![img](../media/pictures/MySql.assets/169fab4ae260d343)
根据最左原则,可以命中复合索引index_name:
EXPLAIN SELECT * FROM user WHERE name='swj' AND status=1;
![img](../media/pictures/MySql.assets/169fab4affd9796f)
注意,最左原则并不是说是查询条件的顺序:
EXPLAIN SELECT * FROM user WHERE status=1 AND name='swj';
![img](../media/pictures/MySql.assets/169fab4b0047390e)
而是查询条件中是否包含索引最左列字段:
EXPLAIN SELECT * FROM user WHERE status=2 ;
![img](../media/pictures/MySql.assets/169fab4b06548ad2)
**5)union、in、or都能够命中索引,建议使用in。**
union:
EXPLAIN SELECT*FROM user WHERE status=1
UNION ALL
SELECT*FROM user WHERE status = 2;
![img](../media/pictures/MySql.assets/169fab4b06d96e59)
in:
EXPLAIN SELECT * FROM user WHERE status IN (1,2);
![img](../media/pictures/MySql.assets/169fab4b14bfcb89)
or:
EXPLAIN SELECT*FROM user WHERE status=1OR status=2;
![img](../media/pictures/MySql.assets/169fab4b1c978ec4)
查询的CPU消耗:or>in>union。
**6)用or分割开的条件,如果or前的条件中列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到。**
EXPLAIN SELECT * FROM payment WHERE customer_id = 203 OR amount = 3.96;
![img](../media/pictures/MySql.assets/169fab4b23724f6c)
因为or后面的条件列中没有索引,那么后面的查询肯定要走全表扫描,在存在全表扫描的情况下,就没有必要多一次索引扫描增加IO访问。
**7)负向条件查询不能使用索引,可以优化为in查询。**
负向条件有:**!=、<>、not in、not exists、not like**等。
status列创建索引:
ALTER TABLE user ADD INDEX index_status (status);
![img](../media/pictures/MySql.assets/169fab4b25c75f18)
user表索引详情:
SHOW INDEX FROM user;
![img](../media/pictures/MySql.assets/169fab4b2c6ecb0a)
负向条件不能命中缓存:
EXPLAIN SELECT * FROM user WHERE status !=1 AND status != 2;
![img](../media/pictures/MySql.assets/169fab4b3331840f)
可以优化为in查询,但是前提是区分度要高,返回数据的比例在30%以内:
EXPLAIN SELECT * FROM user WHERE status IN (0,3,4);
![img](../media/pictures/MySql.assets/169fab4b38f8b6a9)
**8)范围条件查询可以命中索引。范围条件有:<、<=、>、>=、between等。**
status,age列分别创建索引:
ALTER TABLE user ADD INDEX index_status (status);
![img](../media/pictures/MySql.assets/169fab4b25c75f18-1587224765941)
ALTER TABLE user ADD INDEX index_age (age);
![img](../media/pictures/MySql.assets/169fab4b4082bcd3)
user表索引详情:
SHOW INDEX FROM user;
![img](../media/pictures/MySql.assets/169fab4b555acbb1)
范围条件查询可以命中索引:
EXPLAIN SELECT * FROM user WHERE status>5;
![img](../media/pictures/MySql.assets/169fab4b4450f7a8)
范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引,索引最多用于一个范围列,如果查询条件中有两个范围列则无法全用到索引:
EXPLAIN SELECT * FROM user WHERE status>5 AND age<24;
![img](../media/pictures/MySql.assets/169fab4b563095df)
如果是范围查询和等值查询同时存在,优先匹配等值查询列的索引:
EXPLAIN SELECT * FROM user WHERE status>5 AND age=24;
![img](../media/pictures/MySql.assets/169fab4b5cd19ef9)
**8)数据库执行计算不会命中索引。**
EXPLAIN SELECT * FROM user WHERE age>24;
![img](../media/pictures/MySql.assets/169fab4b5f67f5db)
EXPLAIN SELECT * FROM user WHERE age+1>24;
![img](../media/pictures/MySql.assets/169fab4b627a3aa3)
计算逻辑应该尽量放到业务层处理,节省数据库的CPU的同时最大限度的命中索引。
**9)利用覆盖索引进行查询,避免回表。**
被查询的列,数据能从索引中取得,而不用通过行定位符row-locator再到row上获取,即“被查询列要被所建的索引覆盖”,这能够加速查询速度。
user表的索引详情:
![img](../media/pictures/MySql.assets/169fab4b7a2dd2ac)
因为status字段是索引列,所以直接从索引中就可以获取值,不必回表查询:
Using Index代表从索引中查询:
EXPLAIN SELECT status FROM user where status=1;
![img](../media/pictures/MySql.assets/169fab4b7c297188)
当查询其他列时,就需要回表查询,这也是为什么要避免SELECT*的原因之一:
EXPLAIN SELECT * FROM user where status=1;
![img](../media/pictures/MySql.assets/169fab4b80770fc3)
**10)建立索引的列,不允许为null。**
单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集,所以,请使用not null约束以及默认值。
remark列建立索引:
ALTER TABLE user ADD INDEX index_remark (remark);
![img](../media/pictures/MySql.assets/169fab4b81039ab4)
IS NULL可以命中索引:
EXPLAIN SELECT * FROM user WHERE remark IS NULL;
![img](../media/pictures/MySql.assets/169fab4b97c18e40)
IS NOT NULL不能命中索引:
EXPLAIN SELECT * FROM user WHERE remark IS NOT NULL;
![img](../media/pictures/MySql.assets/169fab4b992d6c22)
虽然IS NULL可以命中索引,但是NULL本身就不是一种好的数据库设计,应该使用NOT NULL约束以及默认值。
a. 更新十分频繁的字段上不宜建立索引:因为更新操作会变更B+树,重建索引。这个过程是十分消耗数据库性能的。
b. 区分度不大的字段上不宜建立索引:类似于性别这种区分度不大的字段,建立索引的意义不大。因为不能有效过滤数据,性能和全表扫描相当。另外返回数据的比例在30%以外的情况下,优化器不会选择使用索引。
c. 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。虽然唯一索引会影响insert速度,但是对于查询的速度提升是非常明显的。另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,在并发的情况下,依然有脏数据产生。
d. 多表关联时,要保证关联字段上一定有索引。
e. 创建索引时避免以下错误观念:索引越多越好,认为一个查询就需要建一个索引;宁缺勿滥,认为索引会消耗空间、严重拖慢更新和新增速度;抵制唯一索引,认为业务的唯一性一律需要在应用层通过“先查后插”方式解决;过早优化,在不了解系统的情况下就开始优化。
小结:
**对于自己编写的SQL查询语句,要尽量使用EXPLAIN命令分析一下,做一个对SQL性能有追求的程序员。衡量一个程序员是否靠谱,SQL能力是一个重要的指标。作为后端程序员,深以为然。**
参考:https://www.cnblogs.com/gdjk/p/10668868.html (这篇文章写得真好,美团的一位工程师)
补充:
#### explain
**用法:在select语句前加上explain**
显示mysql如何使用索引来处理select语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句
参考CS的文档,还有:https://www.jianshu.com/p/f29a619b3ee8
## 5.联合索引
由多列属性组成的索引。
下面为创建和删除所有的操作:
```sql
#联合索引
ALTER TABLE litemall_user ADD INDEX index_name_username (username,parent_id)
#索引一旦创建,就不能再修改,只能通过下面删除
DROP INDEX index_name_username ON litemall_user;
```
![1586957379982](../media/pictures/MySql.assets/1586957379982.png)
## 6.MySQL的分库分表,以及SQL语句优化、
#### **数据库分表:**
维度:
##### **垂直拆分**
垂直分库(根据业务不同与微服务类似单独服务对应单独库)
垂直分表
垂直分表是基于数据库中的”列”进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过”大表拆小表”,更便于开发与维护,也**能避免跨页问题**,MySQL底层是通过数据页存储的,**一条记录占用空间过大会导致跨页,造成额外的性能开销**。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。
拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。
**垂直拆分优缺点:**
**优点:**
解决业务系统层面的耦合,业务清晰
与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈
缺点:
部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度
依然存在单表数据量过大的问题(需要水平切分)
分布式事务处理复杂
##### 水平拆分
(根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。)
**水平拆分优缺点:**
优点:
不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
应用端改造较小,不需要拆分业务模块
“冷热数据分离”实现方案
缺点:
跨分片事务难以保证
跨分片的复杂查询如join关联查询
数据多次扩展难度和维护量极大
##### 数据分片规则
冷热数据隔离(近6个月或者1年的数据作为热数据,历史数据作为冷数据再进行时间维度拆分)
地域区域或者其他拆分方式
userNo范围分表,比如0~500w用户在user1表,501w-1000w在user2表等
优点:
单表大小可控
天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数 据进行迁移
使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题
缺点:
热点数据成为性能瓶颈。连续分片可能存在数据热点
还有一种很好地操作:
##### 数据库读写分离 ,弄一个主从数据库
##### hash取模mod的切分方式
优点:根据主键id进行数据切分,达到数据均匀分布,使用一致性hash算法可以避免后期扩展问题
缺点:跨分片聚合操作
#### SQL优化:
其实就是上面的,索引优化。
## 7. 主键索引和普通索引的区别。
#### 主键索引(Primary Key)
**数据表的主键列使用的就是主键索引。**
**一张数据表有只能有一个主键,并且主键不能为null,不能重复。**
**在mysql的InnoDB的表中,当没有显示的指定表的主键时,InnoDB会自动先检查表中是否有唯一索引的字段,如果有,则选择该字段为默认的主键,否则InnoDB将会自动创建一个6Byte的自增主键。**
#### 二级索引(辅助索引)
**二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。**
唯一索引,普通索引,前缀索引等索引属于二级索引。
**PS:不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,也可以自行搜索。**
1. **唯一索引(Unique Key)** :唯一索引也是一种约束。**唯一索引的属性列不能出现重复的数据,但是允许数据为NULL,一张表允许创建多个唯一索引。**建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
2. **普通索引(Index)** :**普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和NULL。**
3. **前缀索引(Prefix)** :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,
因为只取前几个字符。
4. **全文索引(Full Text)** :全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6之前只有MYISAM引擎支持全文索引,5.6之后InnoDB也支持了全文索引。
二级索引:
![B+树](../media/pictures/MySql.assets/B+%E6%A0%91%E4%BA%8C%E7%BA%A7%E7%B4%A2%E5%BC%95(%E8%BE%85%E5%8A%A9%E7%B4%A2%E5%BC%95).png)
## 8.Mysql索引。有哪几种,作用,适用于什么场景。你们的项目中何处用了MySQL索引?用了哪一种?
#### 索引类型:
除了**主键索引**,还有下面:
1. 唯一索引(Unique Key)** :唯一索引也是一种约束。**唯一索引的属性列不能出现重复的数据,但是允许数据为NULL,一张表允许创建多个唯一索引。**建立唯一索引的目的大部分时候都是**为了该属性列的数据的唯一性,而不是为了查询效率**。
2. **普通索引(Index)** :**普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和NULL。**
3. **前缀索引(Prefix)** :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,
因为只取前几个字符。
4. **全文索引(Full Text)** :全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6之前只有MYISAM引擎支持全文索引,5.6之后InnoDB也支持了全文索引。
#### 交易所项目中用到的索引:
对account用的是唯一索引,是为了避免重复。
对下面的email,authority,mobile等用的是一般索引,是为了查询快。
![1586963200056](../media/pictures/MySql.assets/1586963200056.png)
用户钱包里面也用了索引:对user_id,symbol 做了联合索引 (这两个是有规律,稳定的,不变的)
![1586963416248](../media/pictures/MySql.assets/1586963416248.png)
#### 商城项目里面用到的索引:
商城里面也是只有user表里面用了一个索引:
![1586963579924](../media/pictures/MySql.assets/1586963579924.png)
## 9.讲一下你对MySQL的理解,主键索引和非主键索引的存储结构和存储内容分别都是什么?用过联合索引吗?
首先MySQL是关系型数据库,然后说一下关系型数据库。。。非关系型数据库。
#### 主键索引和非主键索引有什么区别?
例如对于下面这个表(其实就是上面的表中增加了一个k字段),且ID是主键。
![img](../media/pictures/MySql.assets/1644694-20190505155016157-1108127109.png)
主键索引和非主键索引的示意图如下:
![img](../media/pictures/MySql.assets/1644694-20190505155026646-1387513390.png)
其中R代表一整行的值。
从图中不难看出,主键索引和非主键索引的区别是:非主键索引的叶子节点存放的是**主键的值**,而主键索引的叶子节点存放的是**整行数据**,其中非主键索引也被称为**二级索引**,而主键索引也被称为**聚簇索引**。
根据这两种结构我们来进行下查询,看看他们在查询上有什么区别。
1、如果查询语句是 select * from table where ID = 100,即主键查询的方式,则只需要搜索 ID 这棵 B+树。
2、如果查询语句是 select * from table where k = 1,即非主键的查询方式,则先搜索k索引树,得到ID=100,再到ID索引树搜索一次,这个过程也被称为回表。
现在,知道他们的区别了吧?
参考:https://www.cnblogs.com/heishuichenzhou/p/10813463.html (写的挺好的)
这里再说一下聚集索引和非聚集索引:
#### 聚集索引
**聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。**
在 Mysql 中,InnoDB引擎的表的 `.ibd`文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
##### 聚集索引的优点
聚集索引的查询速度非常的快,因为整个B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。
##### 聚集索引的缺点
1. **依赖于有序的数据** :因为B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或UUID这种又长又难比较的数据,插入或查找的速度肯定比较慢。
2. **更新代价大** : 如果对索引列的数据被修改时,那么对应的索引也将会被修改,
而且况聚集索引的叶子节点还存放着数据,修改代价肯定是较大的,
所以对于主键索引来说,主键一般都是不可被修改的。
#### 非聚集索引
**非聚集索引即索引结构和数据分开存放的索引。**
**二级索引属于非聚集索引。**
> MYISAM引擎的表的.MYI文件包含了表的索引,
> 该表的索引(B+树)的每个叶子非叶子节点存储索引,
> 叶子节点存储索引和索引对应数据的指针,指向.MYD文件的数据。
>
> **非聚集索引的叶子节点并不一定存放数据的指针,
> 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。**
##### 非聚集索引的优点
**更新代价比聚集索引要小** 。非聚集索引的更新代价就没有聚集索引那么大了,非聚集索引的叶子节点是不存放数据的
##### 非聚集索引的缺点
1. 跟聚集索引一样,非聚集索引也依赖于有序的数据
2. **可能会二次查询(回表)** :这应该是非聚集索引最大的缺点了。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
这是Mysql的表的文件截图:
![Mysql表文件截图](../media/pictures/MySql.assets/Mysql%E7%B4%A2%E5%BC%95%E6%96%87%E4%BB%B6%E6%88%AA%E5%9B%BE.png)
聚集索引和非聚集索引:
![B+树](../media/pictures/MySql.assets/B+%E6%A0%91%E7%B4%A2%E5%BC%95.png)
#### 联合索引
联合索引即由多列属性组成索引。
##### 最左前缀原则
假设创建的联合索引由三个字段组成:
```text
ALTER TABLE table ADD INDEX index_name (num,name,age)
```
那么当查询的条件有为:num / (num AND name) / (num AND name AND age)时,索引才生效。所以在创建联合索引时,尽量把查询最频繁的那个字段作为最左(第一个)字段。查询的时候也尽量以这个字段为第一条件。
## 10.如果SQL查询很慢,如何排查并优化?如果主从分离之后,查询还是很慢,比如数据库有几百万的数据,这个时候应该怎么办?
#### 排查优化:
1.看一下索引,建的合适不
```sql
show index from litemall_user
```
![1587010722777](../media/pictures/MySql.assets/1587010722777.png)
2.然后执行一个查询语句,看一下
```sql
explain SELECT * from litemall_user where username = "steve"
```
看一下建的索引用上了没有?
![1587010757484](../media/pictures/MySql.assets/1587010757484.png)
3.还可以看看type,SQl优化的目标是,至少要达到range级别,要求是ref级别,最好是consts级别。(阿里开发手册,索引规约)
4.然后要结合那十几个索引优化规则看一下Sql写的合适不。
#### 主从分离:
##### 主从分离原理
![img](../media/pictures/MySql.assets/1395687-20190514091613619-1312062639.png)
**1.第一步**:Master(主服务器)将操作记录到binary log(二进制日志文件当中)【即每个事务更新数据完成之前先把操作记录在日志文件中,Mysql将事务串行的写入二进制日志文件中】,写入日志文件完成之后,Master通知存储引擎提交事务(注:对数据的操作成为一次二进制的日志事件【binary log event】);
**2.第二步**:slave(从服务器)把binary log拷贝到relay log(中介日志)【相当于缓存作用,存储在从服务器的缓存中】,首先slave会开始一个工作线程(I/O线程),I/O线程会在Master上打开一个普通的连接,然后读取binary log事件,如果已经跟上master,就会睡眠,并等待Master产生新的事件,I/O线程将读取的这些事件写入到relay log;
**3.第三步**:slave从做中介日志事件(relay log),sql线程读取relay log事件并执行更新从服务器上的数据,使其与Master上的数据一致。
**总结**:主服务器把操作记录到binary log——>从服务器将binary log中的数据同步到relay log(中介日志中)——>从服务器读取中介日志执行同步数据
具体的配置流程,参考:https://www.cnblogs.com/lyq-biu/p/10857766.html
##### 主从分离以后还是很慢,考虑分表?还是还有其他更加好的操作?(然后再细致的研究)
分表,怎么分?就是上面说的垂直,水平,热度,哈希等。
## 11.什么是事务?事务的特性?并发事务带来的问题?事务的隔离级别? 什么叫可重复度?Mysql怎么解决不可重复读和幻读? Mysql默认是哪个隔离级别?事务的传播形式?
#### 什么是事务?
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
#### 事务的特性(ACID):
![事务的特性](../media/pictures/MySql.assets/%E4%BA%8B%E5%8A%A1%E7%89%B9%E6%80%A7.png)
1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
2. **一致性:** 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
3. **隔离性:** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
4. **持久性:** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
#### 并发事务带来的问题:
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
- **脏读(Dirty read):** 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
- **丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
- **不可重复读(Unrepeatableread):** 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
- **幻读(Phantom read):** 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
**不可重复度和幻读区别:**
不可重复读的重点是修改,幻读的重点在于新增或者删除。
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
#### **事务隔离级别**
- **SQL 标准定义了四个隔离级别:**
- **READ-UNCOMMITTED(读取未提交):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**。
- **READ-COMMITTED(读取已提交):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**。
- **REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。
- **SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。
------
| 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
| :--------------: | :--: | :--------: | :----: |
| READ-UNCOMMITTED | √ | √ | √ |
| READ-COMMITTED | × | √ | √ |
| REPEATABLE-READ | × | × | √ |
| SERIALIZABLE | × | × | × |
MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看,MySQL 8.0 该命令改为`SELECT @@transaction_isolation;`
```sql
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
```
实际操作:
![1587016917857](../media/pictures/MySql.assets/1587016917857.png)
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)**事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的**SERIALIZABLE(可串行化)**隔离级别。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
#### 什么叫可重复读?
事务隔离级别第三条。
**REPEATABLE-READ(可重复读):** **对同一字段的多次读取结果都是一致的**,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。
#### Mysql怎么解决不可重复读和幻读?默认什么隔离级别?
首先序列化是都可以避免这两种的。
**SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。
**但是Mysql默认用的可重复读**,也可以避免这两种问题,原因是:
与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)**事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生。
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
#### 事务的传播形式:
传播方式:
**新建事务**
required
required_new - 挂起当前
**非事务方式运行**
supports
not_supported - 挂起当前
never
**嵌套事务:**
nested
**抛异常**
mandatory
never
Spring定义了一下几种事务传播方式:
PROPAGATION_REQUIRED 支持当前事务,如果当前没有事务,就**新建一个事务**。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以**非事务方式**执行。
PROPAGATION_MANDATORY 支持当前事务,如果当前没有事务,就**抛出异常**。
PROPAGATION_REQUIRES_NEW **新建事务**,如果当前存在事务,**把当前事务挂起**。
PROPAGATION_NOT_SUPPORTED 以**非事务方式**执行操作,如果当前存在事务,就**把当前事务挂起。**
PROPAGATION_NEVER 以**非事务方式**执行,如果当前存在事务,则**抛出异常**。
PROPAGATION_NESTED 如果当前事务存在,则以**嵌套事务**的方式执行。否则就按照它自己的事务方式执行。
参考:https://www.iteye.com/blog/uule-1420799
## 12.组合索引(联合索引)A,B两字段怎么用?A,B,AB 为什么这么做?
组合索引遵循最左前缀原则(但是如果Mysql版本8.0以后据说不遵循这个原则,为什么,需要考究)
至于为什么呢?看了一个网上的辩论:https://bbs.csdn.net/topics/390727789
有一个老哥举了一个例子:
```java
//假设数据 表T (a,b,c) rowid 为物理位置
rowid a b c
(1) 1 1 1
(2) 2 1 13
(3) 2 2 14
(4) 1 3 3
(5) 2 3 12
(6) 1 2 5
(7) 2 3 9
(8) 1 2 2
(9) 1 3 6
(10) 2 2 11
(11) 2 2 8
(12) 1 1 7
(13) 2 3 15
(14) 1 1 4
(15) 2 1 10
当你创建一个索引 create index xxx on t(a,b), 则索引文件逻辑上等同于如下
a b rowid
1 1 1
1 1 12
1 1 14
1 2 6
1 2 8
1 3 4
1 3 9
2 1 2
2 1 15
2 2 3
2 2 10
2 2 11
2 3 5
2 3 7
2 3 13
```
当select * from T where a=1 and b=3 的时候, 数据库系统可以直接从索引文件中直接二分法找到A=1的记录,然后再B=3的记录。
但如果你 where b=3 则需要遍历这个索引表的全部!
自己个人理解就和查字典一样,加入你查a开始的单词,如果你没有参考目录,从a的范围内找,那只能遍历整本字典啦。
再往底层来说就是:索引在B+树里面存着,只有叶子节点有数据,上面的非叶子节点只充当一个索引的角色,如果你没有显示第一层索引,直接从第二次开始,就肯定用不到索引,只能遍历。因为没有第一层的话,第二次是无序的。
## 13.怎样判断索引是否有生效,mysql的定时计划任务有用过吗?
用explainpan加载sql语句之前判断,
如果没有生效:
![1587023865165](../media/pictures/MySql.assets/1587023865165.png)
如果生效啦:
![1587024092429](../media/pictures/MySql.assets/1587024092429.png)
定时任务:
用event_scheduler(时间)
```sql
#查看定时任务是否开启
show variables like "%event_sche%";
#如果没有开启就 开启定时任务
set global event_scheduler=1;
#关闭定时任务
set global event_scheduler=0;
```
详细操作:参考:https://jingyan.baidu.com/article/e75aca853cf6b9142fdac64f.html
## 14.Mysql底层是什么数据结构,说一下你对B+树的理解,为什么数据库底层不用平衡二叉树,而要用B+树呢?(因为B+树查找快速,而且只有3层,可以减少IO次数)
Mysql底层用的数据结构是B+树和哈希表,不过多数是B+树。说B+树之前先说一下B树。
哈希表只查一个数据的时候还行,查范围就不行啦。
**一个m阶的B树具有如下几个特征:**(B+树的阶数m表示一个节点最多能有m个子节点,也就是每个节点上最多的键值个数)(这里下面的图,m应该表示的是3)
1.根结点至少有两个子女。
2.每个中间节点都至少包含`ceil(m / 2)`个孩子,最多有m个孩子。 (ceil是向上取整,这里是ceil(1.5)= 2)
3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m。 (1.5<=k<=3)
4.所有的叶子结点都位于同一层。
5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。
![img](https:////upload-images.jianshu.io/upload_images/7862980-42f0acde88d3c0cb.png?imageMogr2/auto-orient/strip|imageView2/2/w/825/format/webp)
#### B+树与B树的区别
- 有k个子结点的结点必然有k个关键码;
- 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。
- 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。
#### 为什么不用平衡二叉树?
因为随着数据量的增多,二叉树的高度会很高,查询就变慢啦。
#### 那为什么要用B+树呢?
以InnoDB为例:
数据是放在主键索引上面,也就是说实际上在每个节点上还会存放所有的数据
使用B树存放数据之后实际是这样子的,会在每个对应的索引列的值上存放上对应的数据
![img](../media/pictures/MySql.assets/1216484-20190825002227995-188371311.png)
而B+树则不同,它只会在叶子节点上面挂载数据,非叶子节点不会存放数据,数据只会存在叶子节点上面,非叶子节点只存放索引列的数据
![img](../media/pictures/MySql.assets/1216484-20190825002311121-524783044.png)
这样一个节点就可以存放很多个索引列数据,一次IO就可以拿到很多数据,mysql默认的一个节点16K的大小,可以通过show global status like "Innodb_page_size" 看到该值是16384,每次IO读取16K大小的数据,以索引列是bigInt类型为例,大小8字节,每一条数据还有一个指向下一层的指针6字节,16384/(8+6)=1170,一个节点就大约可以存1170条数据。
以一个**层高为3的树**为例,叶子节点存放数据之后大小1KB,那么这个树可以存放 1170 *1170 *16 =21,902,400,**大约2200万条数据**。所以在这种千万级的表中通过主键索引查找一条数据,最多3次IO就可以找到一条数据。而很多时候树的根节点基本都是在内存中,所以多数时候只需要2次IO。
叶子节点之间也有双向指针连接,提高区间范围性能,范围查找。
创建索引的时候,可以选择索引数据类型,一个是btree一个是hash,hash查找当然也快,但是当遇到范围查找的时候hash就尴尬了,所以根据实际业务需求来看是用btree还是hash。
参考:https://www.cnblogs.com/nijunyang/p/11406688.html
## 15.数据库的索引是什么意思,什么时候需要创建索引,索引是不是越多越好,like关键字会触发索引吗?建立索引需要注意的事情。
#### 什么是索引?
**索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B树, B+树和Hash。**
索引的作用就相当于目录的作用。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。
#### 什么时候需要创建索引?
**1.数据量很大,需要建索引,数据库本来有主键索引**