MySQL 如何执行一条 SQL

词法分析

词法分析(英语:lexical analysis)是计算机科学中将字符序列转换为记号(token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(lexical analyzer,简称lexer),也叫扫描器(scanner)。词法分析器一般以函数的形式存在,供语法分析器调用。

MySQL 的词法分析器并没有直接使用开源的 flex ,而是自己手动实现了词法分析的函数: int MYSQLlex(YYSTYPE *yylval, YYLTYPE *yylloc, THD *thd)

输入参数为

  • yylval:存储解析到的 token 的一些信息:
    • yylval->lex_str:记录解析到的 token 字符串
    • yylval->charset:记录指定 token 的字符编码,Character Set Introducers
    • yylval->optimizer_hints:记录解析到的查询优化器 hint
    • yylval->symbol:如果 token 是 MySQL 关键字的话,会记录到这个字段
  • yylloc:记录解析到的 token 在原始字符串和分析过程中用到的预处理缓冲中的位置
  • thd:处理当前请求的线程
    • m_parser_state:
      • m_input:解析器参数
      • m_lip:字符输入流
      • m_yacc:语法解析器内部状态

MYSQLlex(yylval, yylloc, thd) 调用 lex_one_token(yylval, thd)thd->m_parser_state->m_lip 读取字符流解析为一个个 token 返回给语法分析器 yyparse。token 用一个整型值表示,在 sql_yacc.h 中通过 #define 定义:

词法分析器的核心实现逻辑都在 lex_one_token,通过有限状态机实现分词。

/* Tokens.  */
#define ABORT_SYM 258
#define ACCESSIBLE_SYM 259
#define ACCOUNT_SYM 260
......
#define ZEROFILL 907
#define JSON_OBJECTAGG 908
#define JSON_ARRAYAGG 909

select uid from table1 为例,会被解析为 token 序列:SELECT_SYM IDENT_QUOTED FROM IDENT_QUOTED END_OF_INPUT


Read more...

2023-05-17

Go PProf 采样的实现

CPU Profiler

CPU Profiler 能帮助我们分析程序中消耗 CPU Time (包括用户空间内核空间的时间)最多的调用栈,我们可以通过如下 API 来获得一份 CPU 消耗的采样结果:

  • 通过命令行测试工具:go test -cpuprofile cpu.pprof
  • 代码中主动开始和停止:pprof.StartCPUProfile(w)pprof.StopCPUProfile()
  • 通过 HTTP 接口:import _ "net/http/pprof"GET /debug/pprof/profile?seconds=30

虽然提供了 API runtime.SetCPUProfileRate(hz) 让我们设置采样的速率,但通常我们并不需要调整。在 pprof.StartCPUProfile(w) 中,默认会设置 runtime.SetCPUProfileRate(100),即程序每消耗 10ms CPU Time 就采样一次。

runtime.SetCPUProfileRate(hz) 会调用 setcpuprofilerate(hz) 设置 CPU 采样的速率为 hz 次每秒,如果 hz <= 0,则会停止 CPU 采样。

在 Go 1.18 之前,setcpuprofilerate(hz) 会通过 signal_unix.go 中的 setProcessCPUProfiler(hz) 执行系统调用 setitimer(_ITIMER_PROF, new, old *itimerval) 设置进程级别的定时器,即当所有线程消耗的 CPU Time 达到 1000/hz ms 时,进程会收到 SIGPROF 信号,并且会随机地由任意一个正在运行中的线程(其信号屏蔽字没有 SIGPROF)执行信号处理器。

这会存在一个问题,即当进程消耗大量的 CPU Time 时,会产生大量的 SIGPROF 信号,而 SIGPROF 信号属于标准信号,多个连续的标准信号,只能有一个处于 pending 状态,其他会被丢弃,这会导致生成的 profile 数据是不准确的。详情可查看 ISSUE:runtime/pprof: Linux CPU profiles inaccurate beyond 250% CPU use

所以从 Go 1.18 开始,在 Linux 平台上,setcpuprofilerate(hz) 会通过 os_linux.go setThreadCPUProfiler(hz) 执行系统调用 timer_create(_CLOCK_THREAD_CPUTIME_ID, &sevp, &timerid) 为每个线程设置单独的定时器,即当一个线程消耗的 CPU Time 达到 1000/hz ms 时,该线程会定向收到 SIGPROF 信号,然后执行信号处理器。

在 Go 中有一个统一的信号处理器 sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g),如果是 SIGPROF 会调用 sigprof(pc, sp, lr uintptr, gp *g, mp *m) 生成当前线程的调用栈,然后调用 cpuProfile.add(tagPtr *unsafe.Pointer, stk []uintptr) 记录下来。

