@@ -19,8 +19,6 @@ permalink: /manuscripts/battle-interview/mysql.html
1919- 引擎层:** 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信**
2020- 存储层:将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互
2121
22-
23-
2422## 存储引擎
2523
2624- InnoDB 【MySQL 默认的存储引擎,支持** 事务、行级锁定和外键** 】
@@ -33,8 +31,6 @@ permalink: /manuscripts/battle-interview/mysql.html
3331
3432** 一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求** ,使用合适的存储引擎,将会提高整个数据库的性能
3533
36-
37-
3834### 查看存储引擎
3935
4036``` bash
@@ -54,13 +50,9 @@ show table status from database where name="tablename"
5450
5551** InnoDB 是聚簇索引,MyISAM 是非聚簇索引** 。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
5652
57-
58-
5953- MyISAM表会把自增主键的最大ID 记录到** 数据文件** 中,重启MySQL自增主键的最大ID也不会丢失
6054- InnoDB 表只是把自增主键的最大ID记录到** 内存** 中,所以重启数据库或对表进行OPTION操作,都会导致最大ID丢失。
6155
62-
63-
6456## 数据类型
6557
6658- 整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
@@ -75,26 +67,22 @@ show table status from database where name="tablename"
7567- TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。
7668- BLOB 保存二进制数据,TEXT 保存字符数据。
7769
78-
79-
8070## 索引(单独重点来)
8171
8272> 索引(Index)是帮助MySQL高效获取数据的数据结构,所以说** 索引的本质是:数据结构**
8373
8474索引本身也很大,不可能全部存储在内存中,** 一般以索引文件的形式存储在磁盘上**
8575
8676优点:
77+
8778- ** 提高数据检索效率,降低数据库IO成本**
8879- ** 降低数据排序的成本,降低CPU的消耗**
8980
9081缺点:
82+
9183- 索引也是一张表,保存了主键和索引字段,并指向实体表的记录,所以也需要占用内存
9284- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,【更新表的时候需要更新索引】
9385
94-
95-
96-
97-
9886## 事务
9987
10088> 主要用于处理操作量大,复杂度高的数据
@@ -122,8 +110,8 @@ show table status from database where name="tablename"
122110
123111- “更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。
124112- “脏读” 、 “不可重复读”和“幻读” ,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决:
125- - 一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。
126- - 另一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 ** MVCC** 或 MCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据** 请求时间点** 的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。
113+ - 一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。
114+ - 另一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 ** MVCC** 或 MCC),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据** 请求时间点** 的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。
127115
128116## 事务隔离级别
129117
@@ -136,16 +124,10 @@ show table status from database where name="tablename"
136124
137125> 简单来说,Serializable可串行化会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
138126
139-
140-
141-
142-
143127** 数据库的事务隔离越严格,并发副作用越小,但付出的代价就越大,并发性就越差,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的**
144128
145129> MySQL InnoDB 存储引擎的默认支持的隔离级别是 ** REPEATABLE-READ(可重读)** 。我们可以通过` SELECT @@tx_isolation; ` 命令来查看,MySQL 8.0 该命令改为` SELECT @@transaction_isolation; `
146130
147-
148-
149131与 SQL 标准不同的地方在于InnoDB 存储引擎在 ** REPEATABLE-READ(可重读)** 事务隔离级别下使用的是` Next-Key Lock ` 算法,** 因此可以避免幻读的产生** ,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)已经可以完全保证事务的隔离性要求,** 即达到了 SQL标准的 SERIALIZABLE(可串行化)隔离级别,而且保留了比较好的并发性能** 。
150132
151133数据库使用锁是为了支持更好的并发,提供数据的完整性和一致性。InnoDB是一个支持行锁的存储引擎,锁的类型有:
@@ -161,37 +143,23 @@ show table status from database where name="tablename"
161143- Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。** GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。**
162144- Next-Key Lock:1+2,锁定一个范围,并且** 锁定记录本身** 。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
163145
164-
165-
166146为了提供更好的并发,InnoDB提供了** 非锁定读** :不需要等待访问行上的锁释放,读取行的一个快照。** 该方法是通过InnoDB的一个特性:MVCC来实现的** 。
167147
168-
169-
170-
171-
172148### MVCC 多版本并发控制
173149
174150- ** 乐观(optimistic)并发控制**
175151- ** 悲观(pressimistic)并发控制**
176152
177153MVCC 是行级锁的一个变种,但它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了** 非阻塞的读操作** ,写操作也只是锁定必要的行
178154
179-
180-
181155** MVCC 的实现是通过保存数据在某个时间点的快照来实现的** 。也就是说不管需要执行多长时间,每个事物看到的数据都是一致的。
182156
183-
184-
185157> InnoDB 的 MVCC,是通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
186158
187159保存这两个额外系统版本号,使大多数操作都不用加锁。使数据操作简单,性能很好,并且也能保证只会读取到符合要求的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。
188160
189161** MVCC 只在 COMMITTED READ(读提交)和REPEATABLE READ(可重复读)两种隔离级别下工作。**
190162
191-
192-
193-
194-
195163## 事务的实现
196164
197165> 事务的实现就是如何实现ACID特性
@@ -205,7 +173,7 @@ MVCC 是行级锁的一个变种,但它在很多情况下避免了加锁操作
205173
206174### MySQL日志分类
207175
208- > 参考:https://www.cnblogs.com/myseries/p/10728533.html
176+ > 参考:< https://www.cnblogs.com/myseries/p/10728533.html >
209177
210178- ** 错误日志** :记录出错信息,也记录一些警告信息或者正确的信息。
211179- ** 查询日志** :记录所有对数据库请求的信息,不论这些请求是否得到了正确的执行。
@@ -305,10 +273,6 @@ InnoDB 实现了以下两种类型的**行锁**:
305273
306274悲观锁会“悲观地”假定大概率会发生并发更新冲突,访问、处理数据前就加排他锁,在整个数据处理过程中锁定数据,事务提交或回滚后才释放锁。另外与乐观锁相对应的,** 悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。**
307275
308-
309-
310-
311-
312276### 锁模式(InnoDB有三种行锁的算法)
313277
314278- ** 记录锁(Record Locks)**
@@ -317,8 +281,6 @@ InnoDB 实现了以下两种类型的**行锁**:
317281
318282** 记录锁(Record Locks)** : 单个行记录上的锁。对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项;
319283
320-
321-
322284** 间隙锁(Gap Locks)** : 当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁。对于键值在条件范围内但并不存在的记录,叫做“间隙”。
323285
324286InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。
@@ -329,8 +291,6 @@ InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙
329291
330292** GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况**
331293
332-
333-
334294** 临键锁(Next-key Locks)** : ** 临键锁** ,是** 记录锁与间隙锁的组合** ,它的封锁范围,既包含索引记录,又包含索引区间。(临键锁的主要目的,也是为了避免** 幻读** (Phantom Read)。** 如果把事务的隔离级别降级为RC,临键锁则也会失效** 。)
335295
336296** Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法** 。** 【通过临建锁可以解决幻读的问题】** 。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。
@@ -345,12 +305,6 @@ InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙
345305
346306InnoDB这种行锁实现特点意味着:** 只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!**
347307
348-
349-
350-
351-
352-
353-
354308### 死锁
355309
356310** 死锁产生** :
@@ -381,8 +335,6 @@ InnoDB这种行锁实现特点意味着:**只有通过索引条件检索数据
381335
382336** 如果出现死锁,可以用 ` show engine innodb status; ` 命令来确定最后一个死锁产生的原因** 。返回结果中包括死锁相关事务的详细信息,如引发死锁的 SQL 语句,事务已经获得的锁,正在等待什么锁,以及被回滚的事务等。据此可以分析死锁产生的原因和改进措施。
383337
384-
385-
386338## 分区
387339
388340一般情况下我们创建的表对应一组存储文件,使用` MyISAM ` 存储引擎时是一个` .MYI ` 和` .MYD ` 文件,使用` Innodb ` 存储引擎时是一个` .ibd ` 和` .frm ` (表结构)文件。
@@ -410,10 +362,6 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
410362
411363** KEY分区** :类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
412364
413-
414-
415-
416-
417365## 分表
418366
419367** 垂直拆分**
@@ -431,12 +379,6 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
431379- 按** 热度拆分** ,高点击率的词条生成各自的一张表,低热度的词条都放在一张大表里,待低热度的词条达到一定的贴数后,再把低热度的表单独拆分成一张表。
432380- 根据** ID的值放入对应的表** ,第一个表user_0000,第二个100万的用户数据放在第二 个表user_0001中,随用户增加,直接添加用户表就行了。
433381
434-
435-
436-
437-
438-
439-
440382## 分库
441383
442384> 为什么要分库?
@@ -452,12 +394,8 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
452394- 减少增量数据写入时的锁对查询的影响
453395- ** 由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘IO,时延变短**
454396
455-
456-
457397微服务下,数据库本来就是分库的
458398
459-
460-
461399## 主从复制
462400
463401- master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events;
@@ -472,18 +410,12 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
472410- 每个 salve只能有一个唯一的服务器 ID【mysql_id唯一性】
473411- 每个master可以有多个salve
474412
475-
476-
477-
478-
479413## 三个范式
480414
481415- 第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
482416- 第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
483417- 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如 果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段 x → 非关键字段y
484418
485-
486-
487419## 百万级别或以上的数据如何删除
488420
489421关于索引:由于** 索引需要额外的维护成本** ,因为** 索引文件是单独存在的文件** ,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。
@@ -500,4 +432,3 @@ hash 分发,好处在于说,可以平均分配每个库的数据量和请求
500432- < https://juejin.cn/post/6850037271233331208#comment >
501433
502434- < https://juejin.cn/post/6850037271233331208#heading-52 >
503-
0 commit comments