> 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/common/distributed-wuxing.md).

# 配置管理(五行)

## 1 项目概述

### 1.1 背景介绍及目标

#### 1.1.1 背景

(1) 配置管理是 PAAS 服务的基础组件，是业务迭代的重要组成部分，目前存在如下问题：

> * 无配置分发机制
> * 无配置检查流程
> * 无配置变更记录

(2) 巡检（监控）数据存储，目前巡检存储在 Redis 中，目前存在如下问题：

> * 无数据存储历史（每天巡检时清空之前数据，然后存储当天数据）
> * 查询数据时，数据列表索引存储在 set 类型中，目前是全量拉取数据，前端渲染耗时长

#### 1.1.2 目标

> * 解决配置 / 巡检（监控）等多种业务场景数据存储问题
> * 解决配置的管理、存储、发布问题
> * 针对配置的**版本管理**、**存储**、**查询**、**修改**进行设计
> * 提供配置的文件级的依赖管理，提供配置文件变更的触发机制

<img src="/files/Cc0WmUiKMiCuueHkcBvD" alt="" class="gitbook-drawing">

### 1.2 名词说明

> * section: 存储数据模板
> * instance: 存储数据记录
> * item: 存储数据值

### 1.3 Roadmap

> 一期：(2021-01-30)

```
(1) 实现数据服务抽象
{
    "namespace1": {
        "instance_name1": {
            "item_name1": {"item_type":"bool", "item_default": "true",
                           "item_value":True, "item_description": "switch", ...},
            "item_name2": {..},
        },
        "instance_name2": {
            "item_name1": {..},
            "item_name2": {..},
        },
    }
}
(2) 实现数据存储 / 查询基本功能
(3) 可追溯
    操作轨迹可追踪
    历史配置可查询和回溯
```

> 二期：

~~section 与状态机绑定，item 变更触发事件~~

通过星桥触发配置更新及配置分发操作

## 2 需求分析

### 2.1 功能需求

> * (1) 多种业务场景（含有巡检（监控）类数据，配置类数据）
> * (2) 数据类型多
> * (3) 可以查询数据变更情况（巡检类数据）
> * (4) 可以通过配置进行反查对应服务

### 2.2 非功能需求

### 2.3 调研

#### 2.3.1 业界方案