所以当我们需要压测消耗大量 CPU 的程序,应该使用 Go 1.18 及以上,并且在 Linux 平台下进行。


Read more...

2022-12-13

Go GC 如何优化掉重新扫描协程栈

三色垃圾回收算法维基百科图解:

如果三色垃圾回收算法是在程序 STW 的时候执行,那么算法的正确性是能够保证的。但如果三色垃圾回收算法在执行的过程中,程序也在执行(在垃圾回收算法中的术语称为赋值器),赋值器会修改对象之间的引用关系,当出现黑色对象引用了白色对象,同时不存在可以通过其他灰色对象直接/间接访问到该白色对象的路径,由于三色垃圾回收算法不会重新扫描黑色对象,就会出现黑色对象引用的白色对象被回收的情况,也就是本该存活的对象被回收器(垃圾回收代码的执行)错误地当做垃圾回收了。


Read more...

2022-05-15

Golang Mock 原理分析

在写单元测试时,通常需要对某些不容易构造或者不容易获取的对象进行 mock,那么在 Golang 中,我们可以 mock 哪些对象,又有哪些 mock 的方法呢,以及它们是如何实现的?本文将对几个 Golang 常见的开源库进行分析,了解其实现原理。

gomock 的实现

https://github.com/golang/mock 用于 interface 接口的 mock,需要先通过命令行工具 mockgen 生成 interface 的 mock 类型,通常会把命令用 //go:generate 写在代码中,比如:

package main

//go:generate mockgen -source=main.go -destination=foo_mock.go -package=main Foo

type Foo interface {
	Say(string, []string) string
}

func main() {
}

mockgen 会为 Foo 生成 MockFoo 类型的实现:

// MockFoo is a mock of Foo interface.
type MockFoo struct {
   ctrl     *gomock.Controller
   recorder *MockFooMockRecorder
}

// MockFooMockRecorder is the mock recorder for MockFoo.
type MockFooMockRecorder struct {
   mock *MockFoo
}

// NewMockFoo creates a new mock instance.
func NewMockFoo(ctrl *gomock.Controller) *MockFoo {
   mock := &MockFoo{ctrl: ctrl}
   mock.recorder = &MockFooMockRecorder{mock}
   return mock
}

Read more...

2021-06-19

MySQL DDL

MySQL 表结构信息存储

InnoDB 表和索引可以创建在系统表空间或者独立表空间(innodb_file_per_table=on)。对于 InnoDB 表,MySQL 会将表的数据字典信息存储在 .frm 文件中,同时也会存储入口信息(数据库名/表名)到系统表空间,在 INFORMATION_SCHEMA.INNODB_SYS_* 系统表中能够查询到表结构信息;如果 innodb_file_per_table=on,MySQL 会将表的数据存储在 .ibd 文件中,否则存储在系统表空间中。


Read more...

2020-12-13

Istio 学习笔记

常用的 Istio 资源类型

VirtualService

