你要如何衡量你的人生

坚持,努力,让好事发生

19 | 为什么我只查一行的语句,也执行这么慢?

需要说明的是,如果 MySQL 数据库本身就有很大的压力,导致数据库服务器 CPU 占用率很高或 ioutil(IO 利用率)很高,这种情况下所有语句的执行都有可能变慢,不属于我们今天的讨论范围。

为了便于描述,我还是构造一个表,基于这个表来说明今天的问题。这个表有两个字段 id 和 c,并且我在里面插入了 10 万行记录。

mysql> CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=100000)do
    insert into t values(i,i);
    set i=i+1;
  end while;
end;;
delimiter ;

call idata();
阅读全文 »

(1) 下载、安装、配置

(1.1) 下载 安装

go 安装包下载地址:(下面三个都可以)
https://studygolang.com/dl
https://golang.google.cn/dl/
https://golang.org/dl/

比较喜欢用压缩包,解压完配置就可以使用。
linux https://dl.google.com/go/go1.13.4.linux-amd64.tar.gz
mac https://dl.google.com/go/go1.13.4.darwin-amd64.tar.gz
mac https://dl.google.com/go/go1.13.4.darwin-amd64.pkg
windows https://dl.google.com/go/go1.13.4.windows-amd64.msi
windows https://dl.google.com/go/go1.13.4.windows-amd64.zip

(1.2) 配置环境变量

go需要配置 GOROOT GOPATH GOBIN
GOROOT 是go的安装目录
GOPATH 目录用来存放Go源码,Go的可运行文件,以及相应的编译之后的包文件。所以这个目录下面有三个子目录:src、bin、pkg。而且这个目录不能和 GOROOT 一样。当有多个GOPATH时,默认会将go get的内容放在第一个目录下。

GOPATH 目录约定有三个子目录:

  • src 存放源代码(比如:.go .c .h .s等)
  • pkg 编译后生成的文件(比如:.a)
  • bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)

(1.2.1) mac配置GOROOT GOPATH

修改 .bash_profile 文件

export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/Users/weikeqin1/gopath

source .bash_profile

$ go version
go version go1.13.4 darwin/amd64
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/weikeqin1/Library/Caches/go-build"
GOENV="/Users/weikeqin1/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/weikeqin1/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/03/wwlrrsnn7b9761k_s31w4yqc0glkg3/T/go-build001757962=/tmp/go-build -gno-record-gcc-switches -fno-common"

(1.2.2) windows配置GOROOT GOPATH

go1.12.3.windows-amd64.zip 解压完我放到 D:\ProfessionalSoftWare\golang\go
修改环境变量
配置 GO_ROOTD:\ProfessionalSoftWare\golang\go
配置 PATHPATH 后添加 %GO_ROOT%\bin
配置 GO_PATHD:\ProfessionalSoftWare\golang\gopath
新打开一个cmd,输入 go env 结果如下:

阅读全文 »

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/dataserver?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowMultiQueries=true
username=admin
password=admin
阅读全文 »

http://kafka.apache.org/downloads

(1) 安装

 使用kafka时需要zookeeper,所以需要安装 kafka 和 zookeeper

(1.1) zookeeper安装

 下载  apache-zookeeper-3.5.5-bin.tar.gz

 解压 tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz

 配置 cp -rf conf/zoo_sample.cfg conf/zoo.cfg

 启动 ./bin/zkServer.sh start

ZBMAC-C02PGMT0F:apache-zookeeper-3.5.5-bin weikeqin1$ ./bin/zkServer.sh start
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /Users/weikeqin1/SoftWare/apache-zookeeper-3.5.5-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
阅读全文 »

转自 02 | 日志系统:一条SQL更新语句是如何执行的? - MySQL实战45讲

问大家一个问题。 UPDATE t set c = 2 where id = 1 ; 这个更新语句是怎么执行的。

假设表结构是

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

(7) 更新一条语句的流程

  1. 连接器 连接数据库。
  2. 清空对应表缓存
  3. 分析器 词法解析 语法解析
  4. 优化器
  5. 执行器
  6. 更新 redo log(重做日志)和 binlog(归档日志)。

参考资料

[1] 02 | 日志系统:一条SQL更新语句是如何执行的? - MySQL实战45讲

转自 01 | 基础架构:一条SQL查询语句是如何执行的? - MySQL实战45讲

问大家一个问题。SELECT * FROM t WHERE id = 1 ; 这条查询语句是怎么执行的。

(6) 执行一条查询语句的流程

  1. 连接器 连接数据库。
  2. 查询缓存
  3. 分析器 词法解析 语法解析
  4. 优化器
  5. 执行器

参考

[1] 01 | 基础架构:一条SQL查询语句是如何执行的? - MySQL实战45讲

什么是重做日志(redo log)

而粉板和账本配合的整个过程,其实就是 MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写做磁盘,也就是先写粉板,等不忙的时候再写账本。

具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜做的事。

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写

重做日志(redo log)作用

1、
2、crash-safe 保证数据一致性

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。

crash-safe

要理解 crash-safe 这个概念,可以想想我们前面赊账记录的例子。只要赊账记录记在了粉板上或写在了账本上,之后即使掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。

