Skip to content

Commit c98186e

Browse files
committed
Update Java Notes
1 parent 635916b commit c98186e

File tree

2 files changed

+141
-21
lines changed

2 files changed

+141
-21
lines changed

DB.md

Lines changed: 129 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,6 @@ EXPLAIN 执行计划在优化器阶段生成,如果 explain 的结果预估的
650650

651651
### 数据空间
652652

653-
==TODO:本节知识是抄录自《MySQL 实战 45 讲》不作为重点学习目标,暂时记录方便后续有了新的理解后更新知识==
654-
655653
#### 数据存储
656654

657655
系统表空间是用来放系统信息的,比如数据字典什么的,对应的磁盘文件是 ibdata,数据表空间是一个个的表数据文件,对应的磁盘文件就是表名.ibd
@@ -685,7 +683,7 @@ InnoDB 的数据是按页存储的如果删掉了一个数据页上的所有记
685683

686684

687685

688-
#### 空间收缩
686+
#### 重建数据
689687

690688
重建表就是按照主键 ID 递增的顺序,把数据一行一行地从旧表中读出来再插入到新表中,重建表时 MySQL 会自动完成转存数据、交换表名、删除旧表的操作,重建命令:
691689

@@ -709,7 +707,7 @@ MySQL 5.6 版本开始引入的 Online DDL,重建表的命令默认执行此
709707

710708
Online DDL 操作会先获取 MDL 写锁,再退化成 MDL 读锁。但 MDL 写锁持有时间比较短,所以可以称为 Online; 而 MDL 读锁,不阻止数据增删查改,但会阻止其它线程修改表结构
711709

712-
问题:想要收缩表空间,执行指令后整体占用空间增大
710+
问题:重建表可以收缩表空间,但是执行指令后整体占用空间增大
713711

714712
原因:在重建表后 InnoDB 不会把整张表占满,每个页留了 1/16 给后续的更新使用。表在未整理之前页已经占用 15/16 以上,收缩之后需要保持数据占用空间在 15/16,所以文件占用空间更大才能保持
715713

@@ -732,6 +730,8 @@ DDL 中的临时表 tmp_table 是在 Server 层创建的,Online DDL 中的临
732730

733731

734732

733+
==本节知识是抄录自《MySQL 实战 45 讲》,作者目前没有更深的理解,暂时记录,后续有了新的认知后会更新知识==
734+
735735
参考文章:https://time.geekbang.org/column/article/72388
736736

737737

@@ -3791,7 +3791,7 @@ InnoDB 使用 B+Tree 作为索引结构,并且 InnoDB 一定有索引
37913791

37923792
* 在 InnoDB 中,表数据文件本身就是按 B+Tree 组织的一个索引结构,这个索引的 key 是数据表的主键,叶子节点 data 域保存了完整的数据记录
37933793

3794-
* InnoDB 的表数据文件**通过主键聚集数据**,如果没有定义主键,会选择非空唯一索引代替,如果也没有这样的列,MySQL 会自动为 InnoDB 表生成一个**隐含字段**作为主键,这个字段长度为 6 个字节,类型为长整形(MVCC 部分的笔记提及)
3794+
* InnoDB 的表数据文件**通过主键聚集数据**,如果没有定义主键,会选择非空唯一索引代替,如果也没有这样的列,MySQL 会自动为 InnoDB 表生成一个**隐含字段 row_id** 作为主键,这个字段长度为 6 个字节,类型为长整形
37953795

37963796
辅助索引:
37973797

@@ -3868,6 +3868,10 @@ InnoDB 存储引擎中有页(Page)的概念,页是 MySQL 磁盘管理的
38683868
* Page Directory:分组的目录,可以通过目录快速定位(二分法)数据的分组
38693869
* File Trailer:检验和字段,在刷脏过程中,页首和页尾的校验和一致才能说明页面刷新成功,二者不同说明刷新期间发生了错误;LSN 字段,也是用来校验页面的完整性
38703870

3871+
数据页中包含数据行,数据的存储是基于数据行的,数据行有 next_record 属性指向下一个行数据,所以是可以遍历的,但是一组数据至多 8 个行,通过 Page Directory 先定位到组,然后遍历获取所需的数据行即可
3872+
3873+
数据行中有三个隐藏字段:trx_id、roll_pointer、row_id(在事务章节会详细介绍它们的作用)
3874+
38713875

38723876

