@@ -19,8 +19,6 @@ permalink: /manuscripts/battle-interview/mysql.html
19
19
- 引擎层:** 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信**
20
20
- 存储层:将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互
21
21
22
-
23
-
24
22
## 存储引擎
25
23
26
24
- InnoDB 【MySQL 默认的存储引擎,支持** 事务、行级锁定和外键** 】
@@ -33,8 +31,6 @@ permalink: /manuscripts/battle-interview/mysql.html
33
31
34
32
** 一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求** ,使用合适的存储引擎,将会提高整个数据库的性能
35
33
36
-
37
-
38
34
### 查看存储引擎
39
35
40
36
``` bash
@@ -54,13 +50,9 @@ show table status from database where name="tablename"
54
50
55
51
** InnoDB 是聚簇索引,MyISAM 是非聚簇索引** 。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
56
52
57
-
58
-
59
53
- MyISAM表会把自增主键的最大ID 记录到** 数据文件** 中,重启MySQL自增主键的最大ID也不会丢失
60
54
- InnoDB 表只是把自增主键的最大ID记录到** 内存** 中,所以重启数据库或对表进行OPTION操作,都会导致最大ID丢失。
61
55
62
-
63
-
64
56
## 数据类型
65
57
66
58
- 整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
@@ -75,26 +67,22 @@ show table status from database where name="tablename"
75
67
- TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。
76
68
- BLOB 保存二进制数据,TEXT 保存字符数据。
77
69
78
-
79
-
80
70
## 索引(单独重点来)
81
71
82
72
> 索引(Index)是帮助MySQL高效获取数据的数据结构,所以说** 索引的本质是:数据结构**
83
73
84
74
索引本身也很大,不可能全部存储在内存中,** 一般以索引文件的形式存储在磁盘上**
85
75
86
76
优点:
77
+
87
78
- ** 提高数据检索效率,降低数据库IO成本**
88
79
- ** 降低数据排序的成本,降低CPU的消耗**
89
80
90
81
缺点:
82
+
91
83
- 索引也是一张表,保存了主键和索引字段,并指向实体表的记录,所以也需要占用内存
92
84
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,【更新表的时候需要更新索引】
93
85
94
-
95
-
96
-
97
-
98
86
## 事务
99
87
100
88
> 主要用于处理操作量大,复杂度高的数据
@@ -122,8 +110,8 @@ show table status from database where name="tablename"
122
110
123
111
- “更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。
124
112
- “脏读” 、 “不可重复读”和“幻读” ,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决:
125
- - 一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。
126
- - 另一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 ** MVCC** 或 MCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据** 请求时间点** 的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。
113
+ - 一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。
114
+ - 另一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 ** MVCC** 或 MCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据** 请求时间点** 的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。
127
115
128
116
## 事务隔离级别
129
117
@@ -136,16 +124,10 @@ show table status from database where name="tablename"
136
124
137
125
> 简单来说,Serializable可串行化会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
138
126
139
-
140
-
141
-
142
-
143
127
** 数据库的事务隔离越严格,并发副作用越小,但付出的代价就越大,并发性就越差,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的**
144
128
145
129
> MySQL InnoDB 存储引擎的默认支持的隔离级别是 ** REPEATABLE-READ(可重读)** 。我们可以通过` SELECT @@tx_isolation; ` 命令来查看,MySQL 8.0 该命令改为` SELECT @@transaction_isolation; `
146
130
147
-
148
-
149
131
与 SQL 标准不同的地方在于InnoDB 存储引擎在 ** REPEATABLE-READ(可重读)** 事务隔离级别下使用的是` Next-Key Lock ` 算法,** 因此可以避免幻读的产生** ,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)已经可以完全保证事务的隔离性要求,** 即达到了 SQL标准的 SERIALIZABLE(可串行化)隔离级别,而且保留了比较好的并发性能** 。
150
132
151
133
数据库使用锁是为了支持更好的并发,提供数据的完整性和一致性。InnoDB是一个支持行锁的存储引擎,锁的类型有:
@@ -161,37 +143,23 @@ show table status from database where name="tablename"
161
143
- Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。** GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。**
162
144
- Next-Key Lock:1+2,锁定一个范围,并且** 锁定记录本身** 。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
163
145
164
-
165
-
166
146
为了提供更好的并发,InnoDB提供了** 非锁定读** :不需要等待访问行上的锁释放,读取行的一个快照。** 该方法是通过InnoDB的一个特性:MVCC来实现的** 。
167
147
168
-
169
-
170
-
171
-
172
148
### MVCC 多版本并发控制
173
149
174
150
- ** 乐观(optimistic)并发控制**
175
151
- ** 悲观(pressimistic)并发控制**
176
152
177
153
MVCC 是行级锁的一个变种,但它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了** 非阻塞的读操作** ,写操作也只是锁定必要的行
178
154
179
-
180
-
181
155
** MVCC 的实现是通过保存数据在某个时间点的快照来实现的** 。也就是说不管需要执行多长时间,每个事物看到的数据都是一致的。
182
156
183
-
184
-
185
157
> InnoDB 的 MVCC,是通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
186
158
187
159
保存这两个额外系统版本号,使大多数操作都不用加锁。使数据操作简单,性能很好,并且也能保证只会读取到符合要求的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
188
160
189
161
** MVCC 只在 COMMITTED READ(读提交)和REPEATABLE READ(可重复读)两种隔离级别下工作。**
190
162
191
-
192
-
193
-
194
-
195
163
## 事务的实现
196
164
197
165
> 事务的实现就是如何实现ACID特性
@@ -205,7 +173,7 @@ MVCC 是行级锁的一个变种,但它在很多情况下避免了加锁操作
205
173
206
174
### MySQL日志分类
207
175
208
- > 参考:https://www.cnblogs.com/myseries/p/10728533.html
176
+ > 参考:< https://www.cnblogs.com/myseries/p/10728533.html >
209
177
210
178
- ** 错误日志** :记录出错信息,也记录一些警告信息或者正确的信息。
211
179
- ** 查询日志** :记录所有对数据库请求的信息,不论这些请求是否得到了正确的执行。
@@ -305,10 +273,6 @@ InnoDB 实现了以下两种类型的**行锁**:
305
273
306
274
悲观锁会“悲观地”假定大概率会发生并发更新冲突,访问、处理数据前就加排他锁,在整个数据处理过程中锁定数据,事务提交或回滚后才释放锁。另外与乐观锁相对应的,** 悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。**
307
275
308
-
309
-
310
-
311
-
312
276
### 锁模式(InnoDB有三种行锁的算法)
313
277
314
278
- ** 记录锁(Record Locks)**
@@ -317,8 +281,6 @@ InnoDB 实现了以下两种类型的**行锁**:
317
281
318
282
** 记录锁(Record Locks)** : 单个行记录上的锁。对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项;
319
283
320
-
321
-
322
284
** 间隙锁(Gap Locks)** : 当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁。对于键值在条件范围内但并不存在的记录,叫做“间隙”。
323
285
324
286
InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。
@@ -329,8 +291,6 @@ InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙
329
291
330
292
** GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况**
331
293
332
-
333
-
334
294
** 临键锁(Next-key Locks)** : ** 临键锁** ,是** 记录锁与间隙锁的组合** ,它的封锁范围,既包含索引记录,又包含索引区间。(临键锁的主要目的,也是为了避免** 幻读** (Phantom Read)。** 如果把事务的隔离级别降级为RC,临键锁则也会失效** 。)
335
295
336
296
** Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法** 。** 【通过临建锁可以解决幻读的问题】** 。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。
@@ -345,12 +305,6 @@ InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙
345
305
346
306
InnoDB这种行锁实现特点意味着:** 只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!**
347
307
348
-
349
-
350
-
351
-
352
-
353
-
354
308
### 死锁
355
309
356
310
** 死锁产生** :
@@ -381,8 +335,6 @@ InnoDB这种行锁实现特点意味着:**只有通过索引条件检索数据
381
335
382
336
** 如果出现死锁,可以用 ` show engine innodb status; ` 命令来确定最后一个死锁产生的原因** 。返回结果中包括死锁相关事务的详细信息,如引发死锁的 SQL 语句,事务已经获得的锁,正在等待什么锁,以及被回滚的事务等。据此可以分析死锁产生的原因和改进措施。
383
337
384
-
385
-
386
338
## 分区
387
339
388
340
一般情况下我们创建的表对应一组存储文件,使用` MyISAM ` 存储引擎时是一个` .MYI ` 和` .MYD ` 文件,使用` Innodb ` 存储引擎时是一个` .ibd ` 和` .frm ` (表结构)文件。
@@ -410,10 +362,6 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
410
362
411
363
** KEY分区** :类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
412
364
413
-
414
-
415
-
416
-
417
365
## 分表
418
366
419
367
** 垂直拆分**
@@ -431,12 +379,6 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
431
379
- 按** 热度拆分** ,高点击率的词条生成各自的一张表,低热度的词条都放在一张大表里,待低热度的词条达到一定的贴数后,再把低热度的表单独拆分成一张表。
432
380
- 根据** ID的值放入对应的表** ,第一个表user_0000,第二个100万的用户数据放在第二 个表user_0001中,随用户增加,直接添加用户表就行了。
433
381
434
-
435
-
436
-
437
-
438
-
439
-
440
382
## 分库
441
383
442
384
> 为什么要分库?
@@ -452,12 +394,8 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
452
394
- 减少增量数据写入时的锁对查询的影响
453
395
- ** 由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘IO,时延变短**
454
396
455
-
456
-
457
397
微服务下,数据库本来就是分库的
458
398
459
-
460
-
461
399
## 主从复制
462
400
463
401
- master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events;
@@ -472,18 +410,12 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
472
410
- 每个 salve只能有一个唯一的服务器 ID【mysql_id唯一性】
473
411
- 每个master可以有多个salve
474
412
475
-
476
-
477
-
478
-
479
413
## 三个范式
480
414
481
415
- 第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
482
416
- 第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
483
417
- 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如 果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段 x → 非关键字段y
484
418
485
-
486
-
487
419
## 百万级别或以上的数据如何删除
488
420
489
421
关于索引:由于** 索引需要额外的维护成本** ,因为** 索引文件是单独存在的文件** ,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。
@@ -500,4 +432,3 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
500
432
- < https://juejin.cn/post/6850037271233331208#comment >
501
433
502
434
- < https://juejin.cn/post/6850037271233331208#heading-52 >
503
-
0 commit comments