学习笔记¶
Mongodb¶
基本操作¶
索引操作¶
# 创建索引
db.collection_name.ensureIndex({"key_name":1})
# 创建唯一索引
db.collection_name.ensureIndex({"key_name":1},{"unique":true})
# 复合唯一索引
db.collection_name.ensureIndex({'key1':1, 'key1':1})
# 10s后自动删除
db.collection_name.ensureIndex({"key_name":1},{expireAfterSeconds:10})
增删改查¶
# 查找
db.getCollection('collection_name').find({"provison_state": "success"})
# 使用正则表达式
db.getCollection('collection_name').find({"date": /2020-01-/})
# range
db.getCollection('collection_name').find({"date": {"$gt": ISODate("2020-07-18T00:00:00.000Z")}})
# 删除文档
db.getCollection('collection_name').remove({"provison_state": "success", "industry": null})
# 删除集合中所有文档
db.getCollection('collection_name').remove({})
更新子文档¶
通过id获取时间¶
mongodb 的 Objectid 由12字节构成,分别是:
- a 4-byte value representing the seconds since the Unix epoch,
- a 3-byte machine identifier,
- a 2-byte process id, and
- a 3-byte counter, starting with a random value.
ObjectId("567a68517507b377a0a20903").getTimestamp()
分页¶
# Page 1
db.getCollection('collection_name').find({}).limit(5)
# Page 2
db.getCollection('collection_name').find({}).skip(5).limit(5)
# Page 3
db.getCollection('collection_name').find({}).skip(10).limit(5)
分片¶
Redis¶
主从同步¶
# 如果主服务设置了密码
config set masterauth <pwd>
# 设置主服务器
SLAVEOF 192.168.1.100 6379
# 取消同步
SLAVEOF NO ONE
mysql¶
常用操作¶
Host授权¶
-- 通配符设置网段
grant all privileges on <db_name>.* to root@'10.10.10.%' identified by '<pwd>';
grant all privileges on *.* to root@'10.10.10.100' identified by '<pwd>';
flush privileges;
存储引擎¶
InnoDb¶
页(Page)是 Innodb 存储引擎用于管理数据的最小磁盘单位。 常见的页类型有数据页、Undo 页、系统页、事务数据页等, 本文主要分析的是数据页。默认的页大小为 16KB,每个页中至少存储有 2 条或以上的行记录, 本文主要分析的是页与行记录的数据结构,有关索引和 B-tree 的部分在后续文章中介绍。
下图是 Innodb 逻辑存储结构图,从上往下依次为: Tablespace、Segment、Extent、Page 以及 Row。本文关注的重点是 Page 和 Row 的数据结构。
上图为 Page 数据结构,File Header 字段用于记录 Page 的头信息, 其中比较重要的是 FIL_PAGE_PREV 和 FIL_PAGE_NEXT 字段, 通过这两个字段,我们可以找到该页的上一页和下一页, 实际上所有页通过两个字段可以形成一条双向链表。 Page Header 字段用于记录 Page 的状态信息。 接下来的 Infimum 和 Supremum 是两个伪行记录, Infimum(下确界)记录比该页中任何主键值都要小的值, Supremum (上确界)记录比该页中任何主键值都要大的值,这个伪记录分别构成了页中记录的边界。
User Records 中存放的是实际的数据行记录,具体的行记录结构将在本文的第二节中详细介绍。 Free Space 中存放的是空闲空间, 被删除的行记录会被记录成空闲空间。 Page Directory 记录着与二叉查找相关的信息。 File Trailer 存储用于检测数据完整性的校验和等数据。
索引¶
锁¶
Elasticsearch¶
Etcd¶
基本语法¶
术语¶
- Node / 节点
- Member / 成员
语法¶
这里全部用 V3 版本
# 设置api 版本
export ETCDCTL_API=3
# 设置key
etcdctl put /monitor_services/thalassa/uri/relay_exist 0.5
# 获取key对应的值
etcdctl get /monitor_services/thalassa/uri/relay_exist
# 只打印值,不打印key
etcdctl get /monitor_services/thalassa/uri/relay_exist --print-value-only
# 匹配key 列表
etcdctl get /monitor_services/thalassa/uri/ --prefix --keys-only
# 监控key是否发生变化
etcdctl watch foo
# 监控匹配key
etcdctl watch --prefix foo
集群管理¶
# 集群状态
etcdctl member list
消息队列¶
如何选择消息队列¶
rabbitmq¶
消息确认机制¶
- 确认消息是否发送到 broker;
- 确认消息是否成功消费;
RabbitMQ为我们提供了两种方式:
通过AMQP事务机制实现,这也是AMQP协议层面提供的解决方案; 通过将channel设置成confirm模式来实现;
事务机制¶
RabbitMQ中与事务机制有关的方法有三个:txSelect(), txCommit()以及txRollback(), txSelect用于将当前channel设置成transaction模式,txCommit用于提交事务,txRollback用于回滚事务,在通过txSelect开启事务之后, 我们便可以发布消息给broker代理服务器了,如果txCommit提交成功了,则消息一定到达了broker了, 如果在txCommit执行之前broker异常崩溃或者由于其他原因抛出异常,这个时候我们便可以捕获异常通过txRollback回滚事务了。
Confirm模式¶
使用事物可以确认消息是否真的到达 broker, 但是会影响系统的吞吐量,使用Confirm可以解决这一问题。
kafka¶
celery¶
配置¶
从4.0版本开始,celery 使用 小写下划线连接方式命令配置项;
保存 task 执行状态和结果
配置 result_backend, 保存的结果格式如下:
正常时的消息
{ "status": "SUCCESS", "result": 10, "traceback": null, "children": [], "task_id": "20fb6fb0-0ef2-4a2f-9517-2fbb5e41e443", "date_done": "2020-09-05T07:40:18.085679" }
异常时的消息
{ "status": "FAILURE", "result": { "exc_type": "ZeroDivisionError", "exc_message": [ "division by zero" ], "exc_module": "builtins" }, "traceback": "Traceback (most recent call last):\n File \"/Users/sealee/.pyenv/versions/3.8.1/envs/tianhe/lib/python3.8/site-packages/celery/app/trace.py\", line 385, in trace_task\n R = retval = fun(*args, **kwargs)\n File \"/Users/sealee/.pyenv/versions/3.8.1/envs/tianhe/lib/python3.8/site-packages/celery/app/trace.py\", line 648, in __protected_call__\n return self.run(*args, **kwargs)\n File \"/Users/sealee/code/mq/consumer.py\", line 13, in add\n 1/ 0\nZeroDivisionError: division by zero\n", "children": [], "task_id": "b7364eda-bbd4-4dc7-89ba-16589dff8401", "date_done": "2020-09-05T08:07:12.214929" }
如果同时在 task 里配置了 ignore_result=True , 则不会保存结果到对应的 backend.
消费配置
通过配置项 broker_transport_options 可以配置消费参数;
broker_transport_options = { 'max_retries': 5 # 最大尝试次数 }
消费完之后再 Acknowledged
celery 默认 ACK 是当一个任务执行后,立刻发送 Acknowledged 信号,标记该任务已经被执行。 但是异常中断时,该任务不会被重新分发。可以通过配置 task_acks_late 让任务执行完成后再 发送 Acknowledged. 这样可以保证不丢消息,但最好保证消费是幂等的,不然会影响结果。
读多条消息
默认情况下,celery worker 一次会读取4条消息,可以通过 worker_prefetch_multiplier 配置, 如果不希望一次读多条,设置为 1, 如果设置为 0, 则 worker 一次会读取尽可能多的消息。
shell¶
sed¶
匹配空格¶
sed -i 's/key[[:space:]]*=[[:space:]]*value/key=new_value/' file
行范围¶
# 匹配行到最后一行
sed -n '/Installed Packages/,$'p file.txt
# 前两行
sed -n '1,2'p file.txt
# 去掉第一行
sed -n '2,$'p file.txt
模糊匹配¶
# 替换 *.iso 为 test.iso, 注意引号的区别
sed -i "s/\\(.*\\)iso/test.iso/" vm.xml
sed -i 's/\(.*\)iso/test.iso/' vm.xml
输出颜色¶
RED='\033[0;31m'
NC='\033[0m'
echo "${RED}hello world!${NC}"
系统¶
进程分析¶
# 查看僵尸进程
ps -A -ostat,ppid,pid,cmd |grep -e '^[Zz]'
Go¶
字符串操作¶
字符串拼接¶
// 直接相加
s1 := "hello" + "world"
// 格式化
s2 := fmt.Sprintf("%s%s", "hello", "world")
// strings.Join
var strList []string = []string{"hello", "world"}
s3 := strings.Join(strList, "")
// buffer.WriteString
var bt bytes.Buffer
bt.WriteString("hello")
bt.WriteString("world")
s4 := bt.String()
// strings.Builder, 和 WriteString 差不多,不过官方推荐这种方式
var build strings.Builder
build.WriteString("hello")
build.WriteString("world")
s5 := build.String()
json 处理¶
警告
如果序列化成JSON,只有大写开头的变量才会被序列化。
eg: 下面的例子中,age 字段不会被序列化。
type Student struct {
Name string `json:"name"`
age int `json:"age"`
}
针对可有可无的字段
如果有些字段不一定存在,可以使用 omitempty 注解,但是不能区分零值和是否存在, eg:
type Domain struct { Hosts []string `json:"hosts"` TaskID string `json:"task_id,omitempty"` }
pprof火焰图¶
pprof 可以用来统计 cpu 和内存的使用情况。如果应用是api或者服务类型的,使用 net/http/pprof 库, 如果是单次运行的,使用 runtime/pprof 工具。
火焰图依赖 graphviz 工具,安装方式如下:
# for macos
brew install graphviz
# for unbunt
apt install graphviz -y
# for centos
yum install graphviz -y
注解
go 1.11 开始已经支持火焰图了,如果是之前的版本,可以使用 go-torch.
runtime/pprof¶
先看看 runtime/pprof 的使用方式,示例代码如下:
package main
import (
"flag"
"log"
"os"
"runtime"
"runtime/pprof"
"time"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
func count() {
sum := 0
for i := 0; i < 1000000; i++ {
sum += i
for j := 0; j < 10000; j++ {
sum -= j
}
}
fmt.Println(sum)
}
func sleep() {
a := []string{"a", "b", "c", "d"}
for i := range a {
fmt.Println(i)
}
time.Sleep(time.Second * 5)
}
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close() // error handling omitted for example
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer f.Close() // error handling omitted for example
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
}
count()
sleep()
}
生成 pprof 文件
# 生成二进制文件
go build main.go
# 生成 prof 文件
go run main.go -cpuprofile cpu.prof -memprofile mem.prof
web 查看
go tool pprof -http=":8081" main cpu.prof
net/http/pprof¶
示例代码:
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe(":8080", nil))
}()
// 占用 cpu
for {
fmt.Println("hello")
}
}
如果是默认的 http.DefaultServeMux, 只需要加一行 import 就行: _ “net/http/pprof”,
如果你使用自定义的 Mux,则需要手动注册一些路由规则:
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
r.HandleFunc("/debug/pprof/trace", pprof.Trace)
- 编译 go build main.go
- 运行 server go run main.go
- 访问 http://localhost:8080/debug/pprof/
- 点击 profile 会下载随后 30s 的cpu profile 文件;
- web 查看 go tool pprof -http=”:8081” main profile
工具使用¶
pkg.go.dev¶
pkg.go.dev 是有关Go软件包和模块的信息资源中心。以前看包的文档需要在 godoc.org 上看, 现在pkg.go.dev也提供Go文档功能。并且它懂go module,提供相关软件包以前版本的信息!
警告
如果一个git 库里有多个子项目,路径需要精确到子项目,否则看不到 api Doc!
eg: github.com/prometheus/client_golang 有 api 和 prometheus, 如果要查看prometheus, 需要搜索:github.com/prometheus/client_golang/prometheus
常用工具库¶
- mongodb
- https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial
- https://github.com/qiniu/qmgok (七牛云开源产品,做了一些封装,项目初期,还不是很稳定)
- mysql
- redis
- elasticsearch
- GUI
go陷阱¶
make slice 时指定len;
eg:
// 指定 len 和 cap 都为10 var docs []string = make([]string, 10, 10)
这个时候 docs 已经有十个已经初始化的值了,进行append 操作时,不会删除原来的值, 正确做法是指定 len 为0,cap 为我们希望的大小。
Go module¶
Go 的依赖管理比较混乱,在 Go 1.11 版本引入了 Go Modules.
启用 go modules¶
启用条件:
- go 版本大于 v1.11
- 设置 GO111MODULE 环境变量
要使用go module 首先要设置GO111MODULE=on,GO111MODULE 有三个值, off、on、auto,off 和 on 即关闭和开启,auto 则会根据当前目录下是否有 go.mod 文件来判断是否使用 modules 功能。 无论使用哪种模式,module 功能默认不在 GOPATH 目录下查找依赖文件,所以使用 modules 功能时请设置好代理。
使用¶
export GO111MODULE=on
# 初始化
go mod init github.com/you/hello
# go build 会将项目的依赖添加到 go.mod 中
go build
配置代理¶
export GOPROXY=https://mirrors.aliyun.com/goproxy/
不喜欢go的点¶
不允许存在未使用的变量和包;
这一点有点烦,特别是在代码开发调试阶段,虽然可以通过注释来规避,但总是看着不太爽。 另外go 的这个检查并不是非常准确。
eg: 下面的代码定义了findOptions 变量,实际并没有使用,编译阶段也不会报错。
findOptions := options.Find() findOptions.SetLimit(2) cur, err := collection.Find(context.TODO(), bson.D{})
数据结构¶
跳表¶
跳表的本质是在链表的基础上建立多级索引,如下图所示:
跳表操作:
- 插入
- 删除
- 查找
- 查找一个区间的元素
- 输出有序序列
为啥redis 使用跳表,不使用红黑树:
- 跳表操作时间复杂度和红黑树相同;
- 跳表代码实现更易读;
- 跳表区间查找效率更高