4. Redis Value Schema 定义实战:String 与 Hash 约束
契约校验的核心在于对 Redis Value 的结构与内容进行精确描述。本章将聚焦于两种最常用的数据类型——String 和 Hash,通过定义具体的 Schema 规则,实现从“存什么”到“怎么存”的规范化控制。
4.1 String 类型约束:长度与正则
String 是 Redis 中最基础的值类型,但实际业务中往往需要限制其内容格式。例如:手机号必须是 11 位数字、验证码必须是 6 位数字、用户昵称长度在 2-20 个字符之间。通过 Schema 定义长度与正则约束,可以在写入前完成校验。
4.1.1 长度约束定义
长度约束用于限制 String 值的字符数量(非字节数)。支持 min 和 max 两个参数,分别表示最小长度和最大长度。
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
min |
int | 最小字符长度(含) | min: 2 |
max |
int | 最大字符长度(含) | max: 20 |
4.1.2 正则约束定义
正则约束通过 pattern 字段指定一个正则表达式,用于匹配 String 值的完整内容。正则表达式必须使用 ^ 和 $ 包裹,确保全值匹配。
// 契约 Schema 示例:String 类型约束
{
"key": "user:phone:{id}",
"type": "string",
"constraints": {
"length": {
"min": 11,
"max": 11
},
"pattern": "^1[3-9]\\d{9}$"
}
}
\d 在 JSON 中应写为 \\d。建议在定义前使用在线工具验证正则的正确性。
4.1.3 String 约束校验流程
下图展示了 String 类型在写入 Redis 前的完整校验流程:
4.2 Hash 类型约束:字段必填与类型
Hash 是 Redis 中用于存储对象类型的数据结构,其内部由多个 field-value 对组成。在实际业务中,Hash 的字段结构往往是固定的,例如用户信息必须包含 name、age、email 等字段。通过 Schema 定义字段的必填与类型约束,可以确保 Hash 数据的完整性。
4.2.1 字段必填约束
字段必填约束通过 required 数组指定,数组中的每个元素对应一个必须存在的 field 名称。当写入或更新 Hash 时,如果缺少任何一个必填字段,校验将拒绝操作。
// 契约 Schema 示例:Hash 字段必填约束
{
"key": "user:profile:{id}",
"type": "hash",
"fields": {
"name": {
"type": "string",
"required": true,
"constraints": {
"length": { "min": 2, "max": 50 }
}
},
"age": {
"type": "integer",
"required": true,
"constraints": {
"range": { "min": 0, "max": 150 }
}
},
"email": {
"type": "string",
"required": false,
"constraints": {
"pattern": "^[\\w.-]+@[\\w.-]+\\.\\w{2,4}$"
}
}
}
}
4.2.2 字段类型约束
每个字段可以独立定义其数据类型,支持的类型包括:string、integer、float、boolean、json 等。类型约束确保字段值符合预期的数据格式,避免出现“年龄存成字符串”或“邮箱存成数字”等数据污染问题。
| 类型 | 说明 | 校验规则 |
|---|---|---|
string |
字符串 | 值必须为字符串类型 |
integer |
整数 | 值必须为整数,支持负数 |
float |
浮点数 | 值必须为数字,支持小数 |
boolean |
布尔值 | 值必须为 true 或 false |
json |
JSON 对象 | 值必须为合法的 JSON 字符串 |
4.2.3 Hash 字段约束校验流程
下图展示了 Hash 类型在写入 Redis 前的字段校验流程:
4.3 实战案例:用户信息契约定义
结合 String 和 Hash 的约束规则,下面给出一个完整的用户信息契约定义示例:
// 用户信息契约定义
{
"version": "1.0",
"keys": [
{
"key": "user:phone:{id}",
"description": "用户手机号",
"type": "string",
"constraints": {
"length": { "min": 11, "max": 11 },
"pattern": "^1[3-9]\\d{9}$"
}
},
{
"key": "user:profile:{id}",
"description": "用户详细信息",
"type": "hash",
"fields": {
"name": {
"type": "string",
"required": true,
"constraints": {
"length": { "min": 2, "max": 20 },
"pattern": "^[\\u4e00-\\u9fa5a-zA-Z]+$"
}
},
"age": {
"type": "integer",
"required": true,
"constraints": {
"range": { "min": 0, "max": 150 }
}
},
"email": {
"type": "string",
"required": false,
"constraints": {
"pattern": "^[\\w.-]+@[\\w.-]+\\.\\w{2,4}$"
}
},
"vip_level": {
"type": "integer",
"required": false,
"constraints": {
"range": { "min": 0, "max": 5 }
}
}
}
}
]
}
4.4 常见错误与避坑指南
- 正则未使用 ^ 和 $:导致部分匹配通过,例如
\d{11}会匹配到 "abc12345678901def" 中的数字部分,应改为^\d{11}$。 - Hash 字段类型误判:Redis 中所有值都是字符串,契约校验层需要将字符串转换为目标类型后再校验。例如 "123" 可以转为 integer,但 "abc" 不能。
- 必填字段与默认值混淆:必填字段要求写入时必须提供,与字段是否有默认值无关。如果业务需要默认值,应在应用层处理,而非契约层。
- 长度单位混淆:长度约束基于字符数,而非字节数。对于包含中文的字符串,一个汉字算一个字符,但在 Redis 中实际占用 3 个字节(UTF-8 编码)。