|
| 1 | +# Redis 技术栈 |
| 2 | +- Author: [HuiFer](https://github.com/huifer) |
| 3 | +- Description: 该文章介绍redis相关技术栈,统筹规划学习路径 |
| 4 | + |
| 5 | +## 安装 |
| 6 | +### yum & apt |
| 7 | +- `apt-get install Redis-server` |
| 8 | +- `yum install Redis` |
| 9 | + |
| 10 | +### 编译 |
| 11 | +1. 下载 |
| 12 | +- `wget http://download.Redis.io/releases/Redis-5.0.5.tar.gz` |
| 13 | +2. 编译 |
| 14 | +``` |
| 15 | + tar -zxvf Redis-5.0.5.tar.gz |
| 16 | + cd Redis-5.0.5 |
| 17 | + make && make install |
| 18 | +``` |
| 19 | +3. 配置 |
| 20 | +- `vim Redis.conf` |
| 21 | +4. 启动 |
| 22 | +- Redis-server |
| 23 | +5. 关闭 |
| 24 | +- Redis-cli shutdown |
| 25 | + |
| 26 | +## key 命令 |
| 27 | +### keys |
| 28 | +- 搜索key根据匹配模式,例如: keys * |
| 29 | +### exists |
| 30 | +- 判断key是否存在,存在返回1,不存在返回0 |
| 31 | +### del |
| 32 | +- 删除key |
| 33 | +### type |
| 34 | +- 获取key类型 |
| 35 | +### randomkey |
| 36 | +- 随机返回key |
| 37 | +### expire |
| 38 | +- 设置key过期时间 |
| 39 | +### sortzh |
| 40 | +- 字符串排序 |
| 41 | +- 数字排序 |
| 42 | + |
| 43 | +## 服务端命令 |
| 44 | +### flushall |
| 45 | +- 强制清空所有key(目标对象所有数据库) |
| 46 | +### flushdb |
| 47 | +- 清空当前数据库 |
| 48 | +### client list |
| 49 | +- 连接信息列表 |
| 50 | +### client kill |
| 51 | +- 关闭ip:port客户端 |
| 52 | +### ping |
| 53 | +- 判断是否正常运行,正常运行返回pong |
| 54 | + |
| 55 | + |
| 56 | +## 数据类型(操作命令非完整) |
| 57 | +### string |
| 58 | +- 一个key最大存储512M的数据 |
| 59 | +#### 操作方法 |
| 60 | +- get /set(单key操作) |
| 61 | +- mget / mset(多key操作) |
| 62 | +- getrange 获取指定下标范围内的字符串 |
| 63 | +- setrange 设置指定下标范围内的字符串 |
| 64 | +- setex => set expire 组合 |
| 65 | +- incr 自增1 |
| 66 | +- incrby 自增指定数字 |
| 67 | +- decr 自减1 |
| 68 | +- decrby 自减指定数字 |
| 69 | +- append 追加字符串 |
| 70 | +- strlen 返回字符串长度 |
| 71 | + |
| 72 | +### list |
| 73 | +- blpop key 删除并获取第一个元素 |
| 74 | +- brpop key 删除并获取最后一个元素 |
| 75 | +- brpoplpush source destination 复制列表从source -> destination |
| 76 | +- lindex key index 根据key 和index 获取value |
| 77 | +- llen key 获取key的列表长度 |
| 78 | +- lpop key 从左侧出列 |
| 79 | +- lpush key value 从左侧入列 |
| 80 | +- lrange key start stop 根据start,stop获取key一定范围内的列表 |
| 81 | +- lset key index value 根据key + index 设置value |
| 82 | +- lrem key count value 删除列表元素 |
| 83 | +- rpush等不做赘述 |
| 84 | + |
| 85 | +### set |
| 86 | +- sadd key value 向key插入值 |
| 87 | +- scard key 获取集合成员数量 |
| 88 | +- sdiff key1 key2 返回集合差集 |
| 89 | +- sinter key1 key2 返回集合交集 |
| 90 | +- sunion key1 key2 返回集合并集 |
| 91 | +- sismember key value 判断集合中是否包含value |
| 92 | +- smembers key 返回集合 |
| 93 | +- srem key value 删除集合中的一个value |
| 94 | + |
| 95 | + |
| 96 | +### hash |
| 97 | +- hget key filed 获取hash中指定key->filed的值 |
| 98 | +- hgetall key 获取hash中指定key的所有制 |
| 99 | +- hexists key filed 查看是否存在filed |
| 100 | +- hdel key filed 删除指定key下的filed |
| 101 | +- hkeys key 获取所有filed |
| 102 | +- hset key filed value 设置key->filed->value |
| 103 | + |
| 104 | +### sortedSet |
| 105 | +- zadd key source member 向key追加source,member |
| 106 | +- zcard key 获取成员数量 |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +## 数据存储(持久化) |
| 111 | +### RDB |
| 112 | +> 将某个时间点的所有数据都以二进制形式存放到硬盘上 |
| 113 | + - 可以将快照复制到其它服务器从而创建具有相同数据的服务器副本. |
| 114 | + - 如果系统发生故障,将会丢失最后一次创建快照之后的数据. |
| 115 | + - 如果数据量很大,保存快照的时间会很长,建议异步写入. |
| 116 | + - 存在的问题:时间、性能开销大,不可控且容易丢失数据. |
| 117 | +- 同步机制 |
| 118 | + - save 命令,阻塞其他命令,直到save命令结束 |
| 119 | + - bgsave 命令,异步化,创建子线程进行持久化,不会阻塞其他命令 |
| 120 | +- 自动化触发 |
| 121 | + - 根据修改数量、时间进行命令执行 |
| 122 | + - save 900 1 # 900秒之内,对数据库进行了一次修改就执行 bgsave 命令 |
| 123 | + - save 300 10 # 300秒之内,对数据库进行了十次修改就执行 bgsave 命令 |
| 124 | + - save 60 10000 # 60秒之内,对数据库进行了一万次修改就执行 bgsav e命令 |
| 125 | + |
| 126 | +### AOF |
| 127 | +>将写命令添加到 AOF 文件(Append Only File)的末尾(MySQL Binlog、HBase HLog). |
| 128 | + |
| 129 | +- 随着服务器写请求的增多,AOF 文件会越来越大.Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令 |
| 130 | +- 使用 AOF 持久化需要设置同步选项,从而确保写命令什么时候会同步到磁盘文件上.这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘. |
| 131 | +#### 同步机制 |
| 132 | +- always 每个命令都同步 |
| 133 | +- eversec 每秒同步 |
| 134 | +- no 操作系统决定同步时间 |
| 135 | +#### AOF重写 |
| 136 | +- 对多条原生命令进行优化,重写成简化的命令以减少磁盘占用量、提高故障恢复效率. |
| 137 | +- 当 AOF 文件过大或增长速度过快时自动触发 |
| 138 | +- 配置 |
| 139 | + - auto-aof-rewrite-min-size:AOF 文件重写需要的大小 |
| 140 | + - auto-aof-rewrite-percentage:AOF 文件增长率 |
| 141 | + |
| 142 | + - aofcurrentsize:AOF 当前大小 |
| 143 | + - aof-base-size:AOF 上次启动和重写的大小 |
| 144 | +- 触发条件 |
| 145 | + - aof_current_size > auto-aof-rewrite-min-size |
| 146 | + - aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage |
| 147 | + |
| 148 | +## 集群 |
| 149 | +### 主从复制 |
| 150 | +#### 主从链(拓扑结构) |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | +#### 复制模式 |
| 156 | +- 全量复制:master 全部同步到 slave |
| 157 | +- 部分复制:slave 数据丢失进行备份 |
| 158 | + |
| 159 | +#### 问题点 |
| 160 | +- 同步故障 |
| 161 | + - 复制数据延迟(不一致) |
| 162 | + - 读取过期数据(Slave 不能删除数据) |
| 163 | + - 从节点故障 |
| 164 | + - 主节点故障 |
| 165 | +- 配置不一致 |
| 166 | + - maxmemory 不一致:丢失数据 |
| 167 | + - 优化参数不一致:内存不一致. |
| 168 | +- 避免全量复制 |
| 169 | + - 选择小主节点(分片)、低峰期间操作. |
| 170 | + - 如果节点运行 id 不匹配(如主节点重启、运行 id 发送变化),此时要执行全量复制,应该配合哨兵和集群解决. |
| 171 | + - 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数). |
| 172 | +- 复制风暴 |
| 173 | + |
| 174 | +### 哨兵机制 |
| 175 | +#### 拓扑图 |
| 176 | + |
| 177 | + |
| 178 | +#### 节点下线 |
| 179 | +- 客观下线 |
| 180 | + - 所有 Sentinel 节点对 Redis 节点失败要达成共识,即超过 quorum 个统一. |
| 181 | +- 主管下线 |
| 182 | + - 即 Sentinel 节点对 Redis 节点失败的偏见,超出超时时间认为 Master 已经宕机. |
| 183 | +#### leader选举 |
| 184 | +- 选举出一个 Sentinel 作为 Leader:集群中至少有三个 Sentinel 节点,但只有其中一个节点可完成故障转移.通过以下命令可以进行失败判定或领导者选举. |
| 185 | +- 选举流程 |
| 186 | + 1. 每个主观下线的 Sentinel 节点向其他 Sentinel 节点发送命令,要求设置它为领导者. |
| 187 | + 1. 收到命令的 Sentinel 节点如果没有同意通过其他 Sentinel 节点发送的命令,则同意该请求,否则拒绝. |
| 188 | + 1. 如果该 Sentinel 节点发现自己的票数已经超过 Sentinel 集合半数且超过 quorum,则它成为领导者. |
| 189 | + 1. 如果此过程有多个 Sentinel 节点成为领导者,则等待一段时间再重新进行选举. |
| 190 | +#### 故障转移 |
| 191 | +- 转移流程 |
| 192 | + 1. Sentinel 选出一个合适的 Slave 作为新的 Master(slaveof no one 命令). |
| 193 | + 1. 向其余 Slave 发出通知,让它们成为新 Master 的 Slave( parallel-syncs 参数). |
| 194 | + 1. 等待旧 Master 复活,并使之称为新 Master 的 Slave. |
| 195 | + 1. 向客户端通知 Master 变化. |
| 196 | +- 从 Slave 中选择新 Master 节点的规则(slave 升级成 master 之后) |
| 197 | + 1. 选择 slave-priority 最高的节点. |
| 198 | + 1. 选择复制偏移量最大的节点(同步数据最多). |
| 199 | + 1. 选择 runId 最小的节点. |
| 200 | +#### 读写分离 |
| 201 | +#### 定时任务 |
| 202 | +- 每 1s 每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping,进行心跳检测. |
| 203 | +- 每 2s 每个 Sentinel 通过 Master 的 Channel 交换信息(pub - sub). |
| 204 | +- 每 10s 每个 Sentinel 对 Master 和 Slave 执行 info,目的是发现 Slave 节点、确定主从关系. |
| 205 | + |
| 206 | +### 分布式集群(Cluster) |
| 207 | +#### 拓扑图 |
| 208 | + |
| 209 | + |
| 210 | + |
| 211 | +#### 通讯 |
| 212 | +##### 集中式 |
| 213 | +> 将集群元数据(节点信息、故障等等)几种存储在某个节点上. |
| 214 | +- 优势 |
| 215 | + 1. 元数据的更新读取具有很强的时效性,元数据修改立即更新 |
| 216 | +- 劣势 |
| 217 | + 1. 数据集中存储 |
| 218 | +##### Gossip |
| 219 | + |
| 220 | + |
| 221 | +- [Gossip 协议](https://www.jianshu.com/p/8279d6fd65bb) |
| 222 | + |
| 223 | +#### 寻址分片 |
| 224 | +##### hash取模 |
| 225 | +- hash(key)%机器数量 |
| 226 | +- 问题 |
| 227 | + 1. 机器宕机,造成数据丢失,数据读取失败 |
| 228 | + 1. 伸缩性 |
| 229 | +##### 一致性hash |
| 230 | +-  |
| 231 | + |
| 232 | +- 问题 |
| 233 | + 1. 一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。 |
| 234 | + - 解决方案 |
| 235 | + - 可以通过引入虚拟节点机制解决:即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。 |
| 236 | +##### hash槽 |
| 237 | +- CRC16(key)%16384 |
| 238 | +- |
| 239 | + |
| 240 | + |
| 241 | + |
| 242 | + |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | +## 使用场景 |
| 247 | +### 热点数据 |
| 248 | +### 会话维持 session |
| 249 | +### 分布式锁 SETNX |
| 250 | +### 表缓存 |
| 251 | +### 消息队列 list |
| 252 | +### 计数器 string |
| 253 | + |
| 254 | + |
| 255 | + |
| 256 | + |
| 257 | + |
| 258 | +## 缓存设计 |
| 259 | +### 更新策略 |
| 260 | +- LRU、LFU、FIFO 算法自动清除:一致性最差,维护成本低. |
| 261 | +- 超时自动清除(key expire):一致性较差,维护成本低. |
| 262 | +- 主动更新:代码层面控制生命周期,一致性最好,维护成本高. |
| 263 | +### 更新一致性 |
| 264 | +- 读请求:先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应. |
| 265 | +- 写请求:先删除缓存,然后再更新数据库(避免大量地写、却又不经常读的数据导致缓存频繁更新). |
| 266 | +### 缓存粒度 |
| 267 | +- 通用性:全量属性更好. |
| 268 | +- 占用空间:部分属性更好. |
| 269 | +- 代码维护成本. |
| 270 | + |
| 271 | +### 缓存穿透 |
| 272 | +> 当大量的请求无命中缓存、直接请求到后端数据库(业务代码的 bug、或恶意攻击),同时后端数据库也没有查询到相应的记录、无法添加缓存. |
| 273 | + 这种状态会一直维持,流量一直打到存储层上,无法利用缓存、还会给存储层带来巨大压力. |
| 274 | +> |
| 275 | +#### 解决方案 |
| 276 | +1. 请求无法命中缓存、同时数据库记录为空时在缓存添加该 key 的空对象(设置过期时间),缺点是可能会在缓存中添加大量的空值键(比如遭到恶意攻击或爬虫),而且缓存层和存储层数据短期内不一致; |
| 277 | +1. 使用布隆过滤器在缓存层前拦截非法请求、自动为空值添加黑名单(同时可能要为误判的记录添加白名单).但需要考虑布隆过滤器的维护(离线生成/ 实时生成). |
| 278 | +### 缓存雪崩 |
| 279 | +> 缓存崩溃时请求会直接落到数据库上,很可能由于无法承受大量的并发请求而崩溃,此时如果只重启数据库,或因为缓存重启后没有数据,新的流量进来很快又会把数据库击倒 |
| 280 | +> |
| 281 | +#### 出现后应对 |
| 282 | +- 事前:Redis 高可用,主从 + 哨兵,Redis Cluster,避免全盘崩溃. |
| 283 | +- 事中:本地 ehcache 缓存 + hystrix 限流 & 降级,避免数据库承受太多压力. |
| 284 | +- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据. |
| 285 | +#### 请求过程 |
| 286 | +1. 用户请求先访问本地缓存,无命中后再访问 Redis,如果本地缓存和 Redis 都没有再查数据库,并把数据添加到本地缓存和 Redis; |
| 287 | +1. 由于设置了限流,一段时间范围内超出的请求走降级处理(返回默认值,或给出友情提示). |
| 288 | + |
| 289 | + |
| 290 | +## 事件型驱动 |
| 291 | +### 文件 |
| 292 | +### 时间 |
| 293 | +### 调度&执行 |
| 294 | + |
0 commit comments