wink

有的鸟是不会被关住的, 因为它们的羽毛太耀眼了.

  • main

wink

有的鸟是不会被关住的, 因为它们的羽毛太耀眼了.

  • main

TiDB 源码分析之 Bootstrap

2017-09-29

Bootstrap

TiDB的Bootstrap是启动的时候的初始化过程, 由session.go: BootstrapSession完成.

在 tidb-server/main.go 的 createStoreAndDomain 被调用

bootstrap 分为两种, 它首先会去查看 tikv 中的 BootstrapKey, 取得当前的bootstapVersion, 如果为0, 那么就是新集群, 作 bootstrap, 如果低于 currentBootstrapVersion, 那么就做 upgrade:

1
2
3
4
5
6
7
8
9
func BootstrapSession(store kv.Storage) (*domain.Domain, error) {
ver := getStoreBootstrapVersion(store)
if ver == notBootstrapped {
runInBootstrapSession(store, bootstrap)
} else if ver < currentBootstrapVersion {
runInBootstrapSession(store, upgrade)
}
...

bootstap:

1
2
3
4
5
6
7
8
9
10
11
12
13
// bootstrap initiates system DB for a store.
func bootstrap(s Session) {
b, err := checkBootstrapped(s)
if err != nil {
log.Fatal(err)
}
if b {
upgrade(s)
return
}
doDDLWorks(s) // 创建一些系统表
doDMLWorks(s) // 插入一下元数据到系统表
}

upgrade:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func upgrade(s Session) {
ver, err := getBootstrapVersion(s)
if err != nil {
log.Fatal(errors.Trace(err))
}
if ver >= currentBootstrapVersion {
// It is already bootstrapped/upgraded by a higher version TiDB server.
return
}
...
if ver < version15 {
upgradeToVer15(s) // 例如这里创建了mysql.gc_delete_range
}
...
}

sysvar

所有系统的变量, 都定义在在 sysvar.go 中的 defaultSysVars, 在 init 里面设置到了 SysVars map中

在 doDMLWorks 时, 会逐一插入到 mysql.GLOBAL_VARIABLES 表中

LoadPrivilegeLoop

因为 TiDB 是分布式的, 所以在 TiDB 中的 privilege 可能被其他的 TiDB 修改了, 所以 TiDB 使用了 etcd, 在 更改了 privilege 的时候通知所有的tidb 去 load 新的 priilege.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// LoadPrivilegeLoop create a goroutine loads privilege tables in a loop, it
// should be called only once in BootstrapSession.
func (do *Domain) LoadPrivilegeLoop(ctx context.Context) error {
ctx.GetSessionVars().InRestrictedSQL = true
do.privHandle = privileges.NewHandle()
err := do.privHandle.Update(ctx)
if err != nil {
return errors.Trace(err)
}
var watchCh clientv3.WatchChan
duration := 5 * time.Minute
if do.etcdClient != nil {
watchCh = do.etcdClient.Watch(goctx.Background(), privilegeKey)
duration = 10 * time.Minute
}
go func() {
var count int
for {
ok := true
select {
case <-do.exit:
return
case _, ok = <-watchCh:
case <-time.After(duration):
}
if !ok {
log.Error("[domain] load privilege loop watch channel closed.")
watchCh = do.etcdClient.Watch(goctx.Background(), privilegeKey)
count++
if count > 10 {
time.Sleep(time.Duration(count) * time.Second)
}
continue
}
count = 0
err := do.privHandle.Update(ctx) // 这里会更新所有的 priv 到内存
if err != nil {
log.Error("[domain] load privilege fail:", errors.ErrorStack(err))
} else {
log.Info("[domain] reload privilege success.")
}
}
}()
return nil
}

UpdateTableStatsLoop

用来做 Analyze 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// UpdateTableStatsLoop creates a goroutine loads stats info and updates stats info in a loop.
// It will also start a goroutine to analyze tables automatically.
// It should be called only once in BootstrapSession.
func (do *Domain) UpdateTableStatsLoop(ctx context.Context) error {
ctx.GetSessionVars().InRestrictedSQL = true
statsHandle := statistics.NewHandle(ctx, do.statsLease)
atomic.StorePointer(&do.statsHandle, unsafe.Pointer(statsHandle))
do.ddl.RegisterEventCh(statsHandle.DDLEventCh())
err := statsHandle.Update(do.InfoSchema())
if err != nil {
return errors.Trace(err)
}
lease := do.statsLease
if lease <= 0 {
return nil
}
do.wg.Add(1)
go do.updateStatsWorker(ctx, lease)
if RunAutoAnalyze {
do.wg.Add(1)
go do.autoAnalyzeWorker(lease)
}
return nil
}

GCWorker

GCWorker 也是在 BootstrapSession 被启动:

1
2
3
4
5
6
if raw, ok := store.(domain.EtcdBackend); ok {
err = raw.StartGCWorker()
if err != nil {
return nil, errors.Trace(err)
}
}

GCWorker 主要做以下3件事情:

  1. DDL的DropTable, DropIndex等的Delete range操作
  2. tikv GC, 基于lifetime来清理保存的历史数据TiDB 读取历史版本数据
  3. 扫描全库, 查找过期的Locks.
赏

thanks

扫一扫,分享到微信

微信分享二维码
TiDB 源码分析之 Order By
golang build
© 2019 wink