Avery's Blog

Work on Web Dev, Infra Dev & ML

0%

MySQL’s explain statement is commonly used to analyze the cause of slow SQL and returns the following columns:

select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, extra.

The most important fields are type, rows, key and extra.

Logical Architecture of MySQL

Let’s start with the basics, the logical architecture of MySQL.

Client -> (Query Cache, 5.7.20 deprecated, 8.0 removed) -> Parser (generates parse trees) -> Preprocessor (generates new parse trees) -> Query Optimizer (generates query plans) -> Execution Engine (execution scheduling) -> Storage Engine (data lookup) -> Execution Engine (data filtering, sorting, etc.) -> Client and Query Cache

“type”, “rows” and “key” fields

The “type” field is used to indicate the type of connection. Connection types can be classified from fast to slow as follows:

  1. const, system: up to one matching row, using a primary key or unique index

  2. eq_ref: returns a row of data, usually found in join, using a primary key or a unique index

  3. ref: uses the leftmost prefix of the key, and the key is not a primary or unique key

  4. range: index range scan, the scan of the index starts at a certain point and returns the matching row(s)

  5. index: full table scan in the order of the index, the advantage is that there is no sorting, the disadvantage is that the whole table has to be scanned

  6. all: full table scan

    Read more »

看完这篇,你就是 Lombok 大师。

@Data

@Data 注解:@ToString, @EqualsAndHashCode, @Getter on all fields, @Setter on all non-final fields, and @RequiredArgsConstructor

需要注意的是,如果存在任何写明的构造器,@Data 注解不会再生成新的构造器。

@Builder

@Builder 注解:允许 Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build(); 的形式创建对象。

需要注意的是,@Builder 注解生成的 AllArgsConstructor 默认是 @AllArgsConstructor(access = AccessLevel.PACKAGE),即构造器上没有 public 标识符,所以跨包生成时可能会出现访问权限问题。

此外,@Builder@Data 注解同时存在时,先生成包访问权限的 AllArgsConstructor,且因为有了构造器,@Data 注解不会再生成 RequiredArgsConstructor,因此结果是只有一个包访问权限的 AllArgsConstructor,因此需要再手动声明 @AllArgsConstructor@RequiredArgsConstructor。不建议声明 @NoArgsConstructor,因为此构造器可以逃逸 Lombok 对 @NonNull 注解字段的 null-check。如果框架要求或其他情况非要使用,建议使用 @NoArgsConstructor(onConstructor = @__({@Deprecated})) 表明该接口已废弃防止手动使用。

Read more »

MySQL 的 Explain 语句常用来分析慢 SQL 的原因,会返回以下的数据:

select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、extra。

其中最重要的是 type、rows、key 和 extra 这四个字段。

MySQL 逻辑架构

先介绍一下基础知识,MySQL 的逻辑架构:

客户端->(查询缓存,5.7.20 已 deprecated,8.0 已移除)->解析器(生成解析树)->预处理器(生成新解析树)->查询优化器(生成查询计划)->执行引擎(执行调度)->存储引擎(数据查找)->执行引擎(数据过滤、排序等)->客户端和查询缓存

type、rows 和 key 字段

type 字段用来说明连接的类型。连接类型从快到慢可以分为以下几种:

  1. const,system:最多一个匹配行,使用主键或者 unique 索引

  2. eq_ref:返回一行数据,通常在联接(join)时出现,使用主键或 unique 索引

  3. ref:使用 key 的最左前缀,且 key 不是主键或 unique 键

    Read more »

问题还原

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Demo {

public void processor(Message message) {

Result result = save(message);

}

@Transactional(timeout = 30, rollbackFor = Exception.class)
public Result save(Message message) {
serviceA.save(message);

Result result = serviceB.save(message);

return result;
}
}

集成测试方法会调用 Demo.processor,然后 processor 方法会调用带有 @Transactional 注解的 save 方法。save 方法中的 serviceA.saveserviceB.save 方法都是带有 @Transactional 注解的。

但是经过测试发现,ServiceA 和 serviceB 的事务都能生效,但是 Demo 类中 save 方法的事务无法生效,会出现 serviceA 成功,serviceB 失败,数据库中只写入了 serviceA 的结果的情况。很明显,Demo.save 方法上的 @Transactional 注解失效了。

Read more »

我一直在质疑单测的作用,无法理解单测到底对 RD 有什么实质的收益。

举例来说,我花 20 分钟写了一段 80 行的代码,这时,我往往需要 40 分钟,写 200 行冗长、累赘、丑陋的 mock 代码去覆盖涉及的各个场景。很多时候,写单测比写业务逻辑还要费事—— mock 返回值太费劲了,尤其是 mock 一些静态或私有方法。而单测能带来什么呢?多数情况下,单测都能通过,而如果单测无法通过,只有两种可能性。毕竟单测只测试一个类,甚至只测试一个方法。如果连这种不和外部交互的代码你都能写错,一,你根本就没有理解需求,你写的代码是一团浆糊,你自己都不到自己在干什么;二,你犯了非常低级的失误(但不得不承认,最优秀的 RD 也会犯低级的失误)。需求没分析清楚,你该找 PM、翻 PRD;犯了低级失误,也犯不着写这么多“无用”的代码。在集成测试环境,用浏览器、Postman、模拟 RPC 很快就能测出这些低级错误,打个断点就能找到问题并修复,效率比写单测高太多了,也能测到很多单测根本无法涉及的地方。所以我一直认为单测是投入产出比是非常低的。

Read more »

断路器 (Circuit Breaker) 一词来源于电气工程,在软件领域则是一种设计模式。

它的作用类似于家庭里的空气开关,当某一个电器发生故障时,所在房间的空气开关会自动断路,防止故障扩散到其他房间,引发更大的事故。

它的一个典型应用场景是:一个服务要发送 100 个请求,其中 99 个请求都正常,但是其中 1 个请求的接口发生了故障,要等待 5 秒。那么这个服务要完成加载就需要 5 秒以上的时间。如果发生故障的请求不在核心链路上,那么引入断路器就能以某种策略,不再请求那个出故障的接口,让服务正常加载。

如果出故障的接口在核心链路上,可以为这个请求加上降级策略:如果调用该接口出现了故障,那么断路器可以将请求指向一个备用接口,来实现高可用性。

断路器的状态

断路器有三种状态:Closed、Open 和 Half-Open。

h_2732904-992404e807e83288.jpg

Closed 状态是断路器连通的状态,是默认状态;

当失败次数达到某个阈值,就会进入 Open 状态,这个时候断路器会拦截所有调用并直接抛出 Exception;

Half-Open 状态是“尝试”状态,在 Open 状态下会不定时尝试接口是否正常,若仍有故障退回 Open 状态,故障消除了的话就回到 Closed 状态。

Read more »

笔记摘录于《JavaScript 精粹》,结合个人总结与思考,以及其他学习资料。

JavaScript Wiki

JavaScript 对象参考手册

JavaScript 安放位置

HTML 中的脚本必须位于 <script></script> 标签之间。

脚本可被放置在 HTML 页面的 <body><head> 部分中。

那些老旧的实例可能会在 <script> 标签中使用type="text/javascript"。现在已经不必这样做了。JavaScript 是所有现代浏览器以及 HTML5 中的默认脚本语言。通常的做法是放入 <head> 部分中,或者放在页面底部。这样就可以把它们安置到同一处位置,不会干扰页面的内容。

外链脚本实例

1
2
<script src="myScript.js">
</script>
Read more »