无题
前面几节讲了用 VSCode Debugger 调试 Vue 和 React 项目,但同学们经常会遇到一些断点相关的问题,比如:
在文件里打的断点是灰的,一直不生效
断点断在了奇怪的文件和位置
不知道什么原因导致的,该怎么解决。
这是因为不清楚 VSCode Debugger 里打的断点是怎么在网页里生效的。
这节就来讲下这个:
断点映射的原理我们在 VSCode 里打的断点是这样的:
VSCode 会记录你在哪个文件哪行打了个断点。
在 breakpoints 这里可以看到:
代码经过编译打包之后,可能会产生一个 bundle.js,网页里运行的是这个 js 文件:
我们打的断点最终还是在代码的运行时,也就是网页里断住的,所以在 VSCode 里打的断点会被传递给浏览器,通过 CDP 调试协议。
但是问题来了,我们本地打的断点是一个绝对路径,也就是包含 ${workspaceFolder} 的路径,而网页里根本没有这个路径,那怎么断住的呢?
这是因为有的文件是关联了 sourcemap 的,也就是文件末尾的这行注释:
它会把文件路径映射到源码路径。
如果映射到的源码路径直 ...
无题
Vue、React 的项目怎么调试我们都知道了,这节我们来调试下 React 的源码。
把 react 和 react-dom 包下载了下来,在项目里引入,开发服务跑起来后,打开 Chrome Devtools 打断点调试。
你会发现调试的是 react-dom.development.js
这是因为 react-dom 包下就是编译后的 react-dom.development.js 文件:
而源码里这些逻辑是分散在不同的包里的,所以就算搞懂了逻辑,也不知道这些逻辑在哪些包里,只能靠搜索来定位。
那怎么能够调试 React 最初的源码呢?
也就是这样的效果:
这就需要用到我们刚学的 sourcemap 的知识了:
用 VSCode 调试 React 项目我们用 create-react-app 创建一个 react 项目,然后 npm run start 跑起来。
这时候浏览器访问就可以用 Chrome DevTools 调试了:
但我们的目标是在 VSCode 里调试,所以要添加一个 VSCode 的 debugger 配置:
然后点击 debug 启动:
这时候 ...
24写作本书时用到的一些重要的参考资料
感谢我不生产知识,只是知识的搬运工。写作本小册的时间主要用在了两个方面:
搞清楚事情的本质是什么。
这个过程就是研究源码、书籍和资料。
如何把我已经知道的知识表达出来。
这个过程就是我不停的在地上走过来走过去,梳理知识结构,斟酌用词用句,不停的将已经写好的文章推倒重来,只是想给大家一个不错的用户体验。
这两个方面用的时间基本上是一半一半吧,在搞清楚事情的本质是什么阶段,除了直接阅读MySQL的源码之外,查看参考资料也是一种比较偷懒的学习方式。本书只是MySQL进阶的一个入门,想了解更多关于MySQL的知识,大家可以从下边这些资料里找点灵感。
一些链接
MySQL官方文档:https://dev.mysql.com/doc/refman/5.7/en/
MySQL官方文档是写作本书时参考最多的一个资料。说实话,文档写的非常通俗易懂,唯一的缺点就是太长了,导致大家看的时候无从下手。
MySQL Internals Manual:https://dev.mysql.com/doc/internals/en/
介绍MySQL如何实现各种功能的文档,写的比较好,但是太少 ...
22后悔了怎么办 —— undo 日志
undo 日志标签: MySQL 是怎样运行的
事务回滚的需求我们说过事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。但是偏偏有些时候做到一半的时候会出一些情况,比如:
情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。
情况二:程序员可以在事务执行过程中手动输入ROLLBACK语句结束当前的事务的执行。
这两种情况都会导致事务执行到一半就结束,但是事务执行过程中可能已经修改了很多东西,为了保证事务的原子性,我们需要把东西改回原先的样子,这个过程就称之为回滚(英文名:rollback),这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性要求。
小时候我非常痴迷于象棋,总是想找厉害的大人下棋,赢棋是不可能赢棋的,这辈子都不可能赢棋的,又不想认输,只能偷偷的悔棋才能勉强玩的下去。悔棋就是一种非常典型的回滚操作,比如棋子往前走两步,悔棋对应的操作就是向后走两步;比如棋子往左走一步,悔棋对应的操作就是向右走一步。数据库中的回滚跟悔棋差不多,你插入了一条记录,回滚操作对应的就是把这条记录删除掉;你更新 ...
21说过的话就一定要办到 —— redo 日志(下)
redo 日志(下)标签: MySQL 是怎样运行的
redo日志文件redo日志刷盘时机我们前边说mtr运行过程中产生的一组redo日志在mtr结束时会被复制到log buffer中,可是这些日志总在内存里呆着也不是个办法,在一些情况下它们会被刷新到磁盘里,比如:
log buffer空间不足时
log buffer的大小是有限的(通过系统变量innodb_log_buffer_size指定),如果不停的往这个有限大小的log buffer里塞入日志,很快它就会被填满。设计InnoDB的大叔认为如果当前写入log buffer的redo日志量已经占满了log buffer总容量的大约一半左右,就需要把这些日志刷新到磁盘上。
事务提交时
我们前边说过之所以使用redo日志主要是因为它占用的空间少,还是顺序写,在事务提交时可以不把修改过的Buffer Pool页面刷新到磁盘,但是为了保证持久性,必须要把修改这些页面对应的redo日志刷新到磁盘。
Force Log at Commit
后台线程不停的刷刷刷
后台有一个线程,大约每秒都会刷新一次log buf ...
20说过的话就一定要办到 —— redo 日志(上)
redo日志(上)标签: MySQL是怎样运行的
事先说明本文以及接下来的几篇文章将会频繁的使用到我们前边唠叨的InnoDB记录行格式、页面格式、索引原理、表空间的组成等各种基础知识,如果大家对这些东西理解的不透彻,那么阅读下边的文字可能会有些吃力,为保证您的阅读体验,请确保自己已经掌握了我前边唠叨的这些知识。
redo日志是个啥我们知道InnoDB存储引擎是以页为单位来管理存储空间的,我们进行的增删改查操作其实本质上都是在访问页面(包括读页面、写页面、创建新页面等操作)。我们前边唠叨Buffer Pool的时候说过,在真正访问页面之前,需要把在磁盘上的页缓存到内存中的Buffer Pool之后才可以访问。但是在唠叨事务的时候又强调过一个称之为持久性的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。但是如果我们只在内存的Buffer Pool中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库中所做的更改也就跟着丢失了,这是我们所不能忍受的(想想ATM机已经提示狗哥转账 ...
19从猫爷被杀说起 —— 事务简介
事务简介标签: MySQL 是怎样运行的
事务的起源对于大部分程序员来说,他们的任务就是把现实世界的业务场景映射到数据库世界。比如银行为了存储人们的账户信息会建立一个account表:
123456CREATE TABLE account ( id INT NOT NULL AUTO_INCREMENT COMMENT '自增id', name VARCHAR(100) COMMENT '客户名称', balance INT COMMENT '余额', PRIMARY KEY (id)) Engine=InnoDB CHARSET=utf8;
狗哥和猫爷是一对好基友,他们都到银行开一个账户,他们在现实世界中拥有的资产就会体现在数据库世界的account表中。比如现在狗哥有11元,猫爷只有2元,那么现实中的这个情况映射到数据库的account表就是这样:
123456+----+--------+---------+| id | name | balance |+----+--------+---- ...
18调节磁盘和CPU的矛盾 —— InnoDB 的 Buffer Pool
InnoDB 的 Buffer Pool标签: MySQL 是怎样运行的
缓存的重要性通过前边的唠叨我们知道,对于使用InnoDB作为存储引擎的表来说,不管是用于存储用户数据的索引(包括聚簇索引和二级索引),还是各种系统数据,都是以页的形式存放在表空间中的,而所谓的表空间只不过是InnoDB对文件系统上一个或几个实际文件的抽象,也就是说我们的数据说到底还是存储在磁盘上的。但是各位也都知道,磁盘的速度慢的跟乌龟一样,怎么能配得上“快如风,疾如电”的CPU呢?所以InnoDB存储引擎在处理客户端的请求时,当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中,也就是说即使我们只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以进行读写访问了,在进行完读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省去磁盘IO的开销了。
InnoDB的Buffer Pool啥是个Buffer Pool设计InnoDB的大叔为了缓存磁盘中的页,在MySQL服务器启动的时候就向操作系统申请了一片连续的内存 ...
17神兵利器 —— optimizer trace 的神器功效
otpimizer trace 表的神奇功效标签: MySQL 是怎样运行的
对于MySQL 5.6以及之前的版本来说,查询优化器就像是一个黑盒子一样,你只能通过EXPLAIN语句查看到最后优化器决定使用的执行计划,却无法知道它为什么做这个决策。这对于一部分喜欢刨根问底的小伙伴来说简直是灾难:“我就觉得使用其他的执行方案比EXPLAIN输出的这种方案强,凭什么优化器做的决定和我想的不一样呢?”
在MySQL 5.6以及之后的版本中,设计MySQL的大叔贴心的为这部分小伙伴提出了一个optimizer trace的功能,这个功能可以让我们方便的查看优化器生成执行计划的整个过程,这个功能的开启与关闭由系统变量optimizer_trace决定,我们看一下:
1234567mysql> SHOW VARIABLES LIKE 'optimizer_trace';+-----------------+--------------------------+| Variable_name | Value |+---------- ...
16查询优化的百科全书 —— Explain 详解(下)
Explain 详解(下)标签: MySQL 是怎样运行的
执行计划输出中各列详解本章紧接着上一节的内容,继续唠叨EXPLAIN语句输出的各个列的意思。
Extra顾名思义,Extra列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句。MySQL提供的额外信息有好几十个,我们就不一个一个介绍了(都介绍了感觉我们的文章就跟文档差不多了~),所以我们只挑一些平时常见的或者比较重要的额外信息介绍给大家哈。
No tables used
当查询语句的没有FROM子句时将会提示该额外信息,比如:
1234567mysql> EXPLAIN SELECT 1;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+| id | select_type | table | partitions | type | possible_keys | ...