mysql> show variables like 'innodb_log%' ;
+------------------------------------+----------+
| Variable_name                      | Value    |
+------------------------------------+----------+
| innodb_log_buffer_size             | 16777216 |
| innodb_log_checksums               | ON       |
| innodb_log_compressed_pages        | ON       |
| innodb_log_file_size               | 50331648 |
| innodb_log_files_in_group          | 2        |
| innodb_log_group_home_dir          | ./       |
| innodb_log_spin_cpu_abs_lwm        | 80       |
| innodb_log_spin_cpu_pct_hwm        | 50       |
| innodb_log_wait_for_flush_spin_hwm | 400      |
| innodb_log_write_ahead_size        | 8192     |
| innodb_log_writer_threads          | ON       |
+------------------------------------+----------+
11 rows in set (0.00 sec)

mysql>

日志不同

redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

思考题

redo log 一般设置多大

回答:redo log 太小的话,会导致很快就被写满,然后不得不强行刷 redo log,这样 WAL 机制的能力就发挥不出来了。
所以,如果是现在常见的几个 TB 的磁盘的话,就不要太小气了,直接将 redo log 设置为 4 个文件、每个文件 1GB 吧。

正常运行中的实例,数据写入后的最终落盘,是从 redo log 更新过来的还是从 buffer pool 更新过来的呢

回答:这个问题其实问得非常好。
这里涉及到了,“redo log 里面到底是什么”的问题。

实际上,redo log 并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也就不存在“数据最终落盘,是由 redo log 更新过去”的情况。
如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写盘。这个过程,甚至与 redo log 毫无关系。在崩溃恢复场景中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让 redo log 更新内存内容。更新完成后,内存页变成脏页,就回到了第一种情况的状态。

参考资料

[1] 详细分析MySQL事务日志(redo log和undo log)

(1) 归档日志(binlog)是什么

我们经常听DBA说MySQL可以恢复到任意时刻的数据,为什么呢?
因为有binlog,准确的说有某个时间点的全量备份和这个时间点之后的实施备份
全量备份就是把截止到某个时间点的数据存一份,类似于历史账本,这个是全量备份,叫做历史归档日志。
实时备份是实时的数据,MySQL无论使用什么存储引擎,在执行新增、修改、删除语句时会写一条日志,记录更改,类似于实时记账,这条日志就是实施binlog。

(2) 归档日志(bin log)的作用

1、可以用来恢复数据库数据
2、可以用来给备库传输数据

没有行不行?
也可以,设置 set sql_log_bin=0来关闭当前线程的binlog
或者 在my.cnf配置文件设置skip-log-bin永久关闭binlog


(3) 归档日志格式

binlog日志有三种格式,分别为 STATMENT、ROW 和 MIXED。
在 MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW。
日志格式通过binlog-format指定。


(3.1) STATMENT

 基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的sql语句会记录到binlog中。

 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO, 从而提高了性能;
 缺点:在某些情况下会导致主从数据不一致,比如执行sysdate()slepp()等。


(3.2) ROW

 基于行的复制(row-based replication, RBR),不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了。

 优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题;
 缺点:会产生大量的日志,尤其是alter table的时候会让日志暴涨

(3.3) MIXED

 基于STATMENT和ROW两种模式的混合复制(mixed-based replication, MBR),一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog

(3) 为什么会有两份日志呢

MySQL保证数据不会丢的能力主要体现在两方面:
(1)能够恢复到任何时间点的状态;
(2)能够保证MySQL在任何时间段突然奔溃,重启后之前提交的记录都不会丢失; 专业名词叫crash-safe

对于第一点将MySQL恢复到任何时间点的状态,只要保留有足够的binlog,可以通过重跑binlog来把数据恢复到任何时间点。
对于第二点的能力,也就是crash-safe,binlog无法保证,在InnoDB存储引擎中,事务提交过程中任何阶段,MySQL突然奔溃,重启后都能保证事务的完整性,已提交的数据不会丢失,未提交完整的数据会自动进行回滚。这个能力依赖的就是redo log和unod log两个日志。

因为最开始MySQL里并没有InnoDB存储引擎。
MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用于归档。

而InnoDB是另一个公司以插件形式引入MySQL的,InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。

所以有2份日志
binlog用来归档,备份数据;由于binlog不支持事务,所以无法保证数据的原子性隔离性一致性
redo log用来crash-safe。用来保证原子性一致性
隔离性是通过undo log来实现的。

(4) binlog设置

(4.1) sync_binlog

sync_binlog:是MySQL 的二进制日志(binary log)同步到磁盘的频率。
取值:0-N

sync_binlog=0,当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘。这个是性能最好的。

sync_binlog=1,当每进行1次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

sync_binlog=n,当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

(5) 思考题

(5.1) MySQL怎么知道binlog是完整的

回答:一个事务的 binlog 是有完整格式的:
statement 格式的 binlog,最后会有 COMMIT;
row 格式的 binlog,最后会有一个 XID event。

另外,在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。
对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。
所以,MySQL 还是有办法验证事务 binlog 的完整性的。

(5.2) redo log 和 binlog 是怎么关联起来的

回答:它们有一个共同的数据字段,叫 XID。
崩溃恢复的时候,会按顺序扫描 redo log:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。

参考资料

[1] 02 | 日志系统:一条SQL更新语句是如何执行的? - MySQL实战45讲
[2] mysql的crash-safe
[3] 详细分析MySQL事务日志(redo log和undo log)

问大家一个问题。SELECT * FROM t WHERE id = 1 ; 这个语句是怎么执行的。

我们看到的只是输入一条语句,返回一个结果,却不知道这条语句在MySQL内部的执行过程。

今天我想和你一起把 MySQL 拆解一下,看看里面都有哪些“零件”

下面是 MySQL 的基本架构示意图
MySQL逻辑架构

大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。

Server 层包括连接器、查询缓存、分析器、优化器、执行器等。

存储引擎层,负责数据的存储和提取。

阅读全文 »
0%