> * [天猫 App 的动态化配置中心实践](https://www.infoq.cn/article/app-dynamic-configuration/)
> * Facebook 配置中心
> * [360 https://github.com/Qihoo360/QConf](https://github.com/Qihoo360/QConf)

#### 2.3.2 OSP 平台

**配置创建**

所谓配置其实就是 "应用程序的变量", 和应用程序息息相关

当我们发布了程序包时，也会有对应需要的配置文件生成，即配置是和发布包绑定的

> * 确定了应用程序包对应配置的默认值
> * 确定了应用程序包涉及哪些配置（不同的版本间可能会增减）

通过 `name`, `program`, `version` 联合主键来标记配置唯一性

> config\_item

```
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| name        | varchar(128) | NO   | PRI | NULL    |       |
| description | varchar(512) | YES  |     | NULL    |       |
| type        | varchar(16)  | NO   |     | NULL    |       |
| default     | varchar(128) | YES  |     | NULL    |       |
| program     | varchar(64)  | NO   | PRI | NULL    |       |
| version     | varchar(32)  | NO   | PRI | NULL    |       |
+-------------+--------------+------+-----+---------+-------+

三个联合组件来标识不同的 version 的 program 的配置项 name

此表是存储的部署包的配置项
```

> 例子 (twemproxy)

```
+-------------------------+-----------------------------------------------------------------------------------------------------+------+---------+-----------+----------+
| name                    | description                                                                                         | type | default | program   | version  |
+-------------------------+-----------------------------------------------------------------------------------------------------+------+---------+-----------+----------+
| auto_eject_hosts        | set if server should be ejected temporarily when it fails consecutively server_failure_limit times. | bool | true    | twemproxy | 1.0.0.0  |
| client_connections      | set the maximum number of client connections can be opened to each server pool                      | int  | 0       | twemproxy | 1.0.0.0  |
| mbuf                    | set size of mbuf chunk in bytes (default: 16384 bytes)                                              | int  | 16384   | twemproxy | 1.0.0.0  |
| server_failure_interval | set time in usecs before we reset the server failures counter                                       | int  | 100000  | twemproxy | 1.0.0.0  |
| server_failure_limit    | set the number of consecutive failures on a server that would lead to it being temporarily ejected  | int  | 3       | twemproxy | 1.0.0.0  |
| server_retry_timeout    | set timeout value in msec to wait for before retrying on a temporarily ejected server               | int  | 2000    | twemproxy | 1.0.0.0  |
| timeout                 | set timeout in msec that we wait for to establish a connection for receive a response               | int  | 20      | twemproxy | 1.0.0.0  |
| verbosity               | set logging level (default: 5, min: 0, max: 11)                                                     | int  | 4       | twemproxy | 1.0.0.0  |
+-------------------------+-----------------------------------------------------------------------------------------------------+------+---------+-----------+----------+
```

**配置存储**

> redis

```
HGETALL "config:{service}:normal"
1) "config_info"
2) "{\"access_rule\":\"\",\"adapter_timeout\":\"1000\",\"auto_eject_hosts\":\"true\",\"client_connections\":\"0\",\"conn_num\":\"500\",\"conn_retry\":\"1\",\"distribution\":\"random\",\"env\":\"online\",\"hash\":\"crc16\",\"log_level\":\"4\",\"max_response_size\":\"65535\",\"mbuf\":\"16384\",\"msg_max_length_limit\":\"6553600\",\"preconnect\":\"true\",\"redis\":\"true\",\"rediscluster\":\"true\",\"server_failure_limit\":\"3\",\"server_retry_timeout\":\"2000\",\"slowlog\":\"true\",\"slowlog_slower_than\":\"100\",\"thread_num\":\"4\",\"timeout\":\"1000\",\"user_name\":\"meetbill\",\"user_tk\":\"meetbill\",\"verbosity\":\"4\"}"
3) "service"
4) "{service}"
5) "mtime"
6) "1538967701"
7) "type"
8) "normal"
```

每个 service 的配置使用 hash 结构进行存储

```
即默认配置和部署包（镜像）关联，创建 service 时，将会和 service 关联，并将配置存储在 redis 中

类比 Python 语法的话，部署包就是“类”，service 是实例化后的对象，配置就是“类”的属性
```

> 直接获取配置（所有配置 value 都是字符串形式）

```
{
  "access_rule": "",
  "adapter_timeout": "1000",
  "auto_eject_hosts": "true",
  "client_connections": "0",
  "conn_num": "500",
  "conn_retry": "1",
  "distribution": "random",
  "env": "online",
  "hash": "crc16",
  "log_level": "4",
  "max_response_size": "65535",
  "mbuf": "16384",
  "msg_max_length_limit": "6553600",
  "preconnect": "true",
  "redis": "true",
  "rediscluster": "true",
  "server_failure_limit": "3",
  "server_retry_timeout": "2000",
  "slowlog": "true",
  "slowlog_slower_than": "100",
  "thread_num": "4",
  "timeout": "1000",
  "user_name": "meetbill",
  "user_tk": "meetbill",
  "verbosity": "4"
}
```

**配置生成**

```
服务端 KV + 实例本地的程序
```

程序中含有初始默认配置，通过程序进行生成默认配置和修改配置

#### 2.3.3 SCS 简单配置生成

> * template + (user config)
> * 部署包(package) 关联 template(版本)
> * 部署包版本与配置版本不是一一对应

<details>

<summary>配置修改</summary>

发起变更

```
{
	"app_id": "scs-bj-xxxxxxxxx",
	"config_param": [{
		"app_id": "scs-bj-xxxxxxxx",
		"type": "proxy",
		"name": "scsproxy_auto_auth_address",
		"value": "192.168.0.0/16"
	}]
}
```

生成配置

```
{
	"Product": "",
	"action": "update_config_new",
	"callback_url": "",
	"params": {
		"meta": {
			"engine": "bdrpproxy",
			"engine_version": "3",
			"basedir": "/root",
			"port": 7099,
			"account_name": "default",
			"password": "xxxxxxxxxxx"                                           // 密文
		},
		"pkgs_to_install": [{
			"download_url": "",
			"md5": "",
			"name": "proxy-slot",
			"version": "",
			"deploy_path": "/root",
			"no_need_execute": true,
			"rendered_confs": [{                                                // 由服务端进行渲染生成
				"conf_path": "whitelist.ip",
				"conf_content": "*.*.*.* rwx\n"
			}, {
				"conf_path": "user.acl",
				"conf_content": "user default on \u003e<password>  +@all ~*"    // password
			}, {
				"conf_path": "nutcracker_.yml",
				"conf_content": "<nutcracker_yaml>"                             // nutcracker_yaml 配置文件
			}, {
				"conf_path": "whitelist.cidr",
				"conf_content": ""
			}, {
				"conf_path": "start_nutcracker.sh",
				"conf_content": "/root/supervise.centos -p /root/status/nutcracker -f \"/root/agent/bin/nutcracker -c /root/agent/conf/nutcracker_.yml ...\""
			}]
		}],
		"config_list": [{
			"name": "auto_auth_address",
			"value": "192.168.0.0/16"
		}],
		"agent_recovers": {                                                     // agent 依赖的配置文件, 文件中格式为 json
			"enable_slow_log": {                                                // 配置文件名为配置项 + ".txt", 如 enable_slow_log.txt
				"enable_slow_log_flag": 0
			},
			"hotkey": {
				"enable_hotkey": 0
			},
			"auth": {
				"client_auth": "<password>",                                    // password
				"redis_auth": "",
				"meta_auth": "",
				"auth_sign": 0
			},
			"id": {
				"uuid": "1.scs-bj-wawaqrszyvcy-bj_wbdpxvkdglus_itf_0-AZONE-bjdd.scs",
				"user_id": "a11602e1c4c24e59865b9bb9209ff16d",
				"cluster_id": 200009010,
				"node_id": ""
			},
			"proxy_conf": {
				"appid": 200009010,
				"cluster_entry": "192.168.96.21:6379",
				"conn_num": 16,
				"disabled_command": [],
				"engine": 1,
				"instance_type": 0,
				"ip_tag": 1,
				"mbuf_size": 4096,
				"mcpack_port": 8099,
				"node_info": [{
					"node": "172.17.35.69:6754:1",
					"node_id": "bj_wbdpxvkdglus_0",
					"server_name": "bj_wbdpxvkdglus_0",
					"uuid": "0.scs-bj-wawaqrszyvcy-bj_wbdpxvkdglus_0-AZONE-bjdd.scs"
				}],
				"node_show_id": "scs-bj-wawaqrszyvcy_proxy_2",
				"pool_name": "bj_wbdpxvkdglus",
				"port": 7099,
				"proxy_id": 200009010,
				"proxy_instance_id": 58623,
				"proxy_meta_master_ip": "<meta_ip>",                            // meta IP
				"proxy_meta_master_port": 7500,
				"proxy_version": 1,
				"qps": 200000,
				"sentinel_ip": "",
				"sentinel_port": 0,
				"stat_port": 9099,
				"store_type": 0,
				"white_ip_list": [{
					"ip": "*.*.*.*",
					"mode": "rwx"
				}]
			},
			"instance_metadata": {
				"port": 7099,
				"type": "proxy",
				"proc_name": "nutcracker",
				"engine": 1,
				"kernel_version": "6.0",
				"resource_type": "container"
			},
			"bcm_conf": {
				"cycle": 5,
				"refactoring_v1": 1
			},
			"blb": {
				"elb_ip": "172.17.35.97",
				"elb_port": 6379
			},
			"monitor": {
				"hash_name": "",
				"hash_id": "",
				"version": 5001,
				"store_type": 0,
				"noah_bns": "58623.cluster-200009010-proxysandbox.BCE.all",
				"noah_endpoint": "<noah_endpoint>",                             // noah endpoint
				"forbid_push": false,
				"proxy_instance_id": 58623
			}
		},
		"force_restart": false
	},
	"task_name": "scs",
	"task_type": "async",
	"timeout": 60
}
```

</details>

* Config
  * 服务配置文件(可能有多个配置文件)
  * Agent 配置文件(监控、日志采集等 agent)
* Auth
  * ACL 文件
  * Whitelist 文件(IP、CIDR) 等
  * Auth 配置

> &#x20;配置项 value 限制

```
MySQL:user_conf_list-------------------------------表
                 id: 103
          conf_name: repl-semi-sync-timeout
        conf_module: 1
          conf_desc: 5Y2K5ZCM5q2l6LaF5pe25pe26Ze077yM5Y2V5L2N5Li6IG1zPGJyLz4=
          conf_type: 2
         conf_range: 1-2147483647
       conf_default: 10000
 conf_cache_version: 10001
 conf_redis_version: 6.0
  conf_user_visible: 1
        need_reboot: 0
             engine:
lowest_full_version: 6.2.8

* 可以通过 conf_type + conf_range 限制 value 范围
  CONF_TYPE
     CONF_TYPE_SINGLE_SELECT = 1, 如 conf_range 为 true|false，conf_default 为 true，若 conf_range 为空，则不限制
     CONF_TYPE_NUMBER = 2, 如 conf_range 为 0-100000，conf_default 为 2000
     CONF_TYPE_MULTI_SELECT = 3, 如 conf_range 为 flushall|flushdb|keys|hgetall|scan，conf_default 为 flushall,flushdb
* conf_user_visible 设置了配置是否用户可见，1 不可见，0 可见
* need_reboot 提示配置是否需要重启
* lowest_full_version 设置了最低的版本
```

> 配置项 value 存储

```
读取 user_conf_list(默认配置) + conf_record_list(用户修改过的配置)
---
conf_history_list(配置修改历史)

MySQL:conf_record_list---------------------------表
         id: 19104
 cluster_id: 200011114
  conf_name: always-propagate-del
conf_module: 1
      value: yes
   effected: 0
```

> 模板渲染

```
conf_tpl + conf_tpl_item

* conf_tpl: 标记模板状态
* conf_tpl_item： 模板内容

MySQL:conf_tpl_item------------------------------表
         id: 743
     tpl_id: sync-agent-1-default-20230908155723
deploy_path: sync_.conf
render_type:
 created_at: 2023-09-08 07:56:32
 updated_at: 2023-09-08 07:57:24
    content: self_cluster={{.cluster_show_id}}
src_redis_ip={{.host}}
src_redis_port={{.port}}
```

> 模板使用

```
MySQL:package------------------------------------表
package_id --> tpl_id
```

## 3 总体设计

### 3.1 系统架构

```
    +----------------+
    |     wuxing     |
    +---+---------+--+
        |         |
    +---V---+  +--V--+
    | MySQL |  |Redis|
    +-------+  +-----+
```

### 3.2 模块简介

五行整体分为三个模块

> * wuxing 为业务逻辑模块
> * MySQL 存储 wuxing 数据
> * Redis 存储 wuxing 缓存

### 3.3 设计与折衷

#### 3.3.1 配置发布方式

> (1) 基本的 KV + 模板生成配置

```
UI 编写模板（模板以.template 为后缀），值
触发上线时，通过本地配置派生工具生成配置
```

例如可以使用 butterfly 自带的模板功能渲染文件

> (2) 基本的 KV + 本地的生成脚本进行生成配置

```
程序内编写模板
配置不会主动生成，通过 config-get 获取值 (json)，然后通过 conf-parser 把模板 + 值一起生成配置，这样用户可以加入一些自定义元素
```

五行偏向于数据的管理，弱化配置管理的模板功能，故选择方案二

{% hint style="info" %}
五行中可以新增 template 表，存储配置模板

模板与 section 关联，可以添加一个参数判断是否获取渲染文件

如果需要获取渲染文件，则根据 section 关联的 template 进行渲染生成
{% endhint %}

#### 3.3.2 配置版本管理

> (1) 版本号方式

```
添加版本号字段
配置表只追加不更新
```

> (2) 操作历史方式

```
添加一个操作历史表
对配置的创建，更新，删除时，都对操作历史表进行新增一条记录
```

折衷分析：使用方案一时，遇到列表展示等需求时不太好实现，故使用"操作历史方式"

#### 3.3.3 存储 key 的类型

> zabbix 的 key 类型

```
show tables like 'history%';
+--------------------------+
| Tables_in_rtm (history%) |
+--------------------------+
| history                  | 存放信息类型为浮点数的监控项历史数据
| history_log              | 存放信息类型为日志的监控项历史数据
| history_str              | 存放信息类型为字符的监控项历史数据
| history_text             | 存放信息类型为文本的监控项历史数据
| history_uint             | 存放信息类型为数字（无正负）的监控项历史数据
+--------------------------+
```

类似的配置 / 属性中也可以存储下浮点数，字符，数字三种类型

> * wuxing\_float
> * wuxing\_uint
> * wuxing\_str

这样可以直接使用数据用于统计使用，变更历史可以为：

> * wuxing\_history\_float
> * wuxing\_history\_int
> * wuxing\_history\_str
> * wuxing\_history\_bool

折衷考虑，item 存储在一张表中方便管理，故 item value 数据会存储在 wuxing\_instance\_item 中，item 的历史记录会存储在 `wuxing_history_{value type}` 中

#### 3.3.4 item 与状态机关联

> (1) section 模板中指定状态机

```
比如：
section_template:
{
    "item_name1": {
        ...
        "fsm": "xxxxx",
    }
}

然后实例化 instance 时，会转为
instance_template:
{
    "item_name1": {
        ...
        "fsm": "xxxxx",
    }
}

优势：section item 与 fsm 强绑定，约束性好
```

> (2) instance\_update\_item 时，指定状态机

```
instance_update_item(req, namespace, instance_name, item_name, item_value):
改为
instance_update_item(req, namespace, instance_name, item_name, item_value, item_fsm_name=None):

当传指定 item_fsm_name 时，根据指定的状态机获取到 item 对象，然后变更 item_name

优势：可灵活指定 fsm
劣势：item 与 fsm 关联性差，需要业务配置其关联关系
```

> (3) instance\_update\_item 时，指定是否使用状态机

```
instance_update_item(req, namespace, instance_name, item_name, item_value):
改为
instance_update_item(req, namespace, instance_name, item_name, item_value, use_item_fsm=False):

当 use_item_fsm 为 True 时，根据一定命名规则找对应 fsm，然后根据指定的状态机获取到 item 对象，然后变更 item_name
比如可以通过 namespace_sectionname_item_name 进行命名在状态机 map 中进行查找

优势：item 与 fsm 松耦合
```

根据约定优于配置，配置优于实现原则，(3) > (2) > (1) 故选择方案 (3)

实现方式还在调研中

#### 3.3.5 item 与触发器关联

状态机模式关联 item 后，可以限制 item\_value 在有限 value 间进行流转

如果存储的是巡检类数据的话，则数据不是固定的，需要在特定条件下进行触发事件，这个业界可参考的方案很多，比如 Zabbix/Nagios/Open-Falcon/Prometheus

(1) zabbix 方式

```
当前语法
{<server>:<key>.<function>(<parameter>)}<operator><constant>
eg: {host:key.func(params)}=0

后续语法 (5.4 版本以后）
func(/host/key, params)
func(/host1/key1, /host2/key2)
```

#### 3.3.6 value 限制

| conf\_type | conf\_range                            | conf\_default    |
| ---------- | -------------------------------------- | ---------------- |
| 1(单选)      | yes\|no\|partial                       | yes              |
| 2(数字)      | 1-500                                  | 20               |
| 3(多选)      | flushall\|flushdb\|keys\|hgetall\|scan | flushall,flushdb |
| 4(正则)      | 通过正则进行比较                               |                  |

* 规则1 若type==1单选
  * 用|切分conf-range形成list1
  * 判断目标值是否在list1中
* 规则2 若type==2 数字
  * 判断目标参数值是否为数字
  * 用-切分conf-range，与目标值比大小（左闭右闭）
* 规则3 若type==3 多选
  * 用|切分conf-range形成list1
  * 用,切分目标值形成list2
  * 判断list2中元素都在list1中

#### 3.3.7 配置分发机制

TODO: service 对应多个配置文件

### 3.4 潜在风险

## 4 详细设计

### 4.1 交互流程

#### 4.1.1 section

**4.1.1.1 增**

> 创建 section(section\_create)

```
       开始
        |
        V
输入 (namespace, section_name, section_version)
        |
        V                  是
检查 section 是否存在 --------> ERR_SECTION_IS_EXIST
        | 否
        V                  否
创建 section 记录是否成功 ----> ERR_SECTION_CREATE_FAILED
        | 是
        V
       OK
```

**4.1.1.2 删**

> 删除 section(section\_delete)

```
       开始
        |
        V
输入 (namespace, section_name, section_version)
        |
        V                  否
检查 section 是否存在 --------> ERR_SECTION_IS_NOT_EXIST
        | 是
        V                  否
删除 section 记录是否成功 ----> ERR_SECTION_DELETE_FAILED
        | 是
        V
       OK
```

**4.1.1.3 改**

> section 模板中添加 item(section\_item\_add)

```
                       开始
                        |
                        V
输入 (namespace, section_name, section_version, item_name, item_default, item_description)
                        |
                        V                                     否
     要添加的 item 前缀是否在 ["i|", "s|", "f|", "b|"] 中 -------------> ERR_SECTION_ITEM_TYPE_FAILED
                        | 是
                        V                                     否
                  section 是否存在 ------------------------------------> ERR_SECTION_IS_NOT_EXIST
                        | 是
                        V                                     否
              section 是否为未启用状态 --------------------------------> ERR_SECTION_IS_ENABLED
                        | 是
                        V
                  获取 section 模板
                        |
                        V                                     是
             要添加的 item 是否已在模板中 -----------------------------> ERR_SECTION_ITEM_IS_EXIST
                        | 否
                        V
              添加 item 到 section 模板中
                        |
                        V
              计算 section 模板 md5 值
                        |
                        V                                     否
               是否成功更新数据到存储中 -------------------------------> ERR_SECTION_ITEM_ADD_FAILED
                        | 是
                        V
                       OK
```

> section 模板中删除 item(section\_item\_delete)

```
                       开始
                        |
                        V
输入 (namespace, section_name, section_version, item_name)
                        |
                        V                                     否
                  section 是否存在 ------------------------------------> ERR_SECTION_IS_NOT_EXIST
                        | 是
                        V                                     否
              section 是否为未启用状态 --------------------------------> ERR_SECTION_IS_ENABLED
                        | 是
                        V
                  获取 section 模板
                        |
                        V                                     否
             要添加的 item 是否在模板中 -------------------------------> ERR_SECTION_ITEM_IS_NOT_EXIST
                        | 是
                        V
             从 section 模板中移除 item
                        |
                        V
              计算 section 模板 md5 值
                        |
                        V                                     否
               是否成功更新数据到存储中 -------------------------------> ERR_SECTION_ITEM_DELETE_FAILED
                        | 是
                        V
                       OK
```

> 启用 section(section\_enable)

```
       开始
        |
        V
输入 (namespace, section_name, section_version)
        |
        V                  否
检查 section 是否存在 --------> ERR_SECTION_IS_NOT_EXIST
        | 是
        V                  否
启用 section 是否成功 --------> ERR_SECTION_ENABLE_FAILED
        | 是
        V
       OK
```

**4.1.1.4 查**

> section 列表 (section\_list)

```
        开始
         |
         V
   设置需要获取的字段 (namespace, section_name, section_version, section_md5, is_enabled, u_time)
         |
         V
     添加搜索条件
         |
         V
进行获取 section 分页列表及总数
         |
         V
        OK
```

> section 详情 (section\_get)

```
       开始
        |
        V
输入 (namespace, section_name, section_version)
        |
        V                  否
检查 section 是否存在 --------> ERR_SECTION_IS_NOT_EXIST
        | 是
        V
转换 section 数据为可读方式
        |
        V
       OK
```

#### 4.1.2 instance

**4.1.2.1 增**

```
            开始
             |
             V
输入：namespace, instance_name, section_name, section_version, items_data=None
             |
             V                    是
      检查是否有此 instance-----------------> ERR_INSTANCE_IS_EXIST
             |
             V
      获取 section 模板
             |
             V                    否
       section 是否存在 ---------------------> ERR_SECTION_IS_NOT_EXIST
             | 是
             V                    否
       section 是否已启用 -------------------> ERR_SECTION_IS_NOT_ENABLED
             | 是
             V                  Exception
       增加 item 记录 -----------------------> ERR_ITEM_XX
             |
             V
     增加 instance 记录
             |
             V
           结束
```

**4.1.2.2 删**

```
            开始
             |
             V
    输入：namespace, instance_name
             |
             V                   否
     检查 instance 是否存在 -------------> ERR_INSTANCE_IS_NOT_EXIST
             | 是
             V                 Exception
删除 instance 关联的所有 item -----------> ERR_INSTANCE_IS_NOT_EXIST
             |（此时不从 instance_template 中获取 item_id 进行逐个删除，
             | 而是直接删除 instance_item 表中所有 instance 相关的 item)
             V                 Exception
      删除 instance ---------------------> ERR_INSTANCE_DELETE_FAILED
             |
             V
           结束
```

**4.1.2.3 改**

> 更新 item 值

```
            开始
             |
             V
输入：namespace, instance_name, item_name, item_value
             |
             V                          否
      检查是否有此 instance-----------------------> ERR_INSTANCE_IS_NOT_EXIST
             | 是
             V
      获取 instance 模板
             |
             V                          否
instance_template 中是否存在此 item_name----------> ERR_ITEM_IS_NOT_EXIST
             | 是
             V                      Exception
      更新 item 记录 ------------------------------> ERR_ITEM_XX
             |
             V
           结束
```

> 更新 section 版本

```
            开始
             |
             V
输入：namespace, instance_name, section_version
             |
             V                    否
      检查是否有此 instance-----------------> ERR_INSTANCE_IS_NOT_EXIST
             |
             V
      获取 instance 当前模板
             |
             V                    否
      新 section 是否存在 -------------------> ERR_SECTION_IS_NOT_EXIST
             | 是
             V                    否
     新  section 是否已启用 -----------------> ERR_SECTION_IS_NOT_ENABLED
             | 是
             V
新 section_template 与当前 instance_template 的 item_name 做 diff
             |
             V                  Exception
  对当前 instance 增加 item 记录 ------------> ERR_ITEM_XX
             |
             V
  对当前 instance 删除 item 记录 ------------> ERR_ITEM_XX
             |（删除比 section_template 多的 item)
             V
     更新 instance 记录 (md5, 模板等）
             |
             V
           结束
```

**4.1.2.4 查**

> 列表

```
```

> 详情

```
```

#### 4.1.3 item

**4.1.3.1 增**

> 备注

```
在 instance_item 中新增 item 仅由 instance 接口触发创建，无法直接创建 item
```

**4.1.3.2 删**

> 备注

```
在 instance_item 中新增 item 仅由 instance 接口触发删除，无法直接删除 item
```

**4.1.3.3 改**

**4.1.3.4 查**

### 4.2 数据库设计

#### 4.2.1 表之间关系

```
+namespace1-------------------------------------------------------+
|                   +section1-----------------+                   |
|                   |+section_template-------+|                   |
|                   ||+-----+ +-----+ +-----+||                   |
|                   |||item1| |item2| |item3||| version:1.0.1     |
|                   ||+-----+ +-----+ +-----+||                   |
|                   |+-----------------------+|                   |
|                   +------+---------+--------+                   |
|                          |         |                            |
|                          |         +---------+                  |
|                          |                   |                  |
|                          |                   |                  |
|   +instance1-------------V--+  +instance2----V-----------+      |
|   |+instance_template------+|  |+instance_template------+|      |
|   ||+id:1-+ +id:2-+ +id:3-+||  ||+id:4-+ +id:5-+ +id:6-+||      |
|   |||item1| |item2| |item3|||  |||item1| |item2| |item3|||      |
|   ||+----++ +---+-+ +---+-+||  ||+--+--+ +---+-+ +---+-+||      |
|   |+-----|------|-------|--+|  |+---|--------|-------|--+|      |
|   +------|------|-------|---+  +----|--------|-------|---+      |
|          |      |       |           |        |       |          |
|   +item--|------|-------|-----------|--------|-------+---+      |
|   | +id:1V--+   |       |           |        |       |   |      |
|   | +-------+   |       |           |        |       |   |      |
|   |        +id:2V--+    |           |        |       |   |      |
|   |        +-------+    |           |        |       |   |      |
|   |               +id:3-V-+         |        |       |   |      |
|   |               +-------+         |        |       |   |      |
|   |                           +id:4-V-+      |       |   |      |
|   |                           +-------+      |       |   |      |
|   |                                    +id:5-V-+     |   |      |
|   |                                    +-------+     |   |      |
|   |                                            +id:6-V-+ |      |
|   |                                            +-------+ |      |
|   +------------------------------------------------------+      |
+-----------------------------------------------------------------+

 (1) wuxing_section 表存储 section 记录
 (2) wuxing_instance 表存储 instance 记录
     『创建』 创建 instance 时会涉及如下行为：
         1> 关联 section 及对应版本，继承 section 模板
         2> 根据 item value 数据类型，在 item 表中以模板默认值创建记录
 (3) wuxing_instance_item 表存储 instance 的 item 具体 value 值

+history----------------------------------------------------------+
|+------------------------+           +-------------------------+ |
||wuxing_history_bool     |           |wuxing_history_float     | |
|+------------------------+           +-------------------------+ |
|+------------------------+           +-------------------------+ | 此五个表记录 instance item 的 value 变更历史
||wuxing_history_int      |           |wuxing_history_string    | |
|+------------------------+           +-------------------------+ |
|+------------------------+                                       |
||wuxing_history_text     |                                       |
|+------------------------+                                       |
+-----------------------------------------------------------------+

备注：
(1) namespace 表是命名空间，同一个 namespace 下 (section_name, section_version) 实例以及 instance 实例 是唯一的
(2) instance 只会关联一个 (section_name, section_version)
(3) item 的类型是固定的
(4) section-instance-instance_item 关系
    section 类似 Python 中的类
    instance 类似 Python 中类实例化后的对象，会继承 section 这个类的属性
    instance_item 类似 Python 中类实例化后对象的属性，会根据 item 的数据类型存储在不同的 value 字段中
```

#### 4.2.2 表详情

**4.2.2.1 section**

> 表名：wuxing\_section

```
+------------------+---------------+------+-----+---------+-------+
| Field            | Type          | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+-------+
| namespace        | varchar(16)   | NO   | PRI | NULL    |       |
| section_name     | varchar(64)   | NO   | PRI |         |       |
| section_version  | varchar(16)   | NO   | PRI | NULL    |       |
| section_template | varchar(4096) | NO   |     | NULL    |       |
| section_md5      | varchar(64)   | NO   | MUL | NULL    |       |
| is_enabled       | tinyint(1)    | NO   | MUL | NULL    |       |
| user             | varchar(32)   | NO   | MUL | NULL    |       |
| u_time           | datetime      | NO   |     | NULL    |       |
| c_time           | datetime      | NO   |     | NULL    |       |
+------------------+---------------+------+-----+---------+-------+
9 rows in set (0.00 sec)
```

**4.2.2.2 instance**

> 表名：wuxing\_instance

```
+-------------------+---------------+------+-----+---------+-------+
| Field             | Type          | Null | Key | Default | Extra |
+-------------------+---------------+------+-----+---------+-------+
| namespace         | varchar(16)   | NO   | PRI | NULL    |       |
| instance_name     | varchar(64)   | NO   | PRI | NULL    |       |
| instance_template | varchar(4096) | NO   |     | NULL    |       |
| section_name      | varchar(64)   | NO   | MUL | NULL    |       |
| section_version   | varchar(64)   | NO   | MUL | NULL    |       |
| section_md5       | varchar(64)   | NO   | MUL | NULL    |       |
| is_valid          | tinyint(1)    | NO   |     | NULL    |       |
| c_time            | datetime      | NO   | MUL | NULL    |       |
| u_time            | datetime      | NO   | MUL | NULL    |       |
+-------------------+---------------+------+-----+---------+-------+
9 rows in set (0.00 sec)
```

**4.2.2.3 item**

> 表名：wuxing\_instance\_item

```
desc wuxing_instance_item;
+-------------------+----------------+------+-----+---------+----------------+
| Field             | Type           | Null | Key | Default | Extra          |
+-------------------+----------------+------+-----+---------+----------------+
| id                | int(11)        | NO   | PRI | NULL    | auto_increment |
| namespace         | varchar(16)    | NO   | MUL | NULL    |                |
| section_name      | varchar(64)    | NO   | MUL | NULL    |                |
| instance_name     | varchar(64)    | NO   | MUL | NULL    |                |
| item_name         | varchar(64)    | NO   | MUL | NULL    |                |
| item_type         | varchar(64)    | NO   | MUL | NULL    |                |
| item_value_bool   | tinyint(1)     | YES  | MUL | NULL    |                |
| item_value_float  | double         | YES  | MUL | NULL    |                |
| item_value_int    | int(11)        | YES  | MUL | NULL    |                |
| item_value_string | varchar(128)   | YES  | MUL | NULL    |                |
| item_value_text   | varchar(20000) | YES  |     | NULL    |                |
| user              | varchar(32)    | NO   |     | NULL    |                |
| c_time            | datetime       | NO   | MUL | NULL    |                |
| u_time            | datetime       | NO   | MUL | NULL    |                |
+-------------------+----------------+------+-----+---------+----------------+
14 rows in set (0.00 sec)
```

**4.2.2.4 item history**

> 表名：wuxing\_history\_bool

```
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| item_id    | int(11)     | NO   | MUL | NULL    |                |
| item_value | tinyint(1)  | NO   |     | NULL    |                |
| cmd        | varchar(8)  | NO   |     | NULL    |                |
| user       | varchar(32) | NO   |     | NULL    |                |
| c_time     | datetime    | NO   | MUL | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
```

> 表名：wuxing\_history\_float

```
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| item_id    | int(11)     | NO   | MUL | NULL    |                |
| item_value | double      | NO   |     | NULL    |                |
| cmd        | varchar(8)  | NO   |     | NULL    |                |
| user       | varchar(32) | NO   |     | NULL    |                |
| c_time     | datetime    | NO   | MUL | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
```

> 表名：wuxing\_history\_int

```
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| item_id    | int(11)     | NO   | MUL | NULL    |                |
| item_value | int(11)     | NO   |     | NULL    |                |
| cmd        | varchar(8)  | NO   |     | NULL    |                |
| user       | varchar(32) | NO   |     | NULL    |                |
| c_time     | datetime    | NO   | MUL | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
```

> 表名：wuxing\_history\_string

```
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| item_id    | int(11)      | NO   | MUL | NULL    |                |
| item_value | varchar(128) | NO   |     | NULL    |                |
| cmd        | varchar(8)   | NO   |     | NULL    |                |
| user       | varchar(32)  | NO   |     | NULL    |                |
| c_time     | datetime     | NO   | MUL | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
```

> 表名：wuxing\_history\_text

```
+------------+----------------+------+-----+---------+----------------+
| Field      | Type           | Null | Key | Default | Extra          |
+------------+----------------+------+-----+---------+----------------+
| id         | int(11)        | NO   | PRI | NULL    | auto_increment |
| item_id    | int(11)        | NO   | MUL | NULL    |                |
| item_value | varchar(20000) | NO   |     | NULL    |                |
| cmd        | varchar(8)     | NO   |     | NULL    |                |
| user       | varchar(32)    | NO   |     | NULL    |                |
| c_time     | datetime       | NO   | MUL | NULL    |                |
+------------+----------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
```

### 4.3 接口

[接口文档](https://github.com/meetbill/butterfly/wiki/distributed_wuxing_user_guide)

## 5 使用实践

### 5.1 命名方式

> * namespace（最多 16 个字符）: 会表明是哪个模块在使用，此数据是监控类数据还是配置类数据，可以通过 "app\_分类\_功能" 进行划分，比如：
>   * m（监控 / 巡检类数据），c（配置类数据），s（统计类数据）
>   * 例子
>     * bq\_m\_conf:（表示扁鹊配置检查类数据）
>     * bq\_m\_state:（表示扁鹊运行状态类数据）
>     * bq\_m\_entry:（表示扁鹊入口检查类数据）
>     * qn\_c\_global:（表示青囊全局配置）
>     * qn\_c\_cluster:（表示青囊集群配置）
>     * xh\_s\_resource:（表示星河资源统计）
> * section（最多 64 个字符）: 可以通过 "level\_module" 进行划分
>   * 例子
>     * pool\_redis（全局）
>     * group\_redis(redis 集群）
>     * service\_proxy(proxy 组件）
>     * service\_redis(redis 组件）
>     * unit\_proxy(redis 实例）

> section level

```
level: 池 / 集群 / 组件 / 实例
+pool---------------------------+
|+group---------------+         |
||+service------+     |         |
|||+unit--+     |     |         |
||||      | ... | ... |   ...   |
|||+------+     |     |         |
||+-------------+     |         |
|+--------------------+         |
+-------------------------------+
```

### 5.2 已有表如何进行转换为五行方式

> 表 + 单个主键

```
原方式：
(table_name + primary_key field name) + field1_name + field2_name


新方式：
(namespace + section_name) + section_template(item1 + item2 + item3)
                | |
                 V
(namespace + instance_name) + instance_template(item1 + item2 + item3)
```

### 5.3 section 与 instance 列表展示

```
instance 列表
+-----------+--------------+-------------+---------------+-----------+------+-------------------+
|namespace  |instance_name |section_name |section_version|section_md5|item1 |       u_time      |
+-----------+--------------+-------------+---------------+-----------+------+-------------------+
|namespace1 |instance_name1|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
|namespace1 |instance_name2|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
|namespace1 |instance_name3|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
|namespace1 |instance_name4|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
|namespace1 |instance_name5|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
|namespace1 |instance_name6|section_name1|1.0.1          |xxxxxxxx   |xxxx  |2021-01-16 10:02:02|
+-----------+--------------+-------------+---------------+-----------+------+-------------------+

资源池可用资源 (namespace + instance_name 要确定唯一记录）
# 方案一 (X)
+-----------+--------------+-------------+---------------+-----------+----------------+-------------------+
| namespace |instance_name |section_name |section_version|section_md5| available_count|       u_time      |
+-----------+--------------+-------------+---------------+-----------+----------------+-------------------+
|xh_s_1pool |     bj       |     idc     |     1.0.1     |xxxxxxxx   |       20       |2021-01-16 10:02:02|
|xh_s_2pool |     nj       |     idc     |     1.0.1     |xxxxxxxx   |      250       |2021-01-16 10:02:02|
+-----------+--------------+-------------+---------------+-----------+----------------+-------------------+
# 方案二
+-------------+-------------+------------+---------------+-----------+-----+-----+-----+-------------------+
|  namespace  |instance_name|section_name|section_version|section_md5|pool1|pool2|poolX|       u_time      |
+-------------+-------------+------------+---------------+-----------+-----+-----+-----+-------------------+
|xh_s_resource|     bj      |     idc    |     1.0.1     |xxxxxxxx   | 20  | 50  |  X  |2021-01-16 10:02:02|
|xh_s_resource|     nj      |     idc    |     1.0.1     |xxxxxxxx   | 250 | 15  |  X  |2021-01-16 10:02:02|
+-------------+-------------+------------+---------------+-----------+-----+-----+-----+-------------------+
```

### 5.4 表限制

> * namespace: (string) 16 个字节限制
> * section\_name: (string) 64 个字节限制
> * section\_version: (string): 16 个字节
> * instance\_name: (string) 64 个字节
> * section\_template: (string) 4k 字节
> * instance\_template: (string) 4k 字节

```
单个 item 基本是 100 个字节左右

也就是单个 instance 可以有 40 个 item

PS: instance 当做一条记录的话，也就是可以有 40 个字段用于存储数据
```

### 5.5 cache

```
# section
规则：wuxing_section@{namespace}@{section_name}@{section_version}
eg.: cache:wuxing_section@group_qingnang@group_appid@1.0.1
备注：cache: 前缀为框架自动添加

# item
规则：wuxing_item@{item_id}
eg.: cache:wuxing_item@3
备注：cache: 前缀为框架自动添加
```

## 6 部署

<https://github.com/meetbill/butterfly/wiki/wuxing\\_ha>

## 7 接入五行例子

### 7.1 【星河】统计各机房资源池信息

> * namespace:section\_name:section\_version(xh\_s\_resource:idc:1.0.1)
> * instance 是机房 idc 名称
> * item 是机房的各个资源池剩余资源 (item\_name 为资源池标识）

### 7.2 【青囊】青囊集群级别以及全局级别开关

#### 7.2.1 集群级别开关

> * namespace:section\_name:section\_version(qn\_c\_cluster:group\_redis:1.0.1)
> * instance\_name 是各集群唯一标识 id(instance\_name 会以字符串方式进行存储）
> * item 是集群的各个开关及自愈相关的配置信息

### 7.3 【扁鹊】扁鹊 group/service/unit 级别巡检数据

bianque// 待完善

## 8 五行 echarts

### 8.1 访问五行 DB

#### 8.1.1 查询 item 数据

item 行数据需要转换为列数据

```
SELECT instance_name,
    MAX(CASE item_name WHEN '{s|item_name1}' THEN item_value_string ELSE 0 END ) item_name1,
    MAX(CASE item_name WHEN '{i|item_name2}' THEN item_value_string ELSE 0 END ) item_name2
FROM wuxing_instance_item
WHERE namespace='{namespace}' and section_name = '{section_name}'
GROUP BY instance_name
LIMIT 20;
```

> 例子

```
SELECT instance_name,
    MAX(CASE item_name WHEN 's|app_name' THEN item_value_string ELSE 0 END ) app_name,
    MAX(CASE item_name WHEN 'i|unit_appid' THEN item_value_int ELSE 0 END ) appid,
    MAX(CASE item_name WHEN 's|unit_matrixid' THEN item_value_string ELSE 0 END ) container_id,
    MAX(CASE item_name WHEN 's|unit_version' THEN item_value_string ELSE 0 END ) version
FROM wuxing_instance_item
WHERE namespace='bq_m_state' and section_name = 'unit_proxy'
GROUP BY instance_name
LIMIT 20;
```

#### 8.1.2 行转列参考

举个学生成绩信息的例子

**8.1.2.1 建表及插入数据**

```
-- 创建表
drop table if EXISTS t_score;

create table t_score(
id int auto_increment primary key,
u_name varchar(32),
c_name varchar(32),
score double
);
-- 插入数据
insert into t_score(u_name, c_name, score) values
('张三', 'JavaSE', 80),
('张三', 'JDBC', 90),
('张三', 'Servlet', 85),
('李四', 'JavaSE', 70),
('李四', 'JDBC', 80),
('李四', 'Servlet', 80),
('王五', 'JavaSE', 90),
('王五', 'JDBC', 90),
('王五', 'Servlet', 60);
```

**8.1.2.2 行转列**

> 使用多表连接的方式

```
-- 1. 使用多表连接查询的方式
select
	t1.u_name, t2.score as 'JavaSE', t3.score as 'JDBC', t4.score as 'Servlet'
from t_score t1
join (select u_name, score from t_score where c_name = 'JavaSE')t2  on t2.u_name = t1.u_name
join (select u_name, score from t_score where c_name = 'JDBC')t3  on t3.u_name = t3.u_name
join (select u_name, score from t_score where c_name = 'Servlet')t4  on t4.u_name = t4.u_name
group by t1.u_name;
```

> 使用 MySQL 中 case when then else end 结构

```
-- 2. 使用 case when 条件 then 字段值 else 值 end
select u_name
,sum(case when c_name = 'JavaSE' then score else 0 end ) as 'JavaSE'
,sum(case when c_name = 'JDBC' then score else 0 end ) as 'JDBC'
,sum(case when c_name = 'Servlet' then score else 0 end ) as 'Servlet'
from t_score
group by u_name;
```

#### 8.1.3 peewee join 例子

You can also order across joins. Assuming you want to order tweets by the username of the author, then by created\_date:

```
query = (Tweet
         .select()
         .join(User)
         .order_by(User.username, Tweet.created_date.desc()))
```

> SQL

```
SELECT t1."id", t1."user_id", t1."message", t1."is_published", t1."created_date"
FROM "tweet" AS t1
INNER JOIN "user" AS t2
  ON t1."user_id" = t2."id"
ORDER BY t2."username", t1."created_date" DESC
```

[peewee 文档](http://docs.peewee-orm.com/en/latest/peewee/querying.html#sorting-records)

> join on

```
recursive = (RTerm
             .select(RTerm.id, RTerm.name, RTerm.parent, rlevel, rpath)
             .join(base_case, on=(RTerm.parent == base_case.c.id)))

# The recursive CTE is created by taking the base case and UNION ALL with
# the recursive term.
cte = base_case.union_all(recursive)
```

### 8.2 图表

#### 8.2.1 需求

```
                +--------- item1 value
                |
                |
instance -------+--------- item2=value2 （条件）
                |
                |
                +--------- item3=value2 （条件）
```

统计某个 item2=value2, item3=value3 的条件下，item1 的各个 value 的统计数

而图表的下钻可以通过增加 item=value 来进行条件约束

#### 8.2.2 SQL

> (1) 直接获取某个 item\_name 的 value 分布

```
SELECT `t1`.`item_value_string` AS `item_value`, COUNT(*) AS `count`
FROM `wuxing_instance_item` AS `t1`
WHERE ((`t1`.`namespace` = 'bq_m_state'
    AND `t1`.`section_name` = 'unit_redis')
    AND `t1`.`item_name` = 's|unit_idc')
GROUP BY `t1`.`item_value_string`
```

> (2) condition1

```
SELECT `t1`.`item_value_string` AS `item_value`, COUNT(*) AS `count`
FROM `wuxing_instance_item` AS `t1`
INNER JOIN (SELECT `condition1`.`instance_name`
    FROM `wuxing_instance_item` AS `condition1`
    WHERE (((`condition1`.`namespace` = 'bq_m_state'
        AND `condition1`.`section_name` = 'unit_redis')
        AND `condition1`.`item_name` = 's|app_name')
        AND `condition1`.`item_value_string` = 'xxx_app_name'))
AS `condition1` ON (`t1`.`instance_name` = `condition1`.`instance_name`)
WHERE ((`t1`.`namespace` = 'bq_m_state'
    AND `t1`.`section_name` = 'unit_redis')
    AND `t1`.`item_name` = 's|unit_idc')
GROUP BY `t1`.`item_value_string`
```

> (3) condition1 + condition2

```
SELECT `t1`.`item_value_string` AS `item_value`, COUNT(*) AS `count`
FROM `wuxing_instance_item` AS `t1`
INNER JOIN (SELECT `condition1`.`instance_name`
    FROM `wuxing_instance_item` AS `condition1`
    WHERE (((`condition1`.`namespace` = 'bq_m_state'
        AND `condition1`.`section_name` = 'unit_redis')
        AND `condition1`.`item_name` = 's|app_name')
        AND `condition1`.`item_value_string` = 'xxx_app_name'))
AS `condition1` ON (`t1`.`instance_name` = `condition1`.`instance_name`)
INNER JOIN (SELECT `condition2`.`instance_name`
    FROM `wuxing_instance_item` AS `condition2`
    WHERE (((`condition2`.`namespace` = 'bq_m_state'
        AND `condition2`.`section_name` = 'unit_redis')
        AND `condition2`.`item_name` = 's|unit_ip')
        AND `condition2`.`item_value_string` = 'xxx_ip'))
AS `condition2` ON (`t1`.`instance_name` = `condition2`.`instance_name`)
WHERE ((`t1`.`namespace` = 'bq_m_state'
    AND `t1`.`section_name` = 'unit_redis')
    AND `t1`.`item_name` = 's|unit_idc')
GROUP BY `t1`.`item_value_string`
```

### 8.3 例子

某个 Redis 机房有断电风险，需要看下此机房可能影响的集群，如果有此机房的主库则需要重点关注下

```
                          region
                            |
                            V
                           idc
                         /     \
                        |       |
                       role    app
                        |   (条件:idc)
                       app
                 (条件:idc+role)
```

## 9 传送门

> * <https://github.com/google/gin-config>
> * <https://github.com/mozilla/configman>
> * <https://github.com/install-logos/ricedb>
> * <https://github.com/opendevops-cn/kerrigan> --- 是将整个配置文件当做一个文本存储到数据库中
> * [百度 better remote config center](https://github.com/baidu/brcc)
> * [天猫 App 的动态化配置中心实践](https://www.infoq.cn/article/app-dynamic-configuration/)
> * Facebook 配置中心
> * [360 https://github.com/Qihoo360/QConf](https://github.com/Qihoo360/QConf)
> * [ovine](https://ovine.igroupes.com/org/showcase)
>   * OvineData

### 9.1 其他
