> For the complete documentation index, see [llms.txt](https://meetbill.gitbook.io/butterfly-project-doc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://meetbill.gitbook.io/butterfly-project-doc/project-handlers/chunfeng/butterfly-bianque/butterfly-yingyan.md).

# 全量检查-Redis

## 鹰眼

* [1 项目概述](#1-项目概述)
  * [1.1 背景介绍及目标](#11-背景介绍及目标)
  * [1.2 名词说明](#12-名词说明)
    * [1.2.1 数据一致率](#121-数据一致率)
  * [1.3 Roadmap](#13-roadmap)
* [2 需求分析](#2-需求分析)
  * [2.1 功能需求](#21-功能需求)
    * [2.1.1 不一致类型](#211-不一致类型)
    * [2.1.2 key 不一致](#212-key-不一致)
    * [2.1.3 对比模式](#213-对比模式)
  * [2.2 非功能需求](#22-非功能需求)
  * [2.3 调研](#23-调研)
    * [2.3.1 vipshop/redis-migrate-tool](#231-vipshopredis-migrate-tool)
    * [2.3.2 alibaba/RedisFullCheck](#232-alibabaredisfullcheck)
* [3 总体设计](#3-总体设计)
  * [3.1 系统架构](#31-系统架构)
  * [3.2 模块简介](#32-模块简介)
  * [3.3 设计与折衷](#33-设计与折衷)
  * [3.4 潜在风险](#34-潜在风险)
* [4 详细设计](#4-详细设计)
  * [4.1 分发任务](#41-分发任务)
    * [4.1.1 交互流程](#411-交互流程)
    * [4.1.2 数据库设计](#412-数据库设计)
    * [4.1.3 接口形式](#413-接口形式)
    * [4.1.4 Redis key](#414-redis-key)
* [5 数据统计](#5-数据统计)
  * [5.1 scan 耗时](#51-scan-耗时)
  * [5.2 scan 并发送任务耗时](#52-scan-并发送任务耗时)

## 1 项目概述

### 1.1 背景介绍及目标

> * Redis 集群间数据检查

### 1.2 名词说明

#### 1.2.1 数据一致率

> * 一致率 = (key\_sum\_num - key\_diff\_num)/key\_sum\_num \* 100%

### 1.3 Roadmap

## 2 需求分析

> 需求分析重点是需求

### 2.1 功能需求

#### 2.1.1 不一致类型

不一致的方式主要分为 2 类：key 不一致和 value 不一致。

#### 2.1.2 key 不一致

key 不一致主要分为以下几种情况：

> * lack\_target : key 存在于源库，但不存在于目的库。
> * type: key 存在于源库和目的库，但是类型不一致。
> * length: value 长度不一致
> * value: key 存在于源库和目的库，且类型一致，但是 value 不一致。
>   * string: value 不同。
>   * hash: 存在 field，满足下面 3 个条件之一：
>     * field 存在于源端，但不存在与目的端。
>     * field 存在于目的端，但不存在与源端。
>     * field 同时存在于源和目的端，但是 value 不同。
>   * set/zset：与 hash 类似。
>   * list: 与 hash 类似。
> * other: 其他异常

field 冲突类型有以下几种情况（只存在于 hash，set，zset，list 类型 key 中）：

> * lack\_source: field 存在于源端 key，field 不存在与目的端 key。
> * lack\_target: field 不存在与源端 key，field 存在于目的端 key。
> * value: field 存在于源端 key 和目的端 key，但是 field 对应的 value 不同。

#### 2.1.3 对比模式

check\_method

> * length: 对比 value 值的长度是否相等。
> * content: 对比 value 长度、value 值是否相等。
>   * 对于 hash，set，zset，list 大 key 处理采用以下方式：
>     * len <= 1000，直接取全量 field、value 进行比较，使用如下命令：hgetall，smembers，zrange 0 -1 withscores，lrange 0 -1。
>     * len > 1000, 仅打印日志，不校验数据

### 2.2 非功能需求

### 2.3 调研

#### 2.3.1 vipshop/redis-migrate-tool

<https://github.com/vipshop/redis-migrate-tool>

```
$src/redis-migrate-tool -c rmt.conf -o log -C redis_check
Check job is running...

Checked keys: 1000
Inconsistent value keys: 0
Inconsistent expire keys : 0
Other check error keys: 0
Checked OK keys: 1000

All keys checked OK!
Check job finished, used 1.041s
```

> 如果想检查更多的 key，可以使用下面命令：

```
$src/redis-migrate-tool -c rmt.conf -o log -C "redis_check 200000"
Check job is running...

Checked keys: 200000
Inconsistent value keys: 0
Inconsistent expire keys : 0
Other check error keys: 0
Checked OK keys: 200000

All keys checked OK!
Check job finished, used 11.962s
```

> 插入一些测试 key （redis\_testinsert）

```
$src/redis-migrate-tool -c rmt.conf -o log -C "redis_testinsert"
Test insert job is running...

Insert string keys: 200
Insert list keys  : 200
Insert set keys   : 200
Insert zset keys  : 200
Insert hash keys  : 200
Insert total keys : 1000

Correct inserted keys: 1000
Test insert job finished, used 0.525s

$ 插入更多的 key
$src/redis-migrate-tool -c rmt.conf -o log -C "redis_testinsert 30000"
```

#### 2.3.2 alibaba/RedisFullCheck

<https://github.com/alibaba/RedisFullCheck>

## 3 总体设计

> 总体设计重点是设计与折衷

### 3.1 系统架构

> 一般来说会有个简单的架构图，并配以文字对架构进行简要说明；

```
    +------------------------------------------------+
    | +--------------------------+ +---------------+ |
    | |       Source Redis       | | Target  Redis | |
    | + ------------^--------^-^-+ +-------------^-+ |
    +---------------|----------\-\---------------|---+
                    | redis       \ \ twemproxy  |
                    1(scan)    (type)2 3         3(length + content)
                    |                   \ \      |
                    |                      \ \   |
    +---------------|-------------------------+-\+---+
    |               |                            |   |
    | +yingyan------------------+  +yingyan_agent--+ |
    | |+----------+ +----------+|  | +-----------+ | |
    | ||scan_group| |scan_shard||  | | key_check | | |
    | |+--+-------+ +--+----+--+|  | +-----+-----+ | |
    | +---|------------|----|---+  +-------|-------+ |
    +-----|------------|----|--------------|---------+
          |            |    |              |
   (1)push|     (2)pull|    |(2)push       |(3)pull
          |            |    |              |
          |            |    |              |
          |            |    |              |
    +MQ---V------------V----V--------------V---------+
    |             +---------+                        |
    |             |Twemproxy| \                      |
    |             +--+---+--+  +----------+          |
    |                |   |     |Metaserver|          |
    |             +--V---V--+  +----------+          |
    |             |  Redis  | /                      |
    |             +---------+                        |
    +------------------------------------------------+

(1) scan_group(src_group, dst_group)
    获取源集群 redis shard 从库 ip port
(2) scan_shard(src_group, dst_group, src_ip, src_port)
    对从库进行 scan 获取 key，每 1000 个 key 进行打包发到队列（同时包含 src_ip, src_port, dst_ip, dst_port)
(3) key_check(src_ip, src_port, dst_ip, dst_port, key_list)
    从源集群获取集群 type
    根据 type 对集群的 key 的 value 进行检查长度，
        若长度不一则进行记录
            目标 value 长度为 0: 则记录为 lack_target
            目标 value 长度为其他 length： 则记录为 length
            目标 value 中含 "WRONGTYPE": 则记录为 type
            目标 value 异常：则记录为 "other"
        长度一致
            若检查内容，则对比内容的 md5 值
            
```

### 3.2 模块简介

> 架构图中如果有很多模块，需要对各个模块的功能进行简要介绍；

### 3.3 设计与折衷

> 设计与折衷是总体设计中最重要的部分；

### 3.4 潜在风险

## 4 详细设计

> 详细设计重点在“详细”

### 4.1 分发任务

> （有了数据库 + 接口 + 流程，别的同学拿到详设文档，基本也能够搞定了）

#### 4.1.1 交互流程

> 简要的交互可用文字说明，复杂的交互建议使用流程图，交互图或其他图形进行说明

#### 4.1.2 数据库设计

#### 4.1.3 接口形式

#### 4.1.4 Redis key

python 中 repr 和 eval 可以用来在数据结构和字符串间互转 在这个功能上，repr 和 str 的作用一样，把一个数据结构转换成字符串，例如：

```
>>> repr([1,2,3,4])
'[1, 2, 3, 4]'

eval 是把字符串转换成数据结构，例如：
>>> eval('[1,2,3,4]')
[1, 2, 3, 4]
```

Redis 中很多 key name 是使用的中文，可以先通过 repr(key) 转为字符串，塞入队列，然后处理 worker 获取到时先 eval(key\_str) 进行转换为原数据

## 5 数据统计

### 5.1 scan 耗时

> * 10G 数据，不限速

```
scan_count:256
cost:66s
counts:43676
key_num:8746171(874w)

avg_qps:661
cpu: 上涨 30%
```

> * 10G 数据（每次 scan 后，等待 10ms)

```
scan_count:256
cost:519s
counts:43752
key_num:8761618

avg_qps:84
cpu: 上涨 6%
```

### 5.2 scan 并发送任务耗时

> * 10G 数据 (900w key)

```
生产者：
    发送耗时：154s(3 分钟）
    开始时间：2021-09-01 12:23:29
    结束时间：2021-09-01 12:26:03

消费者：
    处理耗时：17 分钟
    开始时间：2021-09-01 12:23:30
    结束时间：2021-09-01 12:40:34

目标集群 CPU:
    上涨 5-10% 左右
```
