第十九章 说过的话就一定要做到-redo日志
一、事先说明
本章会频繁提到之前说的InnoDB记录行格式、页面格式、索引原理、表空间组成等基础知识
二、redo日志是什么
InnoDB以页为单位进行管理存储空间的,
MySQL中负责对表进行数据的读取写入工作的部分是存储引擎
InnoDB是一个将表中数据存储到磁盘的存储引擎。InnoDB是将数据划分为若干页,以页作为磁盘和内存之间交互的基本单位
页的默认大小为16KB
四种行格式
创建表或者修改表的语句中去实现
InnoDB表主键生成策略:优先使用用户自定义的主键;如果用户未定义主键,选取一个不允许为NULL的UNIQUE键作为主键;若都没有,则会为表添加一个row_id隐藏列作为主键
页是InnoDB中磁盘与内存交互的基本单位,也是InnoDB管理存储空间的基本单位,默认大小为16KB
Docker的核心思想就是将应用整合到容器中,并且能在容器中运行
Go语言源码复用建立在包的基础之上
Go语言的包与文件夹一一对应,所有与包有关的操作必须依赖于工作目录(GOPATH)
GOPATH是GO语言使用的一个环境变量,使用绝对路径提供项目的工作目录。
1 | go env |
执行上面的指令可以得到以下输出(部分)
1 | GO111MODULE="on" |
就不一一解释了,由上命令行输出可见,GOPATH路径为:/Users/xietingyu/go
代码保存在$GOPATH/src
下
工程经过编译后的二进制文件在$GOPATH/bin
下
生成的中间缓存文件在$GOPATH/pkg
下
1 | export GOPATH=`pwd` |
1 | mkdir -p src/hello |
先进入src/hello
文件夹
1 | cd src/hello |
使用vim创建文件
1 | vim hello.go |
写入以下代码
1 | package main |
1 | go install hello |
在bin
目录下会有名为hello的可执行文件
1 | ./hello |
这节课程能学到的啥?
输入域名到网页加载出来,经历了哪些过程?
DNS -> TCP -> TLS -> HTTP请求
《》
《》
解决Host管理的问题
关于域名空间:
顶级域gTLD:gov、edu、com、mil、org等
域名报文格式:
《》
各大云厂商可以购买
《》
Q:企业需要那种DNS?
A:权威DNS
常见的开源DNS:bind、nsd、knot、coredns
HTTP明文传输,所以使用HTTPS
《》
《》
得到密钥:session key
client收到证书会进行验证:
证书链验证签名
证书摘要信息 - 数字签名 - 上级CA公钥
《》
源站容量
后端机器扩容、静态加速缓存
网络传输
动态加速DCDN
全站加速:静态加速 + 动态加速
缓存静态文件
针对 POST 等非静态请求等不能在用户边缘缓存的业务,基于智能选路技术,从众多回源线路中择优选择一条线路进行传输
《》
《》
异步非阻塞模型、减少OS进程切换
《》
并发是指在同一时间内可以执行多个任务
Go语言通过编译器运行时(runtime),从语言上支持了并发的特性,通过goroutine特性完成并发。goroutine由Go语言的运行时调度完成,而线程是通过操作系统调度完成。
Go程序从main包的main()函数开始,就会为mian()函数创建一个默认goroutine
使用go关键字,将running()函数并发执行
1 | func running() { |
在main中创建一个匿名函数并为匿名函数启动goroutine
1 | func main() { |
所有goroutine函数都会在main()函数结束以后一同结束
一般情况下可以使用runtime.numCPU()
查询cpu数量,并使用runtime.GOMAXPROCS()
函数进行设置
Go语言提倡使用通信的方式代替共享内存,就是所谓的通道(channel)
任何时候,只能有一个goroutine进行发送和获取数据
创建一个无缓冲通道,使用无缓冲通道时,装入方将被阻塞,直到数据在另一个goroutine被取出
1 | func printer(c chan int) { |
Go通道可以在声明时约束其操作方向,如只发送或接收
1 | ch := make(chan int) |
带缓冲的通道无需等待接收方接收即可完成发送过程,并且不会阻塞,只有存储空间满的时候才会发生阻塞
1 | // 创建一个3个元素缓冲区大小的整形通道 |
阻塞条件:
Go语言对通道限制长度的原因
一方生产数据,一方消费数据,当提供数据一方速度大于消费数据一方速度时候,若不限制长度,会撑爆内存
服务器开发会使用RPC(远程调用)简化通信过程,使得远程通信如同本地函数调用一样
1 | // 模拟RPC客户端请求和接收 |
time.After(time.Second)
通过这个返回的通道在指定时间后返回当前时间两个case会同时执行,看哪个通道先返回数据
1 | // 模拟RPC服务端接收客户端请求和相应 |
使用无限循环处理客户端请求
在main函数中分别调用两个函数,输出如下:
在RPCServer中加入一段代码即可:time.Sleep(time.Second * 2)
,通过睡眠阻塞代码
在某些轻量级场合,原子访问(atomic包)、互斥锁(sync.Mutex)以及等待组(sync.WaitGroup)能最大程度满足需求
我们执行以下代码,就会发现错误
1 | var ( |
输出结果每次都不一样,原因是return seq
有竞态问题,将代码改成如下所示就没问题了:return atomic.AddInt64(&seq, 1)
使用锁非常简单
1 | // 声明锁 |
加锁解锁过程保证原子性
在读多写少的环境中,可以使用读写互斥锁
基本使用类似
1 | var lockr sync.RWMutex |
保证并发环境中完成指定的任务数,等待组内部有一个计数器,计数器的值可以通过调用方法进行加减
直接上代码看演示
1 | func main() { |
所有的任务(网站响应)完成后,wait就会停止阻塞状态向下执行