Skip to content

Commit

Permalink
fix typos and format (pingcap#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
exialin authored and YiniXu9506 committed Nov 8, 2018
1 parent 7d86df9 commit e3cfef3
Show file tree
Hide file tree
Showing 12 changed files with 33 additions and 33 deletions.
10 changes: 5 additions & 5 deletions Spanner-cap-truetime-transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ TiDB 在设计的时候也是一个 CP + HA 系统,多数时候也是一个 CA

1. 全局序列号生成器是一个典型的单点,即使会做一些 failover 的处理,但它仍然是整个系统的一个瓶颈。同时也避免不了网络开销。但全局序列号的实现非常简单,Google 之前的 Percolator 以及现在 TiDB 都是采用这种方式。
2. 为什么要用时间?判断两个事件的先后顺序,时间是一个非常直观的度量方式,另外,如果用时间跟事件关联,那么我们就能知道某一个时间点整个系统的 snapshot。在 TiDB 的用户里面,一个非常典型的用法就是在游戏里面确认用户是否谎报因为回档丢失了数据,假设用户说在某个时间点得到某个装备,但后来又没有了,我们就可以直接在那个特定的时间点查询这个用户的数据,从而知道是否真的有问题。
3. 我们不光可以用时间来确定以前的 snapshot,同样也可以用时间来约定集群会在未来达到某个状态。这个典型的应用就是 shema change。虽然笔者不清楚 Spanner schema change 的实现,但 Google F1 有一篇 [Online, Asynchronous Schema Change in F1](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/41376.pdf) 论文提到了相关的方法,而 TiDB 也是采用的这种实现方式。简单来说,对于一个 schema change,通常都会分几个阶段来完成,如果集群某个节点在未来一个约定的时间没达到这个状态,这个节点就需要自杀下线,防止因为数据不一致损坏数据。
3. 我们不光可以用时间来确定以前的 snapshot,同样也可以用时间来约定集群会在未来达到某个状态。这个典型的应用就是 schema change。虽然笔者不清楚 Spanner schema change 的实现,但 Google F1 有一篇 [Online, Asynchronous Schema Change in F1](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/41376.pdf) 论文提到了相关的方法,而 TiDB 也是采用的这种实现方式。简单来说,对于一个 schema change,通常都会分几个阶段来完成,如果集群某个节点在未来一个约定的时间没达到这个状态,这个节点就需要自杀下线,防止因为数据不一致损坏数据。

使用 TrueTime,Spanner 可以非常方便的实现笔者提到的用法,但 TrueTime 也并不是万能的:

+ TrueTime 需要依赖 atomic clock 和 GPS,这属于硬件方案,而 Google 并没有论文说明如果构造 TrueTime,对于其他用户的实际并没有太多参考意义。
+ TrueTime 需要依赖 atomic clock 和 GPS,这属于硬件方案,而 Google 并没有论文说明如何构造 TrueTime,对于其他用户的实际并没有太多参考意义。
+ TrueTime 也会有误差范围,虽然非常的小,在毫秒级别以下,所以我们需要等待一个最大的误差时间,才能确保事务的相关顺序。

## Transaction

Spanner 默认将数据使用 range 的方式切分成不同的 splits,就跟 TiKV 里面 region 的概念比较类似。每一个 Split 都会有多个副本,分布在不同的 node 上面,各个副本之间使用 Paxos 协议保证数据的一致性。

Spanner 对外提供了 read-only transaction 和 read-write transaction 两种事物,这里简单的介绍一下,主要参考 Spanner 的[白皮书](https://cloud.google.com/spanner/docs/whitepapers/LifeCloudSpannerReadWrite.pdf)
Spanner 对外提供了 read-only transaction 和 read-write transaction 两种事务,这里简单的介绍一下,主要参考 Spanner 的[白皮书](https://cloud.google.com/spanner/docs/whitepapers/LifeCloudSpannerReadWrite.pdf)

### Single Split Write

Expand Down Expand Up @@ -113,15 +113,15 @@ TiDB 现在并没有使用 1PC 的方式,但不排除未来也针对单个 reg
2. API Layer 通过 TrueTime 获取一个 read timestamp(如果我们能够接受 Stale Read 也可以直接选择一个以前的 timestamp 去读)。
3. API Layer 将读的请求发给 Split 1,Split 2 和 Split 3 的一些副本上面,这里有几种情况:
+ 多数情况下面,各个副本能通过内部状态和 TrueTime 知道自己有最新的数据,直接能提供 read。
+ 如果一个副本不确定是否有最新的数据,就像 Leader 问一下最新提交的事务 timestamp 是啥,然后等到这个事务被 apply 了,就可以提供 read。
+ 如果一个副本不确定是否有最新的数据,就向 Leader 问一下最新提交的事务 timestamp 是啥,然后等到这个事务被 apply 了,就可以提供 read。
+ 如果副本本来就是 Leader,因为 Leader 一定有最新的数据,所以直接提供 read。
4. 各个副本的结果汇总然会返回给 client。

当然,Spanner 对于 Read 还有一些优化,如果我们要进行 stale read,并且这个 stale 的时间在 10s 之前,那么就可以直接在任何副本上面读取,因为 Leader 会每隔 10s 将最新的 timestamp 更新到其他副本上面。

现在 TiDB 只能支持从 Leader 读取数据,还没有支持 follower read,这个功能已经实现,但还有一些优化需要进行,现阶段并没有发布。

TiDB 在 Leader 上面的读大部分走的是 lease read,也就是只要 Leader 能够确定自己仍然在 lease 有效范围里面,就可以直接读,如果不能确认,我们就会走 Raft 的 ReadIndex 机制,让 Leader 跟其他节点进行 heartbeat 交互,确认自己仍然是 Leader 之后在进行读操作
TiDB 在 Leader 上面的读大部分走的是 lease read,也就是只要 Leader 能够确定自己仍然在 lease 有效范围里面,就可以直接读,如果不能确认,我们就会走 Raft 的 ReadIndex 机制,让 Leader 跟其他节点进行 heartbeat 交互,确认自己仍然是 Leader 之后再进行读操作

## 小结

Expand Down
2 changes: 1 addition & 1 deletion cases/user-case-linkdoc.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ LinkDoc 通过将患者真实的病例数据和算法模型应用于肿瘤治疗

![](https://upload-images.jianshu.io/upload_images/542677-cb74e459c299c159.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

支撑 LinkDoc 业务的底层数据库平台也面临着医疗行业新领域的技术 & 业务挑战,如数据量的快速增长(亿级别)、大数据量下的清洗逻辑的数据擦写、分析型事物对数据库的读压力都要求我们在数据库平台进行重新探索,选择一款适合医疗大数据业务的数据库解决方案。
支撑 LinkDoc 业务的底层数据库平台也面临着医疗行业新领域的技术 & 业务挑战,如数据量的快速增长(亿级别)、大数据量下的清洗逻辑的数据擦写、分析型事务对数据库的读压力都要求我们在数据库平台进行重新探索,选择一款适合医疗大数据业务的数据库解决方案。

## 选择 TiDB

Expand Down
10 changes: 5 additions & 5 deletions grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
title: 深入了解 gRPC:协议
author: ['唐刘']
date: 2017-06-18
summary: 经过很长一段时间的开发,TiDB 终于发了 RC3。RC3 版本对于 TiKV 来说最重要的功能就是支持了 gRPC,也就意味着后面大家可以非常方便的使用自己喜欢的语言对接 TiKV 了。gRPC 是基于 HTTP/2 协议的,要深刻理解 gRPC,理解下 HTTP/2 是必要的,这里先简单介绍一下 HTTP/2 相关的知识,然后在介绍下 gRPC 是如何基于 HTTP/2 构建的。
summary: 经过很长一段时间的开发,TiDB 终于发了 RC3。RC3 版本对于 TiKV 来说最重要的功能就是支持了 gRPC,也就意味着后面大家可以非常方便的使用自己喜欢的语言对接 TiKV 了。gRPC 是基于 HTTP/2 协议的,要深刻理解 gRPC,理解下 HTTP/2 是必要的,这里先简单介绍一下 HTTP/2 相关的知识,然后再介绍下 gRPC 是如何基于 HTTP/2 构建的。
tags: ['TiKV', 'gRPC']
---

经过很长一段时间的开发,TiDB 终于发了 RC3。RC3 版本对于 TiKV 来说最重要的功能就是支持了 gRPC,也就意味着后面大家可以非常方便的使用自己喜欢的语言对接 TiKV 了。

gRPC 是基于 HTTP/2 协议的,要深刻理解 gRPC,理解下 HTTP/2 是必要的,这里先简单介绍一下 HTTP/2 相关的知识,然后在介绍下 gRPC 是如何基于 HTTP/2 构建的。
gRPC 是基于 HTTP/2 协议的,要深刻理解 gRPC,理解下 HTTP/2 是必要的,这里先简单介绍一下 HTTP/2 相关的知识,然后再介绍下 gRPC 是如何基于 HTTP/2 构建的。

## HTTP/1.x

Expand Down Expand Up @@ -38,7 +38,7 @@ HTTP/1.x 另一个问题就在于它的交互模式,一个连接每次只能
用 HTTP/1.x 做过推送的同学,大概就知道有多么的痛苦,因为 HTTP/1.x 并没有推送机制。所以通常两种做法:

+ Long polling 方式,也就是直接给 server 挂一个连接,等待一段时间(譬如 1 分钟),如果 server 有返回或者超时,则再次重新 poll。
+ Web-socket,通过 upgrade 机制显示的将这条 HTTP 连接变成裸的 TCP,进行双向交互。
+ Web-socket,通过 upgrade 机制显式地将这条 HTTP 连接变成裸的 TCP,进行双向交互。

相比 Long polling,笔者还是更喜欢 web-socket 一点,毕竟更加高效,只是 web-socket 后面的交互并不是传统意义上面的 HTTP 了。

Expand All @@ -52,7 +52,7 @@ HTTP/2 是一个二进制协议,这也就意味着它的可读性几乎为 0

+ Stream: 一个双向流,一条连接可以有多个 streams。
+ Message: 也就是逻辑上面的 request,response。
+ Frame::数据传输的最小单位。每个 Frame 都属于一个特定的 stream 或者整个连接。一个 message 可能有多个 frame 组成。
+ Frame::数据传输的最小单位。每个 Frame 都属于一个特定的 stream 或者整个连接。一个 message 可能由多个 frame 组成。

### Frame Format

Expand All @@ -70,7 +70,7 @@ Frame 是 HTTP/2 里面最小的数据传输单位,一个 Frame 定义如下
+---------------------------------------------------------------+
```

Length:也就是 Frame 的长度,默认最大长度是 16KB,如果要发送更大的 Frame,需要显示的设置 max frame size。
Length:也就是 Frame 的长度,默认最大长度是 16KB,如果要发送更大的 Frame,需要显式地设置 max frame size。
Type:Frame 的类型,譬如有 DATA,HEADERS,PRIORITY 等。
Flag 和 R:保留位,可以先不管。
Stream Identifier:标识所属的 stream,如果为 0,则表示这个 frame 属于整条连接。
Expand Down
2 changes: 1 addition & 1 deletion kudu.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Kudu 提供两种一致性模型:snapshot consistency 和 external consistency

为了实现 External consistency,Kudu 提供了几种方法:

+ 在 clients 之间显示的传递时间戳。当写入一条数据之后,用户用要求 client 去拿一个时间戳作为 token,然后通过一个 external channel 的方式传递给另一个 client。然后另一个 client 就可以通过这个 token 去读取数据,这样就一定能保证读取到最新的数据了。不过这个方法实在是有点复杂。
+ 在 clients 之间显式地传递时间戳。当写入一条数据之后,用户用要求 client 去拿一个时间戳作为 token,然后通过一个 external channel 的方式传递给另一个 client。然后另一个 client 就可以通过这个 token 去读取数据,这样就一定能保证读取到最新的数据了。不过这个方法实在是有点复杂。

+ 提供类似 Spanner 的 commit-wait 机制。当写入一条数据之后,client 需要等待一段时间来确定写入成功。Kudu 并没有采用 Spanner TrueTime 的方案,而是使用了 HybridTime 的方案。HybridTime 依赖 NTP,这个可能导致 wait 的时间很长,但 Kudu 认为未来随着 read-time clock 的完善,这应该不是问题了。

Expand Down
2 changes: 1 addition & 1 deletion pax.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ NSM 会将 record 依次在磁盘 page 里面存放,每个 page 的末尾会

## PAX

可以看到,NSM 和 DSM 都有各自的优劣,所以如何将它们和优点结合起来,就是现在很多 hybrid storage 包括 PAX 考虑的问题。
可以看到,NSM 和 DSM 都有各自的优劣,所以如何将它们的优点结合起来,就是现在很多 hybrid storage 包括 PAX 考虑的问题。

PAX 全称是 Partition Attributes Across,它在 page 里面使用了一种 mini page 的方式,将 record 切到不同的 mini page 里面。

Expand Down
2 changes: 1 addition & 1 deletion tidb-source-code-reading-10.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Column 里面的字段非常多,这里先简单介绍一下:

* 往 [offsets](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L324) 中 append 当前 [data](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L325) 的 size 作为下一个元素在 data 中的起始点。

上面第 1 步在 [appendString](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L404) 这个函数中完成,第 2、3 步在 [finishAppendVar](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L398) 这个函数中完成。其他边长类型元素的追加操作也是非常相似,感兴趣的同学可以接着看看 [appendBytes](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L409)[appendJSON](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L449) 等函数。
上面第 1 步在 [appendString](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L404) 这个函数中完成,第 2、3 步在 [finishAppendVar](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L398) 这个函数中完成。其他变长类型元素的追加操作也是非常相似,感兴趣的同学可以接着看看 [appendBytes](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L409)[appendJSON](https://github.com/pingcap/tidb/blob/source-code/util/chunk/chunk.go#L449) 等函数。

#### 1.3  追加一个 NULL 值

Expand Down
2 changes: 1 addition & 1 deletion tidb-source-code-reading-11.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ TiDB 中 ILJ 的执行阶段可划分为如下图所示的 5 步:

**1\. 启动 Outer Worker 及 Inner Workers**

这部分工作由 [startWorkers](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L130) 函数完成。该函数会 [启动一个 Outer Worker](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L138)[多个 Inner Worker](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L141) [多个 Inner Worker](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L141)。Inner Woker 的数量可以通过 `tidb_index_lookup_concurrency` 这个系统变量进行设置,默认为 4。
这部分工作由 [startWorkers](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L130) 函数完成。该函数会 [启动一个 Outer Worker](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L138)[多个 Inner Worker](https://github.com/pingcap/tidb/blob/source-code/executor/index_lookup_join.go#L141)。Inner Woker 的数量可以通过 `tidb_index_lookup_concurrency` 这个系统变量进行设置,默认为 4。

**2\. 读取 Outer 表数据**

Expand Down
2 changes: 1 addition & 1 deletion tidb-source-code-reading-12.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Count-Min Sketch 维护了一个 d*w 的计数数组,对于每一个值,用

在范围查询的时候,涉及的桶都有可能对最终的结果贡献一些误差。因此,一种更新的方法便是假定所有桶贡献的误差都是均匀的,即如果最终估计的结果为 E,实际的结果为 R,某一个桶的估计结果为 b = 桶高 h * 覆盖比例 r,那么就可以将这个桶的桶高调整为 (b / r) * (R / E) = h * (R / E)。不过如果可以知道落在每一个桶范围中的实际结果,便可以不去假定所有桶贡献的误差都是均匀的。

为了知道落在每一个桶范围中的实际结果,需要先把查询的范围按照直方图桶的边界切分成不相交的部分,这样在 TiKV 在执行查询的时候,可以统计出每一个范围中实际含有的行数目。这样我们便可以按照类似于前述的方法调整每一个桶,不过这个时候不需要假定每个桶贡献的误差都是均匀的,因为我们可以准确知道每一个桶贡献的误差。
为了知道落在每一个桶范围中的实际结果,需要先把查询的范围按照直方图桶的边界切分成不相交的部分,这样 TiKV 在执行查询的时候,可以统计出每一个范围中实际含有的行数目。这样我们便可以按照类似于前述的方法调整每一个桶,不过这个时候不需要假定每个桶贡献的误差都是均匀的,因为我们可以准确知道每一个桶贡献的误差。

### 桶边界的更新

Expand Down
4 changes: 2 additions & 2 deletions tidb-source-code-reading-14.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ CM Sketch 的定义可以在 [cmsketch.go](https://github.com/lamxTyler/tidb/bl

在收集了每一个 Region 上分别建立的直方图后,还需要用 [MergeHistogram](https://github.com/lamxTyler/tidb/blob/source-code/statistics/histogram.go#L609) 把每个 Region 上的直方图进行合并。在这个函数中:

* 为了保证每个值只在一个桶中,我们处理了处理一下交界处桶的问题,即如果交界处两个桶的上界和下界 [相等](https://github.com/lamxTyler/tidb/blob/source-code/statistics/histogram.go#L623),那么需要先合并这两个桶;
* 为了保证每个值只在一个桶中,我们处理了一下交界处桶的问题,即如果交界处两个桶的上界和下界 [相等](https://github.com/lamxTyler/tidb/blob/source-code/statistics/histogram.go#L623),那么需要先合并这两个桶;

* 在真正合并前,我们分别将两个直方图的平均桶深 [调整](https://github.com/lamxTyler/tidb/blob/source-code/statistics/histogram.go#L642) 至大致相等;

Expand Down Expand Up @@ -117,7 +117,7 @@ CM Sketch 的定义可以在 [cmsketch.go](https://github.com/lamxTyler/tidb/bl

* 对于每一次增删,都去更新对应的桶深。在一个桶的桶深过高的时候分裂桶,一般是把桶的宽度等分,不过这样很难准确的确定分界点,引起误差。

* 使用查询得到的真实数去反馈调整直方图,假定所有桶贡献的误差都是均匀的,用连续值假设去调整所有涉及到的桶。然而误差均匀的假设常常会引起问题,比如当当新插入的值大于直方图的最大值时,就会把新插入的值引起的误差分摊到直方图中,从而引起误差。
* 使用查询得到的真实数去反馈调整直方图,假定所有桶贡献的误差都是均匀的,用连续值假设去调整所有涉及到的桶。然而误差均匀的假设常常会引起问题,比如当新插入的值大于直方图的最大值时,就会把新插入的值引起的误差分摊到直方图中,从而引起误差。

目前 TiDB 的统计信息还是以单列的统计信息为主,为了减少独立性假设的使用,在将来 TiDB 会探索多列统计信息的收集和维护,为优化器提供更准确的统计信息。

Loading

0 comments on commit e3cfef3

Please sign in to comment.