38733877
***
@@ -5529,7 +5533,7 @@ MySQL Server 是多线程结构,包括后台线程和客户服务线程。多
55295533
BEGIN [WORK];
55305534
```
55315535

5532-
说明:只读事务不能对普通的表进行增删改操作,但是可以对临时表增删改
5536+
说明:不填状态默认是读写事务
55335537

55345538
* 回滚事务,用来手动中止事务
55355539

@@ -5614,6 +5618,26 @@ MySQL Server 是多线程结构,包括后台线程和客户服务线程。多
56145618

56155619

56165620

5621+
****
5622+
5623+
5624+
5625+
#### 事务 ID
5626+
5627+
只读事务不能对普通的表进行增删改操作,但是可以对临时表增删改,读写事务可以对数据表执行增删改查操作
5628+
5629+
事务在执行过程中对某个表执行了**增删改操作或者创建表**,就会为当前事务分配一个独一无二的事务 ID(对临时表并不会分配 ID),如果当前事务没有被分配 ID,默认是 0
5630+
5631+
事务 ID 本质上就是一个数字,服务器在内存中维护一个全局变量:
5632+
5633+
* 每当需要为某个事务分配 ID,就会把全局变量的值赋值给事务 ID,然后变量自增 1
5634+
* 每当变量值为 256 的倍数时,就将该变量的值刷新到系统表空间的 Max Trx ID 属性中,该属性占 8 字节
5635+
* 系统再次启动后,会读取表空间的 Max Trx ID 属性到内存,加上 256 后赋值给全局变量,因为关机时的事务 ID 可能并不是 256 的倍数,会比 Max Trx ID 大,所以需要加上 256 保持事务 ID 是一个递增的数字
5636+
5637+
**聚簇索引**的行记录除了完整的数据,还会自动添加 trx_id、roll_pointer 隐藏列,如果表中没有主键并且没有非空唯一索引,也会添加一个 row_id 的隐藏列作为聚簇索引
5638+
5639+
5640+
56175641

56185642
***
56195643

@@ -5669,6 +5693,8 @@ MySQL Server 是多线程结构,包括后台线程和客户服务线程。多
56695693

56705694
### 原子特性
56715695

5696+
#### 实现方式
5697+
56725698
原子性是指事务是一个不可分割的工作单位,事务的操作如果成功就必须要完全应用到数据库,失败则不能对数据库有任何影响。比如事务中一个 SQL 语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态
56735699

56745700
InnoDB 存储引擎提供了两种事务日志:redo log(重做日志)和 undo log(回滚日志)
@@ -5686,16 +5712,100 @@ undo log 属于逻辑日志,根据每行操作进行记录,记录了 SQL 执
56865712

56875713
* 对于每个 update,回滚时会执行一个相反的 update,把数据修改回去
56885714

5689-
undo log 是采用段(segment)的方式来记录,每个 undo 操作在记录的时候占用一个 undo log segment
56905715

5691-
rollback segment 称为回滚段,每个回滚段中有 1024 个 undo log segment
56925716

5693-
* 在以前老版本,只支持 1 个 rollback segment,只能记录 1024 个 undo log segment
5694-
* MySQL5.5 开始支持 128 个 rollback segment,支持 128*1024 个 undo 操作
5717+
参考文章:https://www.cnblogs.com/kismetv/p/10331633.html
56955718

56965719

56975720

5698-
参考文章:https://www.cnblogs.com/kismetv/p/10331633.html
5721+
***
5722+
5723+
5724+
5725+
#### DML 解析
5726+
5727+
##### INSERT
5728+
5729+
乐观插入:当前数据页的剩余空间充足,直接将数据进行插入
5730+
5731+
悲观插入:当前数据页的剩余空间不足,需要进行页分裂,申请一个新的页面来插入数据,会造成更多的 redo log,undo log 影响不大
5732+
5733+
当向某个表插入一条记录,实际上需要向聚簇索引和所有二级索引都插入一条记录,但是 undo log 只需要针对聚簇索引记录,在回滚时会根据聚簇索引去所有的二级索引进行回滚操作
5734+
5735+
roll_pointer 是一个指针,**指向记录对应的 undo log 日志**,一条记录就是一个数据行,行格式中的 roll_pointer 就指向 undo log
5736+
5737+
5738+
5739+
***
5740+
5741+
5742+
5743+
##### DELETE
5744+
5745+
插入到页面中的记录会根据 next_record 属性组成一个单向链表,这个链表称为正常链表,被删除的记录也会通过 next_record 组成一个垃圾链表,该链表中所占用的存储空间可以被重新利用,并不会直接清除数据
5746+
5747+
在页面 Page Header 中,PAGE_FREE 属性指向垃圾链表的头节点,删除的工作过程:
5748+
5749+
* 将要删除的记录的 delete_flag 位置为 1,其他不做修改,这个过程叫 **delete mark**
5750+
* 在事务提交前,delete_flag = 1 的记录一直都会处于中间状态
5751+
* 事务提交后,有专门的线程将 delete_flag = 1 的记录从正常链表移除并加入垃圾链表,这个过程叫 **purge**
5752+
5753+
在对一条记录 delete mark 前会将记录的隐藏列 trx_id 和 roll_pointer 的旧值记录到 undo log 对应的属性中,这样就会产生记录的 roll_pointer 指向当前 undo log 记录,当前 undo log 记录的 roll_pointer 指向旧的 undo log 记录,**形成一个版本链**
5754+
5755+
当有新插入的记录时,首先判断 PAGE_FREE 指向的头节点是否足够容纳新纪录:
5756+
5757+
* 如果可以容纳新纪录,就会直接重用已删除的记录的存储空间,然后让 PAGE_FREE 指向垃圾链表的下一个节点
5758+
* 如果不能容纳新纪录,就直接向页面申请新的空间存储,并不会遍历垃圾链表
5759+
5760+
重用已删除的记录空间,可能会造成空间碎片,当数据页容纳不了一条记录时,会判断将碎片空间加起来是否可以容纳,判断为真就会重新组织页内的记录:
5761+
5762+
* 开辟一个临时页面,将页内记录一次插入到临时页面,此时临时页面时没有碎片的
5763+
* 把临时页面的内容复制到本页,这样就解放出了内存碎片,但是会耗费很大的性能资源
5764+
5765+
5766+
5767+
****
5768+
5769+
5770+
5771+
##### UPDATE
5772+
5773+
执行 UPDATE 语句,对于更新主键和不更新主键有两种不同的处理方式
5774+
5775+
不更新主键的情况:
5776+
5777+
* 就地更新(in-place update),如果更新后的列和更新前的列占用的存储空间一样大,就可以直接在原记录上修改
5778+
5779+
* 先删除旧纪录,再插入新纪录,这里的删除不是 delete mark,而是直接将记录加入垃圾链表,并且修改页面的相应的控制信息,执行删除的线程不是 purge,是执行更新的用户线程
5780+
5781+
插入新记录时可能造成页空间不足,从而导致页分裂
5782+
5783+
更新主键的情况:
5784+
5785+
* 将旧纪录进行 delete mark,在更新语句提交后由 purge 线程移入垃圾链表
5786+
* 根据更新的各列的值创建一条新纪录,插入到聚簇索引中
5787+
5788+
5789+
5790+
***
5791+
5792+
5793+
5794+
#### 回滚日志
5795+
5796+
undo log 是采用段(segment)的方式来记录,每个 undo 操作在记录的时候占用一个 undo log segment
5797+
5798+
Rollback Segement 称为回滚段,每个回滚段中有 1024 个 undo slot
5799+
5800+
* 在以前老版本,只支持 1 个 Rollback Segement,只能记录 1024 个 undo log segment
5801+
* MySQL5.5 开始支持 128 个 Rollback Segement,支持 128*1024 个 undo 操作
5802+
5803+
工作流程:
5804+
5805+
* 事务执行前需要到系统表空间第 5 号页面中分配一个回滚段(页),获取一个 Rollback Segement Header 页面的地址
5806+
* 回滚段页面有 1024 个 undo slot,每个 slot 存放 undo 链表页面的头节点页号。首先去回滚段的两个 cached 链表看是否有缓存的 slot,缓存中没有就在回滚段中找一个可用的 slot
5807+
* 缓存中获取的 slot 对应的 Undo Log Segment 已经分配了,需要重新分配,然后从 Undo Log Segment 中申请一个页面作为日志链表的头节点,并填入对应的 slot 中
5808+
* 开始记录
56995809

57005810

57015811

@@ -5817,11 +5927,6 @@ undo log 主要分为两种:
58175927
* 事务 1 修改该行数据时,数据库会先对该行加排他锁,然后先记录 undo log,然后修改该行 name 为 Tom,并且修改隐藏字段的事务 ID 为当前事务 1 的 ID(默认为 1 之后递增),回滚指针指向拷贝到 undo log 的副本记录,事务提交后,释放锁
58185928
* 以此类推
58195929

5820-
补充知识:purge 线程
5821-
5822-
* 为了实现 InnoDB 的 MVCC 机制,更新或者删除操作都只是设置一下老记录的 deleted_bit,并不真正将过时的记录删除,为了节省磁盘空间,InnoDB 有专门的 purge 线程来清理 deleted_bit 为 true 的记录
5823-
* purge 线程维护了一个 Read view(这个 Read view 相当于系统中最老活跃事务的 Read view),如果某个记录的 deleted_bit 为 true,并且 DB_TRX_ID 相对于 purge 线程的 Read view 可见,那么这条记录一定是可以被安全清除的
5824-
58255930

58265931

58275932
***
@@ -5943,7 +6048,7 @@ RC、RR 级别下的 InnoDB 快照读区别
59436048

59446049
### 持久特性
59456050

5946-
#### 持久方式
6051+
#### 实现方式
59476052

59486053
持久性是指一个事务一旦被提交了,那么对数据库中数据的改变就是永久性的,接下来的其他操作或故障不应该对其有任何影响。
59496054

@@ -6021,7 +6126,9 @@ MTR 的执行过程中修改过的页对应的控制块会加到 Buffer Pool 的
60216126
* oldest_modification:第一次修改 Buffer Pool 中某个缓冲页时,将修改该页的 MTR **开始时**对应的 lsn 值写入这个属性,所以链表页是以该值进行排序的
60226127
* newest_modification:每次修改页面,都将 MTR **结束时**对应的 lsn 值写入这个属性,所以是该页面最后一次修改后对应的 lsn 值
60236128

6024-
全局变量 checkpoint_lsn 表示当前系统中可以被覆盖的 redo 日志量,当 redo 日志对应的脏页已经被刷新到磁盘后就可以被覆盖重用,此时执行一次 checkpoint 来更新 checkpoint_lsn 的值存入管理信息,刷脏和执行一次 checkpoint并不是同一个线程
6129+
全局变量 checkpoint_lsn 表示当前系统中可以被覆盖的 redo 日志量,当 redo 日志对应的脏页已经被刷新到磁盘后就可以被覆盖重用,此时执行一次 checkpoint 来更新 checkpoint_lsn 的值存入管理信息,刷脏和执行一次 checkpoint 并不是同一个线程
6130+
6131+
在系统忙碌时,后台线程的刷脏操作不能将脏页快速刷出,导致系统无法及时执行 checkpoint,这时需要用户线程从 flush 链表中把最早修改的脏页刷新到磁盘中,然后执行 checkpoint
60256132

60266133
使用命令可以查看当前 InnoDB 存储引擎各种 lsn 的值:
60276134

@@ -6046,6 +6153,10 @@ SHOW ENGINE INNODB STATUS\G
60466153
* 使用哈希表:根据 redo log 的 space ID 和 page number 属性计算出哈希值,将对同一页面的修改放入同一个槽里,可以一次性完成对某页的恢复,**避免了随机 IO**
60476154
* 跳过已经刷新到磁盘中的页面:数据页的 File Header 中的 FILE_PAGE_LSN 属性(类似 newest_modification)表示最近一次修改页面时的 lsn 值,如果在 checkpoint 后,数据页被刷新到磁盘中,那么该页 lsn 属性肯定大于 checkpoint_lsn
60486155

6156+
问题:系统崩溃前没有提交的事务的 redo log 可能已经刷盘,这些数据可能在重启后也会恢复
6157+
6158+
解决:通过 undo log 在服务器重启时将未提交的事务回滚掉,定位到 128 个回滚段,遍历 slot,获取 undo 链表首节点页面的 Undo Segement Header 中的 TRX_UNDO_STATE 属性,表示当前链表的事务属性,如果是活跃的就全部回滚
6159+
60496160

60506161

60516162
参考书籍:https://book.douban.com/subject/35231266/

Java.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919

2020

2121

22+
初学时笔记内容参考视频:https://www.bilibili.com/video/BV1TE41177mP,后随着学习的深入逐渐增加了很多知识
23+
24+
给初学者的一些个人建议:
25+
26+
* 初学者对编程的认知比较浅显,一些专有词汇和概念难以理解,所以建议观看视频进行入门,大部分公开课视频讲的比较基础
27+
* 在有了一定的编程基础后,需要看一些经典书籍和技术博客,来扩容自己的知识广度和深度,可以长期保持记录笔记的好习惯
28+
29+
30+
2231
***
2332

2433

@@ -140,8 +149,8 @@ Java 语言提供了八种基本类型。六种数字类型(四个整数型,
140149
```
141150

142151

143-
144-
152+
153+
145154

146155
***
147156

@@ -4464,7 +4473,7 @@ public class Student implements Comparable<Student>{
44644473
}
44654474
```
44664475

4467-
比较器原理:底层是以第一个元素为基准,加一个新元素,就会和第一个元素比,如果大于,就继续和大于的元素进行比较,直到遇到比新元素大的元素为止,放在该位置的左边。(
4476+
比较器原理:底层是以第一个元素为基准,加一个新元素,就会和第一个元素比,如果大于,就继续和大于的元素进行比较,直到遇到比新元素大的元素为止,放在该位置的左边。(红黑树
44684477

44694478

44704479

0 commit comments

Comments
 (0)