From 8bd15f24fd01488a6d639d5a13e7876ddb3159da Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 10 Dec 2021 00:57:33 +0800 Subject: [PATCH] make region size dynamic (#82) Signed-off-by: Jay Lee --- media/dynamic-size-buckets.png | Bin 0 -> 5275 bytes media/dynamic-size.png | Bin 0 -> 22931 bytes text/0082-dynamic-size-region.md | 209 +++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 media/dynamic-size-buckets.png create mode 100644 media/dynamic-size.png create mode 100644 text/0082-dynamic-size-region.md diff --git a/media/dynamic-size-buckets.png b/media/dynamic-size-buckets.png new file mode 100644 index 0000000000000000000000000000000000000000..70122b60a45edfa5d89c9a0404567fff3f23c2d8 GIT binary patch literal 5275 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 tSs!3WU=}D8e0~6}|AxZ

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 literal 0 HcmV?d00001 diff --git a/text/0082-dynamic-size-region.md b/text/0082-dynamic-size-region.md new file mode 100644 index 00000000..a5880f7e --- /dev/null +++ b/text/0082-dynamic-size-region.md @@ -0,0 +1,209 @@ +# Dynamic size region + +- RFC PR: https://github.com/tikv/rfcs/pull/0082 +- Tracking Issue: https://github.com/tikv/tikv/issues/11515 + +## Summary + +Make the size of a region dynamic, and only set a upper limit. + +This is the first step that we try to support PiB scale cluster. + +## Motivation + +We have observed a lot of regressions when the count of regions increases. The major drawback +comes from three aspects: + +1. Transactions need to access more regions, which lead to too many RPCs and have high latency; +2. A node needs to drive more regions, which can have high resource usages and hurt performance; +3. Tools and service like CDC/BR/GC need to loop over all regions, which is slow. + +We have hibernate raft (#23) to get around the most significant problem 2. But it's not always +working as expected: + +- Workload with random access can still activate a lot of regions; +- Hibernated region needs extra time to renew its lease, which lead to high tail latency; +- It complicates the design of CDC/BR, which requires access all replicas instead of just leader now; +- GC will still wake up all regions and cause periodical high usage. + +There are two source of region creations: size/keys split and table split. Table split is disabled by +default in TiKV. Even if it splits a lot of regions, small one will be merged later. + +Size/keys split contributes the most count of regions. The current size of a region is 96MiB by default, +and the key count is 960,000. They are set based on an assumption that region is the concurrent unit of +requests. Too large can lead to smaller concurrency and high latency. Since keys is related to the size, +we only discuss region size here. + +## Detailed design + +To reduce the count of regions, we can set the size of region to a larger value. More precisely, instead +of 96MiB, we set the max region size to 10GiB (before compression). To get around the problem of +concurrency, we introduce new concept bucket that is smaller ranges within a region. To make hot spot be +recognized and scheduled quickly, we choose the make the size of a region dynamic. + +![dynamic size](../media/dynamic-size.png) + +### Dynamic size + +The hotter a region is, the smaller its size becomes. To make it simplified, we choose 512MiB for hot regions +and 10GiB for cold regions. So there are two types of split, hot spot split and general size split. Hot spot +split is triggered by PD. General size split is triggered by TiKV itself. + +10GiB is just an example, it's allowed to change to a bigger or smaller value. + +### Bucket + +A region is divided into several buckets logically. We will collect query stats by buckets and report the bucket +to PD. For hot spot regions, buckets are created by scan. For cold regions, buckets are created by approximate +range size. Every bucket should have the size about 128MiB. Their ranges and stats are all reported to PD. PD +can detect hot buckets from all regions and decide whether need to split new hot spots and schedules them to +balance. + +![dynamic size buckets](../media/dynamic-size-buckets.png) + +A new bucket metadata will be added to kvproto: +``` +message Buckets { + uint64 region_id = 1; + uint64 version = 2; // A hint indicate if keys have changed. + repeated bytes keys = 3; + repeated uint64 read = 4; + repeated uint64 write = 5; + Duration period = 6; // The period that stats are collected with in. +} +``` + +When the metadata is queried by client, PD may erase the read/write/period field in response. + +### Read and Write + +Since regions’ sizes become dynamic, region count in a single instance is not a problem any more. +Hibernate regions can be deprecated, and lease renew by heartbeats can be enabled by default for +all regions. This can bring significant improvement for read tail latency. + +After the region becomes larger, the chance to hit 1PC becomes higher. And the RPC count in a +transaction can also be reduced. However, the hot spot chance is also increased. For writes, we rely +on PD to split the hot spot in time and increase concurrency. In addition, we can also use unordered +apply to make single region apply logs faster. Since unordered apply is a standalone feature, I’m not +going into details here. + +For read hot spots, split should be triggered by PD, which can utilize the global statistics from all +regions and nodes. For normal read requests, TiKV will need to split its range into smaller buckets +according to statics to increase concurrency. When TiKV client wants to do a scan, it sends the RPC +once, and TiKV will split the requests into smaller concurrent jobs. + +In the past design, follower read has also been discussed to offload works from leader. But client +can’t predict the progress of a follower, so latency can also become unpredictable. It’s more +controllable to let PD split hot spots and schedule to get balance. + +Now that a region can be in 10GiB, a full scan can make the response exceed the limit of gRPC, +which is 4GiB. So instead of unary, we need to use server streaming RPC to return the response as +stream. The stream can set a limit on memory or count to avoid too many pending responses +exhausting memory. + +If follower read is enabled in client side, client can use buckets from PD to split requests and +balance loads across leader and followers. Because buckets is not strictly mapping to a region, +so client may maintain a sorted map about all available bucket keys, and update it in background +periodically. When starting a query, client may query the map for available keys within region +range and split request. When updating in the background, only removing overlap keys with the new +region and keep the old, and possible stale, bucket keys. The point is that bucket keys don't need +to be 100% accurate, a close match can enhance concurrency and load balance a lot. For fast lookup, +a map from region id to bucket keys can also be maintained. + +After a region becomes larger, it needs to be warmed up before serving requests. There are two +common cases: + +- In the very beginning, writes will only happen in a single region, the problem can be solved by + pre-split + scatter. +- Because followers don’t serve read, so their data is usually not in cache. After transferring + the leader, serving a range scan can trigger at most 10GiB IO. To warm it up, we can extend + the existing pre transfer leader mechanism. Leader will send its bucket statics to follower + in pre transfer leader, follower can do a pre scan for hot buckets before accepting + transferring leader. + +### Flow report + +Buckets statistics are reported in a standalone stream. They will be reported more frequently +than original region heartbeats, which is 60s. The new stream is expected to be reported every +10 seconds when have changes. + +PD will maintain top N hot spot buckets and split all of those buckets that exceed P of +all throughput. N and P should be configurable, I suggest to set it to 300 and 1% respectively +first. The algorithm is just one possible solution and subject to change for evaluation. + +Because the boundaries of buckets can changed by writes, so PD may need to merge the history +of different versions of buckets. There are two possible solutions. For example, buckets +{ [a, c), [c, e) } is replaced by { [a, b), [b, d), [d, e) }, +1. To simulate the old split behavior, history of [a, c) should be inherited by [b, d) and [c, e) + should be inherited by [d, e). The history should be inherited by the rightest overlapping + buckets. +2. Or the history of [a, c) can be shared by both [a, b) and [b, d), [d, e) can be shared by + both [b, d) and [d, e). The history should be inherited by all overlapping buckets and + optionally multiply a factor like 0.8 for errors. + +### Replication + +A large region can take minutes to be replicated, PD should make cost estimation on actual size +instead of fixed value. To avoid logs being truncated during sending a snapshot, we should set +the log gc limit based on the actual size too. Raft engine should be GA before dynamic size +region, so we don't need to worry about the extra write amplifications. + +To avoid ingesting a large snapshot cause large compaction, we need to split the snapshot into +several smaller files. Each snapshot files should not be larger than 128MiB. + +There are ways to optimize the replications, sending a sub-LSM tree for example. But these designs +may be complicated. And we rely on further designs like separating LSM tree to optimize the cost +for all. + +### Compatibility + +As buckets don't modify existing metadata, so it's backward compatible. When upgrading from small +regions, PD may trigger a lot of merge to get a large region size. This procedure should be made +at slow pace to avoid introducing spike to online service. When downgrading from dynamic regions, +TiKV may trigger a lot of split at start. Split is lightweight than merge and very fast, we may not +need to take extra care. + +Note PD may not have enough statistics about hot spot when it's just started. So it should not +trigger merge until the system is ready for service for a configured time, which should be set to +an interval that PD can identify hot spots in theory. + +Snapshot is split into multiple files in dynamic regions, which is different from the past. But it +seems fine for old version to apply the multiple snapshot files for one CF. We need more tests to +verify the behavior. + +## Drawbacks + +Increasing the size only may not have significant improvement on all problems, it can introduce +higher replication cost than before. + +## Alternatives + +For the problems on cost of too many regions, we can also optimize TiKV itself to make it run +faster and capable to handle more regions. But the dynamic design make it possible to adjust +current architecture and introduce more significant changes in the future. + +## Questions + +1. How about always splitting request in TiKV client no matter whether follower read is enabled? + +This can make design and implement simple, but we have seen a lot of cases that too many RPC +requests can slow down TiKV client as too many coroutines. We may need further evaluation to +decide whether always enable client side request split. + +## Unresolved questions + +1. Page response (tikv/tikv#11448) can solve the problem that response may exceed 4GiB, how about always using unary API? + +Combined page response, it's possible to only use unary API to serve scan requests. +For example, if TiKV still does TiKV side split and if it finds it's impossible to +return all result, it can return partial response and hint TiDB to send page requests. + +The downside is that it can make range scan very slow as it's ping-pong style while +streaming is pipeline and the implementation is complicated than streaming. We may +prototype both ways and see which has good balance between maintainability and performance. + +2. How about the original load base split? That is how to further split a region that is smaller than + 128MiB? + +I think it depends on the evaluation of buckets. If we do need to split further, we can also add +a requirement on the minimal bucket count of a region.