控制请求路由到网格中哪个服务。

  • hosts: 可以配置为带通配符前缀的 DNS 名称,或者 IP 地址。如果采用缩写,比如 reviews,那么在 Kubernetes 平台上就会根据 VirtualService 所在的命名空间,解释为 reviews.default.svc.cluster.local
  • gateways: 该 VirtualService 应用于哪些 Gateway 和 Sidecar。如果没有配置,那么默认值为 mesh。mesh 表示所有的 Sidecar。
  • http HTTPRoute[]: 匹配 HTTP 流量的有序路由规则列表,按顺序应用于 Kubernetes service.spec.ports.name(以 http-/http2-/grpc-* 命名)、gateway.spec.servers.port.protocol(HTTP/HTTP2/GRPC/ TLS-terminated-HTTPS)、serviceentry.spec.ports.protocol(HTTP/HTTP2/GRPC),第一条匹配路由规则将被使用。
    • name: 路由的名字,会被记录到 access log 中
    • match HTTPMatchRequest[]: 单个 HTTPMatchRequest 中的条件是 AND 语义,多个 HTTPMatchRequest 之间是 OR 语义
      • name: 与路由的名字拼接后记录到 access log 中
      • uri StringMatch: 匹配 URI,支持三种匹配规则:
        • exact: 精确匹配
        • prefix:前缀匹配
        • regex:正则匹配
      • ignoreUriCase: 不区分大小写匹配 URI
      • scheme StringMatch: 匹配 URI Scheme
      • method StringMatch: 匹配 HTTP method
      • authority StringMatch: 匹配 HTTP Authority
      • headers map<string, StringMatch>: 必须小写,并且采用连字符作为分隔符,例如 x-request-id。如果值为空,则判断请求头是否存在。注意 key 如果为 uri、scheme、method、authority 将被忽略
      • port: 端口
      • sourceLabels: 限制部分 Sidecar 应用此匹配规则,gateways 必须包含 mesh 值
      • gateways: 覆盖顶层的 gateways
      • queryParams map<string, StringMatch>: 匹配查询参数,不支持前缀匹配
      • withoutHeaders map<string, StringMatch>: 与 headers 参数相反的含义
      • sourceNamespace: 限制应用此匹配规则的命名空间,gateways 必须包含 mesh 值
    • route HTTPRouteDestination[]: 流量被转发到哪个服务上
      • destination Destination:
        • host: 必须存在于 service registry(Kubernetes services)中,或来自 ServiceEntry 中定义的 hosts
        • subset: DestinationRule 中定义的 subset 名字
        • port: 转发的端口
      • weight: 权重
      • headers Headers: 对请求头或响应头进行修改
    • redirect: 配置重定向
    • delegate: route 和 redirect 为空时才可以设置,用于指定把流量转交给其他 VirtualService 处理
    • rewrite: 转发请求之前对 uri 和 Authority/Host 请求头进行重写
    • timeout: 请求超时
    • retries: 重试策略
    • fault: 故障注入
    • mirror Destination: 流量镜像
    • mirrorPercentage: 流量镜像百分比,默认 100%
    • corsPolicy:CORS 策略
    • headers Headers:对请求头或响应头进行修改
  • tls: 匹配 HTTPS 流量的有序路由规则列表。
  • tcp: 匹配 TCP 流量的有序路由规则列表。
  • exportTo string[]: 控制 VirtualService 能否被其他命名空间的 Gateway 和 Sidecar 使用,如果不设置值,默认导出到所有命名空间
    • . 表示当前命名空间
    • * 表示所有命名空间

Read more...

2020-12-01

Kubernetes CronJob 完全指南

Linux Crontab 在过去作为执行定时任务的服务被广泛使用至今,但因其缺乏弹性,也出现了一些分布式的定时任务解决方案,比如 vipshop/Saturn、ihaolin/antares,那么在云原生时代,Kubernetes 作为最受欢迎的容器编排系统,它所提供的 CronJob 类型的工作负载,如何满足我们的需求,以及使用上会有哪些需要注意的地方,包括官方文档中没有讲述的细节,笔者将在这篇文章进行介绍。

Linux Crontab 相信很多人都熟悉,通过 crontab -u nobody -l 我们可以查看指定用户的定时任务,如果需要修改定时任务,则执行 crontab -u nobody -e,通过修改文件的方式配置定时任务,文件格式为:

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

具体的调度执行是由 crond.service 负责。


Read more...

2020-09-29