今日内容

  1. 1. redis
  2. 1. 概念
  3. 2. 下载安装
  4. 3. 命令操作
  5. 1. 数据结构
  6. 4. 持久化操作
  7. 5. 使用Java客户端操作redis

Redis

  1. 1. 概念: redis是一款高性能的NOSQL系列的非关系型数据库
  2. 1.1.什么是NOSQL
  3. NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
  4. 随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。
  5. 1.1.1. NOSQL和关系型数据库比较
  6. 优点:
  7. 1)成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
  8. 2)查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
  9. 3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
  10. 4)扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
  11. 缺点:
  12. 1)维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
  13. 2)不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
  14. 3)不提供关系型数据库对事务的处理。
  15. 1.1.2. 非关系型数据库的优势:
  16. 1)性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
  17. 2)可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
  18. 1.1.3. 关系型数据库的优势:
  19. 1)复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
  20. 2)事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然。
  21. 1.1.4. 总结
  22. 关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库,
  23. NoSQL数据库对关系型数据库的不足进行弥补。
  24. 一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据
  25. 1.2.主流的NOSQL产品
  26. 键值(Key-Value)存储数据库
  27. 相关产品: Tokyo Cabinet/TyrantRedisVoldemortBerkeley DB
  28. 典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
  29. 数据模型: 一系列键值对
  30. 优势: 快速查询
  31. 劣势: 存储的数据缺少结构化
  32. 列存储数据库
  33. 相关产品:Cassandra, HBase, Riak
  34. 典型应用:分布式的文件系统
  35. 数据模型:以列簇式存储,将同一列数据存在一起
  36. 优势:查找速度快,可扩展性强,更容易进行分布式扩展
  37. 劣势:功能相对局限
  38. 文档型数据库
  39. 相关产品:CouchDBMongoDB
  40. 典型应用:Web应用(与Key-Value类似,Value是结构化的)
  41. 数据模型: 一系列键值对
  42. 优势:数据结构要求不严格
  43. 劣势: 查询性能不高,而且缺乏统一的查询语法
  44. 图形(Graph)数据库
  45. 相关数据库:Neo4JInfoGridInfinite Graph
  46. 典型应用:社交网络
  47. 数据模型:图结构
  48. 优势:利用图结构相关算法。
  49. 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
  50. 1.3 什么是Redis
  51. Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
  52. 1) 字符串类型 string
  53. 2) 哈希类型 hash
  54. 3) 列表类型 list
  55. 4) 集合类型 set
  56. 5) 有序集合类型 sortedset
  57. 1.3.1 redis的应用场景
  58. 缓存(数据查询、短连接、新闻内容、商品内容等等)
  59. 聊天室的在线好友列表
  60. 任务队列。(秒杀、抢购、12306等等)
  61. 应用排行榜
  62. 网站访问统计
  63. 数据过期处理(可以精确到毫秒
  64. 分布式集群架构中的session分离
  65. 2. 下载安装
  66. 1. 官网:https://redis.io
  67. 2. 中文网:http://www.redis.net.cn/
  68. 3. 解压直接可以使用:
  69. * redis.windows.conf:配置文件
  70. * redis-cli.exeredis的客户端
  71. * redis-server.exeredis服务器端
  72. 3. 命令操作
  73. 1. redis的数据结构:
  74. * redis存储的是:key,value格式的数据,其中key都是字符串,value5种不同的数据结构
  75. * value的数据结构:
  76. 1) 字符串类型 string
  77. 2) 哈希类型 hash map格式
  78. 3) 列表类型 list linkedlist格式。支持重复元素
  79. 4) 集合类型 set 不允许重复元素
  80. 5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
  81. 2. 字符串类型 string
  82. 1. 存储: set key value
  83. 127.0.0.1:6379> set username zhangsan
  84. OK
  85. 2. 获取: get key
  86. 127.0.0.1:6379> get username
  87. "zhangsan"
  88. 3. 删除: del key
  89. 127.0.0.1:6379> del age
  90. (integer) 1
  91. 3. 哈希类型 hash
  92. 1. 存储: hset key field value
  93. 127.0.0.1:6379> hset myhash username lisi
  94. (integer) 1
  95. 127.0.0.1:6379> hset myhash password 123
  96. (integer) 1
  97. 2. 获取:
  98. * hget key field: 获取指定的field对应的值
  99. 127.0.0.1:6379> hget myhash username
  100. "lisi"
  101. * hgetall key:获取所有的fieldvalue
  102. 127.0.0.1:6379> hgetall myhash
  103. 1) "username"
  104. 2) "lisi"
  105. 3) "password"
  106. 4) "123"
  107. 3. 删除 hash field hdel key field
  108. 4. 删除 hash key: del key
  109. 4. 列表类型 list:可以添加一个元素到列表的头部(左边)或者尾部(右边)
  110. 1. 添加:
  111. 1. lpush key value: 将元素加入列表左表
  112. 2. rpush key value:将元素加入列表右边
  113. 127.0.0.1:6379> lpush myList a
  114. (integer) 1
  115. 127.0.0.1:6379> lpush myList b
  116. (integer) 2
  117. 127.0.0.1:6379> rpush myList c
  118. (integer) 3
  119. 2. 获取:
  120. * lrange key start end :范围获取
  121. 127.0.0.1:6379> lrange myList 0 -1 // 0 -1 默认就是回去list 中所有数据
  122. 1) "b"
  123. 2) "a"
  124. 3) "c"
  125. 3. 删除:
  126. * lpop key 删除列表最左边的元素,并将元素返回
  127. * rpop key 删除列表最右边的元素,并将元素返回
  128. 5. 集合类型 set 不允许重复元素
  129. 1. 存储:sadd key value
  130. 127.0.0.1:6379> sadd myset a
  131. (integer) 1
  132. 127.0.0.1:6379> sadd myset a
  133. (integer) 0
  134. 2. 获取:smembers key:获取set集合中所有元素
  135. 127.0.0.1:6379> smembers myset
  136. 1) "a"
  137. 3. 删除:srem key value:删除set集合中的某个元素
  138. 127.0.0.1:6379> srem myset a
  139. (integer) 1
  140. 6. 有序集合类型 sortedset:不允许重复元素,且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
  141. 企业级:
  142. 1. 存储:zadd key score(double) value
  143. 127.0.0.1:6379> zadd mysort 60 zhangsan
  144. (integer) 1
  145. 127.0.0.1:6379> zadd mysort 50 lisi
  146. (integer) 1
  147. 127.0.0.1:6379> zadd mysort 80 wangwu
  148. (integer) 1
  149. 2. 获取:zrange key start end [withscores]
  150. 127.0.0.1:6379> zrange mysort 0 -1 //0 -1 固定写法获取set 中所有数据
  151. 1) "lisi"
  152. 2) "zhangsan"
  153. 3) "wangwu"
  154. 127.0.0.1:6379> zrange mysort 0 -1 withscores //0 -1 固定写法,得到所以数据+权重(score)
  155. 1) "zhangsan"
  156. 2) "60"
  157. 3) "wangwu"
  158. 4) "80"
  159. 5) "lisi"
  160. 6) "500"
  161. 3. 删除:zrem key value
  162. 127.0.0.1:6379> zrem mysort lisi
  163. (integer) 1
  164. 7. 通用命令
  165. 1. keys * : 查询所有的键
  166. 2. type key 获取键对应的value的类型
  167. 3. del key:删除指定的key value
  168. 4. expire key 20 : key 设置 过期时间, 单位秒
  169. 4. 持久化
  170. 1. redis是一个内存数据库,当redis服务器重启,获取电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
  171. 2. redis持久化机制:
  172. 1. RDB:默认方式,不需要进行配置,默认就使用这种机制
  173. * 在一定的间隔时间中,检测key的变化情况,然后持久化数据
  174. 1. 编辑redis.windwos.conf文件
  175. # after 900 sec (15 min) if at least 1 key changed
  176. save 900 1
  177. # after 300 sec (5 min) if at least 10 keys changed
  178. save 300 10
  179. # after 60 sec if at least 10000 keys changed
  180. save 60 10000
  181. 2. 重新启动redis服务器,并指定配置文件名称
  182. D:\JavaWeb2018\day23_redis\资\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf
  183. 2. AOF:日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据
  184. 1. 编辑redis.windwos.conf文件
  185. appendonly no(关闭aof --> appendonly yes (开启aof
  186. # appendfsync always : 每一次操作都进行持久化
  187. appendfsync everysec 每隔一秒进行一次持久化
  188. # appendfsync no : 不进行持久化
  189. 5. Java客户端 Jedis
  190. * Jedis: 一款java操作redis数据库的工具.
  191. * 使用步骤:
  192. 1. 下载jedisjar
  193. 2. 使用
  194. //1. 获取连接
  195. Jedis jedis = new Jedis("localhost",6379);
  196. //2. 操作
  197. jedis.set("username","zhangsan");
  198. //3. 关闭连接
  199. jedis.close();
  200. * Jedis操作各种redis中的数据结构
  201. 1) 字符串类型 string
  202. set
  203. get
  204. @Test
  205. public void test2(){
  206. //1. 获取连接
  207. Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
  208. //2. 操作
  209. //存储
  210. jedis.set("username","zhangsan");
  211. jedis.expire("username", 10); // 给 string key username 设置10秒的过期时间
  212. //获取
  213. String username = jedis.get("username");
  214. System.out.println(username);
  215. //可以使用setex()方法存储可以指定过期时间的 key value
  216. jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对
  217. jedis.del("activecode");
  218. //3. 关闭连接
  219. jedis.close();
  220. }
  221. * jedis连接池: JedisPool
  222. * 使用:
  223. 1. 创建JedisPool连接池对象
  224. 2. 调用方法 getResource()方法获取Jedis连接
  225. //0.创建一个配置对象
  226. JedisPoolConfig config = new JedisPoolConfig();
  227. config.setMaxTotal(50);
  228. config.setMaxIdle(10);
  229. //1.创建Jedis连接池对象
  230. JedisPool jedisPool = new JedisPool(config,"localhost",6379);
  231. //2.获取连接
  232. Jedis jedis = jedisPool.getResource();
  233. //3. 使用
  234. jedis.set("hehe","heihei");
  235. //4. 关闭 归还到连接池中
  236. jedis.close();
  1. 2) 哈希类型 hash map格式
  2. hset
  3. hget
  4. hgetAll
  5. //1. 获取连接
  6. Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
  7. //2. 操作
  8. // 存储hash
  9. jedis.hset("user","name","lisi");
  10. jedis.hset("user","age","23");
  11. jedis.hset("user","gender","female");
  12. // 获取hash
  13. String name = jedis.hget("user", "name");
  14. System.out.println(name);
  15. // 获取hash的所有map中的数据
  16. Map<String, String> user = jedis.hgetAll("user");
  17. // keyset
  18. Set keySet = user.keySet();
  19. for (String key : keySet) {
  20. //获取value
  21. String value = user.get(key);
  22. System.out.println(key + ":" + value);
  23. }
  24. //3. 关闭连接
  25. jedis.close();
  1. 3) 列表类型 list linkedlist格式。支持重复元素
  2. lpush / rpush
  3. lpop / rpop
  4. lrange start end : 范围获取
  5. //1. 获取连接
  6. Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
  7. //2. 操作
  8. // list 存储
  9. jedis.lpush("mylist","a","b","c");//从左边存
  10. jedis.rpush("mylist","a","b","c");//从右边存
  11. // list 范围获取
  12. List<String> mylist = jedis.lrange("mylist", 0, -1);
  13. System.out.println(mylist);
  14. // list 弹出
  15. String element1 = jedis.lpop("mylist");//c
  16. System.out.println(element1);
  17. String element2 = jedis.rpop("mylist");//c
  18. System.out.println(element2);
  19. // list 范围获取
  20. List<String> mylist2 = jedis.lrange("mylist", 0, -1);
  21. System.out.println(mylist2);
  22. //3. 关闭连接
  23. jedis.close();
  24. 4) 集合类型 set 不允许重复元素
  25. sadd
  26. smembers:获取所有元素
  27. //1. 获取连接
  28. Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
  29. //2. 操作
  30. // set 存储
  31. jedis.sadd("myset","java","php","c++");
  32. // set 获取
  33. Set myset = jedis.smembers("myset");
  34. System.out.println(myset);
  35. //3. 关闭连接
  36. jedis.close();
  1. 5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
  2. zadd
  3. zrange
  4. //1. 获取连接
  5. Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
  6. //2. 操作
  7. // sortedset 存储
  8. jedis.zadd("mysortedset",3,"亚瑟");
  9. jedis.zadd("mysortedset",30,"后裔");
  10. jedis.zadd("mysortedset",55,"孙悟空");
  11. // sortedset 获取
  12. Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);
  13. System.out.println(mysortedset);
  14. //3. 关闭连接
  15. jedis.close();
  1. * jedis连接池: JedisPool
  2. * 使用:
  3. 1. 创建JedisPool连接池对象
  4. 2. 调用方法 getResource()方法获取Jedis连接
  5. //0.创建一个配置对象
  6. JedisPoolConfig config = new JedisPoolConfig();
  7. config.setMaxTotal(50);
  8. config.setMaxIdle(10);
  9. //1.创建Jedis连接池对象
  10. JedisPool jedisPool = new JedisPool(config,"localhost",6379);
  11. //2.获取连接
  12. Jedis jedis = jedisPool.getResource();
  13. //3. 使用
  14. jedis.set("hehe","heihei");
  15. //4. 关闭 归还到连接池中
  16. jedis.close();
  17. * 连接池工具类
  18. public class JedisPoolUtils {
  19. private static JedisPool jedisPool;
  20. static{
  21. //读取配置文件
  22. InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
  23. //创建Properties对象
  24. Properties pro = new Properties();
  25. //关联文件
  26. try {
  27. pro.load(is);
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. //获取数据,设置到JedisPoolConfig中
  32. JedisPoolConfig config = new JedisPoolConfig();
  33. config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
  34. config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
  35. //初始化JedisPool
  36. jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port")));
  37. }
  38. /**
  39. * 获取连接方法
  40. */
  41. public static Jedis getJedis(){
  42. return jedisPool.getResource();
  43. }
  44. }

案例

  1. 案例需求:
  2. 1. 提供index.html页面,页面中有一个省份 下拉列表
  3. 2. 页面加载完成后 发送ajax请求,加载所有省份
  4. * 注意:使用redis缓存一些不经常发生变化的数据。
  5. * 数据库的数据一旦发生改变,则需要更新缓存。
  6. * 数据库的表执行 增删改的相关操作,需要将redis缓存数据情况,再次存入
  7. * service对应的增删改方法中,将redis数据删除。

redis五种数据结构的实现

redis对象

redis中并没有直接使用以上所说的各种数据结构来实现键值数据库,而是基于一种对象,对象底层再间接的引用上文所说的具体的数据结构。
结构如下图:
redis笔记 - 图1

字符串

redis笔记 - 图2
其中:embstr和raw都是由SDS动态字符串构成的。唯一区别是:raw是分配内存的时候,redisobject和 sds 各分配一块内存,而embstr是redisobject和raw在一块儿内存中。

列表

redis笔记 - 图3

hash

redis笔记 - 图4

set

redis笔记 - 图5

zset

redis笔记 - 图6

Redis持久化

1、简介

什么是持久化?

利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。

为什么要持久化

防止数据的意外丢失,确保数据安全性

持久化过程保存什么

  • 将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据
  • 将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程

redis笔记 - 图7

2、RDB

RDB启动方式——save

  • 命令

    1. save
  • 作用
    手动执行一次保存操作

    RDB配置相关命令

  • dbfilename dump.rdb

    • 说明:设置本地数据库文件名,默认值为 dump.rdb
    • 经验:通常设置为dump-端口号.rdb
  • dir
    • 说明:设置存储.rdb文件的路径
    • 经验:通常设置成存储空间较大的目录中,目录名称data
  • rdbcompression yes
    • 说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF 压缩
    • 经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)
  • rdbchecksum yes
    • 说明:设置是否进行RDB文件格式校验,该校验过程在写文件和读文件过程均进行
    • 经验:通常默认为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险

RDB启动方式——save指令工作原理

redis笔记 - 图8
注意save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用

RDB启动方式——bgsave

  • 命令

    1. bgsaveCopy
  • 作用
    手动启动后台保存操作,但不是立即执行

**

RDB启动方式 —— bgsave指令工作原理

redis笔记 - 图9
注意bgsave命令是针对save阻塞问题做的优化。Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用,推荐使用bgsave
bgsave的保存操作可以通过redis的日志查看

  1. docker logs myredis

RDB启动方式 ——save配置

  • 配置

    1. save second changes
  • 作用
    满足限定时间范围内key的变化数量达到指定数量即进行持久化

  • 参数
    • second:监控时间范围
    • changes:监控key的变化量
  • 配置位置
    conf文件中进行配置

    RDB启动方式 ——save配置原理

    redis笔记 - 图10
    注意

  • save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的

  • save配置中对于second与changes设置通常具有互补对应关系(一个大一个小),尽量不要设置成包含性关系
  • save配置启动后执行的是bgsave操作

    RDB启动方式对比

    redis笔记 - 图11

    RDB优缺点

  • 优点

    • RDB是一个紧凑压缩的二进制文件,存储效率较高
    • RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景
    • RDB恢复数据的速度要比AOF很多
    • 应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复
  • 缺点

    • RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
    • bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
    • Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象

      3、AOF

      AOF概念

  • AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令,以达到恢复数据的目的。与RDB相比可以简单描述为改记录数据为记录数据产生的过程

  • AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式

    AOF写数据过程

    redis笔记 - 图12

    AOF写数据三种策略(appendfsync)

  • always

    • 每次写入操作均同步到AOF文件中,数据零误差,性能较低,不建议使用
  • everysec
    • 每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高建议使用,也是默认配置
    • 在系统突然宕机的情况下丢失1秒内的数据
  • no

    • 由操作系统控制每次同步到AOF文件的周期,整体过程不可控

      AOF功能开启

  • 配置

    1. appendonly yes|no
    • 作用
      • 是否开启AOF持久化功能,默认为不开启状态
  • 配置

    1. appendfsync always|everysec|no
    • 作用
      • AOF写数据策略

        AOF重写

        作用
  • 降低磁盘占用量,提高磁盘利用率

  • 提高持久化效率,降低持久化写时间,提高IO性能
  • 降低数据恢复用时,提高数据恢复效率

    规则
  • 进程内已超时的数据不再写入文件

  • 忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令
    • 如del key1、 hdel key2、srem key3、set key4 111、set key4 222等
  • 对同一数据的多条写命令合并为一条命令

    • 如lpush list1 a、lpush list1 b、 lpush list1 c 可以转化为:lpush list1 a b c
    • 为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素
      如何使用
  • 手动重写

    1. bgrewriteaof
  • 自动重写

    1. auto-aof-rewrite-min-size size
    2. auto-aof-rewrite-percentage percentage

    工作原理

    redis笔记 - 图13

    AOF自动重写
  • 自动重写触发条件设置

    1. //触发重写的最小大小
    2. auto-aof-rewrite-min-size size
    3. //触发重写须达到的最小百分比
    4. auto-aof-rewrite-percentage percent
  • 自动重写触发比对参数( 运行指令info Persistence获取具体信息 )

    1. //当前.aof的文件大小
    2. aof_current_size
    3. //基础文件大小
    4. aof_base_size
  • 自动重写触发条件
    redis笔记 - 图14

    工作原理

    redis笔记 - 图15
    redis笔记 - 图16
    redis笔记 - 图17

    缓冲策略

    AOF缓冲区同步文件策略,由参数appendfsync控制

  • write操作会触发延迟写(delayed write)机制,Linux在内核提供页缓冲区用 来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步硬盘操作依 赖于系统调度机制,列如:缓冲区页空间写满或达到特定时间周期。同步文件之 前,如果此时系统故障宕机,缓冲区内数据将丢失。

  • fsync针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞知道 写入硬盘完成后返回,保证了数据持久化。

    4、RDB VS AOF

    redis笔记 - 图18

    RDB与AOF的选择之惑
  • 对数据非常敏感,建议使用默认的AOF持久化方案

    • AOF持久化策略使用everysecond,每秒钟fsync一次。该策略redis仍可以保持很好的处理性能,当出现问题时,最多丢失0-1秒内的数据。
    • 注意:由于AOF文件存储体积较大,且恢复速度较慢
  • 数据呈现阶段有效性,建议使用RDB持久化方案
    • 数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且恢复速度较快,阶段 点数据恢复通常采用RDB方案
    • 注意:利用RDB实现紧凑的数据持久化会使Redis降的很低
  • 综合比对

    • RDB与AOF的选择实际上是在做一种权衡,每种都有利有弊
    • 如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF
    • 如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB
    • 灾难恢复选用RDB
    • 双保险策略,同时开启 RDB 和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据

      五、Redis事务

      1、Redis事务的定义

      redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列)。当执行时,一次性按照添加顺序依次执行,中间不会被打断或者干扰

      2、事务的基本操作

  • 开启事务

    1. multi
    • 作用
      • 作设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中
  • 取消事务

    1. discard
    • 作用
      • 终止当前事务的定义,发生在multi之后,exec之前
  • 执行事务

    1. exec
    • 作用
      • 设定事务的结束位置,同时执行事务。与multi成对出现,成对使用

        3、事务操作的基本流程

        redis笔记 - 图19

        4、事务操作的注意事项

        定义事务的过程中,命令格式输入错误怎么办?
  • 语法错误

    • 指命令书写格式有误 例如执行了一条不存在的指令
  • 处理结果
    • 如果定义的事务中所包含的命令存在语法错误,整体事务中所有命令均不会执行。包括那些语法正确的命令

定义事务的过程中,命令执行出现错误怎么办?

  • 运行错误
    • 指命令格式正确,但是无法正确的执行。例如对list进行incr操作
  • 处理结果
    • 能够正确运行的命令会执行,运行错误的命令不会被执行

注意:已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚。

5、基于特定条件的事务执行

  • 对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行

    1. watch key1, key2....
  • 取消对所有key的监视

    1. unwatch

    分布式锁

  • 使用 setnx 设置一个公共锁

    1. //上锁
    2. setnx lock-key value
    3. //释放锁
    4. del lock-key
    • 利用setnx命令的返回值特征,有值(被上锁了)则返回设置失败,无值(没被上锁)则返回设置成功
    • 操作完毕通过del操作释放锁

注意:上述解决方案是一种设计概念,依赖规范保障,具有风险性

分布式锁加强

  • 使用 expire 为锁key添加时间限定,到时不释放,放弃锁

    1. expire lock-key seconds
    2. pexpire lock-key milliseconds
  • 由于操作通常都是微秒或毫秒级,因此该锁定时间不宜设置过大。具体时间需要业务测试后确认。

    • 例如:持有锁的操作最长执行时间127ms,最短执行时间7ms。
    • 测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时
    • 锁时间设定推荐:最大耗时_120%+平均网络延迟_110%
    • 如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可

      六、删除策略

      设置定期时间

      EXPIRE key ttl 用于将键key的生存时间设置为ttl秒
      PEXPIRE key ttl 用于将键key的生存时间设置为ttl毫秒
      EXPIREAT key timestamp 用于将键key的过期时间设置为timestamp所指定的秒数时间戳
      PEXPIREAT key timestamp 用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳
      EXPIRE,PEXPIRE和EXPIREAT都是将指令转换成PEXPIREAT命令实现的
      unix时间戳:是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒
      image.png

      1、数据删除策略

  • 定时删除

  • 惰性删除
  • 定期删除

    时效性数据的存储结构

  • Redis中的数据,在expire中以哈希的方式保存在其中。其value是数据在内存中的地址,filed是对应的生命周期

redis笔记 - 图21

数据删除策略的目标

在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或内存泄露

2、三种删除策略

定时删除

  • 创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
  • 优点:节约内存,到时就删除,快速释放掉不必要的内存占用
  • 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量
  • 总结:用处理器性能换取存储空间 (拿时间换空间

    惰性删除

  • 数据到达过期时间,不做处理。等下次访问该数据时

    • 如果未过期,返回数据
    • 发现已过期,删除,返回不存在
  • 优点:节约CPU性能,发现必须删除的时候才删除
  • 缺点:内存压力很大,出现长期占用内存的数据
  • 总结:用存储空间换取处理器性能 (拿空间换时间)

    定期删除

    redis笔记 - 图22

  • 周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

  • 特点1:CPU性能占用设置有峰值,检测频度可自定义设置
  • 特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
  • 总结:周期性抽查存储空间 (随机抽查,重点抽查),Redis使用定期删除和惰性删除策略

    3、逐出算法

    _当新数据进入redis时,如果内存不足怎么办? _

  • Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法

  • 注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。

    影响数据逐出的相关配置

  • 最大可使用内存

    1. maxmemory
  • 占用物理内存的比例,默认值为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上。

  • 每次选取待删除数据的个数

    1. maxmemory-samples
  • 选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据

  • 删除策略

    1. maxmemory-policy
  • 达到最大内存后的,对被挑选出来的数据进行删除的策略

    影响数据逐出的相关配置

    redis笔记 - 图23
    LRU:最长时间没被使用的数据
    LFU:一段时间内使用次数最少的数据

    数据逐出策略配置依据

  • 使用INFO命令输出监控信息,查询缓存 hit 和 miss 的次数,根据业务需求调优Redis配置

redis笔记 - 图24

七、高级数据类型

1、Bitmaps

用于信息的状态统计

基础操作

  • 获取指定key对应偏移量上的bit值

    1. getbit key offset
  • 设置指定key对应偏移量上的bit值,value只能是1或0

    1. setbit key offset value

    扩展操作

  • 对指定key按位进行交、并、非、异或操作,并将结果保存到destKey

    1. bitop op destKey key1 [key2...]
    • and:交
    • or:并
    • not:非
    • xor:异或
  • 统计指定key中1的数量

    1. bitcount key [start end]

    2、HyperLogLog

    统计独立UV

    基数

  • 基数是数据集去重后元素个数

  • HyperLogLog 是用来做基数统计的,运用了LogLog的算法

redis笔记 - 图25

基本操作

  • 添加数据

    1. pfadd key element1, element2...
  • 统计数据

    1. pfcount key1 key2....
  • 合并数据

    1. pfmerge destkey sourcekey [sourcekey...]

    相关说明

  • 用于进行基数统计,不是集合,不保存数据,只记录数量而不是具体数据

  • 核心是基数估算算法,最终数值存在一定误差
  • 误差范围:基数估计的结果是一个带有 0.81% 标准错误的近似值
  • 耗空间极小,每个hyperloglog key占用了12K的内存用于标记基数
  • pfadd命令不是一次性分配12K内存使用,会随着基数的增加内存逐渐增大
  • Pfmerge命令合并后占用的存储空间为12K,无论合并之前数据量多少

    3、GEO

    根据经纬度求值

    基本操作

  • 添加坐标点

    1. geoadd key longitude latitude member [longitude latitude member ...]
    2. georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]
  • 获取坐标点

    1. geopos key member [member ...]
    2. georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]
  • 计算坐标点距离

    1. geodist key member1 member2 [unit]
    2. geohash key member [member ...]

    八、主从复制

    1、简介

    多台服务器连接方案

    redis笔记 - 图26

  • 提供数据方:master

    • 主服务器,主节点,主库
    • 主客户端
  • 接收数据的方:slave
    • 从服务器,从节点,从库
    • 从客户端
  • 需要解决的问题
    • 数据同步
  • 核心工作

    • master的数据复制到slave中

      主从复制

      主从复制即将master中的数据即时、有效的复制到slave中
      特征:一个master可以拥有多个slave,一个slave只对应一个master
      职责:
  • master:

    • 写数据
    • 执行写操作时,将出现变化的数据自动同步到slave
    • 读数据(可忽略)
  • slave:

    • 读数据
    • 写数据(禁止

      2、作用

  • 读写分离:master写、slave读,提高服务器的读写负载能力

  • 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
  • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案

    3、工作流程

    总述

  • 主从复制过程大体可以分为3个阶段

    • 建立连接阶段(即准备阶段)
    • 数据同步阶段
    • 命令传播阶段

redis笔记 - 图27

阶段一:建立连接

  • 建立slave到master的连接,使master能够识别slave,并保存slave端口号
    redis笔记 - 图28

_主从连接(slave连接master) _

  • 方式一:客户端发送命令

    1. slaveof <masterip> <masterport>
  • 方式二:启动服务器参数

    1. redis-server -slaveof <masterip> <masterport>
  • 方式三:服务器配置 (常用)

从服务器配置文件

  1. slaveof <masterip> <masterport>

主从断开连接

  • 客户端发送命令

    1. slaveof no oneCopy
    • 说明: slave断开连接后,不会删除已有数据,只是不再接受master发送的数据

授权访问

  • master客户端发送命令设置密码

    1. requirepass <password>
  • master配置文件设置密码

    1. config set requirepass <password>
    2. config get requirepass
  • slave客户端发送命令设置密码

    1. auth <password>
  • slave配置文件设置密码

    1. masterauth <password>
  • slave启动服务器设置密码

    1. redis-server a <password>

    阶段二:数据同步阶段

    redis笔记 - 图29

  • 全量复制

    • 将master执行bgsave之前,master中所有的数据同步到slave中
  • 部分复制(增量复制)
    • 将master执行bgsave操作中,新加入的数据(复制缓冲区中的数据)传给slave,slave通过bgrewriteaof指令来恢复数据
      数据同步阶段master说明
  1. 如果master数据量巨大,数据同步阶段应避开流量高峰期避免造成master阻塞,影响业务正常执行
  2. 复制缓冲区大小设定不合理,会导致数据溢出。如进行全量复制周期太长,进行部分复制时发现数据已经存在丢失的情况,必须进行第二次全量复制,致使slave陷入死循环状态。

    1. repl-backlog-size 1mbCopy
  3. master单机内存占用主机内存的比例不应过大,建议使用50%-70%的内存,留下30%-50%的内存用于执 行bgsave命令和创建复制缓冲区

    数据同步阶段slave说明
  4. 为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,建议关闭此期间的对外服务

    1. slave-serve-stale-data yes|noCopy
  5. 数据同步阶段,master发送给slave信息可以理解master是slave的一个客户端,主动向slave发送命令

  6. 多个slave同时对master请求数据同步,master发送的RDB文件增多,会对带宽造成巨大冲击,如果master带宽不足,因此数据同步需要根据业务需求,适量错峰
  7. slave过多时,建议调整拓扑结构,由一主多从结构变为树状结构,中间的节点既是master,也是 slave。注意使用树状结构时,由于层级深度,导致深度越高的slave与最顶层master间数据同步延迟较大,数据一致性变差,应谨慎选择

    阶段三:命令传播阶段

  • 当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播
  • master将接收到的数据变更命令发送给slave,slave接收命令后执行命令

    命令传播阶段的部分复制
  • 命令传播阶段出现了断网现象

    • 网络闪断闪连 忽略
    • 短时间网络中断 部分赋值
    • 长时间网络中断 全量复制
  • 部分复制的三个核心要素

    • 服务器的运行 id(run id)
    • 主服务器的复制积压缓冲区
    • 主从服务器的复制偏移量
      服务器运行ID(runid)
  • 概念:服务器运行ID是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行id

  • 组成:运行id由40位字符组成,是一个随机的十六进制字符 例如- -
    • fdc9ff13b9bbaab28db42b3d50f852bb5e3fcdce
  • 作用:运行id被用于在服务器间进行传输,识别身份
    • 如果想两次操作均对同一台服务器进行,必须每次操作携带对应的运行id,用于对方识别
  • 实现方式:运行id在每台服务器启动时自动生成的,master在首次连接slave时,会将自己的运行ID发送给slave,slave保存此ID,通过info Server命令,可以查看节点的runid

    复制缓冲区
  • 概念:复制缓冲区,又名复制积压缓冲区,是一个先进先出(FIFO)的队列,用于存储服务器执行过的命 令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区

  • 由来:每台服务器启动时,如果开启有AOF或被连接成为master节点,即创建复制缓冲区
  • 作用:用于保存master收到的所有指令(仅影响数据变更的指令,例如set,select)
  • 数据来源:当master接收到主客户端的指令时,除了将指令执行,会将该指令存储到缓冲区中

redis笔记 - 图30

复制缓冲区内部工作原理
  • 组成
    • 偏移量
    • 字节值
  • 工作原理
    • 通过offset区分不同的slave当前数据传播的差异
    • master记录已发送的信息对应的offset
    • slave记录已接收的信息对应的offset
  • redis笔记 - 图31

    主从服务器复制偏移量(offset)
  • 概念:一个数字,描述复制缓冲区中的指令字节位置

  • 分类:
    • master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个)
    • slave复制偏移量:记录slave接收master发送过来的指令字节对应的位置(一个)
  • 数据来源: master端:发送一次记录一次 slave端:接收一次记录一次
  • 作用:同步信息,比对master与slave的差异,当slave断线后,恢复数据使用

    数据同步+命令传播阶段工作流程

    redis笔记 - 图32

    心跳机制

  • 进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线

  • master心跳:
    • 指令:PING
    • 周期:由repl-ping-slave-period决定,默认10秒
    • 作用:判断slave是否在线
    • 查询:INFO replication 获取slave最后一次连接时间间隔,lag项维持在0或1视为正常
  • slave心跳任务

    • 指令:REPLCONF ACK {offset}
    • 周期:1秒
    • 作用1:汇报slave自己的复制偏移量,获取最新的数据变更指令
    • 作用2:判断master是否在线
      心跳阶段注意事项
  • 当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作

    1. min-slaves-to-write 2
    2. min-slaves-max-lag 8Copy
    • slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
  • slave数量由slave发送REPLCONF ACK命令做确认
  • slave延迟由slave发送REPLCONF ACK命令做确认

    完整流程

    redis笔记 - 图33

    常见问题

    redis笔记 - 图34
    redis笔记 - 图35

    频繁的网络中断

    redis笔记 - 图36
    image.png

    数据不一致

    redis笔记 - 图38

    九、哨兵

    1、简介

    哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
    redis笔记 - 图39

    2、作用

  • 监控

    • 不断的检查master和slave是否正常运行。 master存活检测、master与slave运行情况检测
  • 通知(提醒)
    • 当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
  • 自动故障转移
    • 断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址

注意:
哨兵也是一台redis服务器,只是不提供数据服务 通常哨兵配置数量为单数

3、配置哨兵

  • 配置一拖二的主从结构
  • 配置三个哨兵(配置相同,端口不同)
    • 参看sentinel.conf
  • 启动哨兵

    1. redis-sentinel sentinel端口号 .confCopy

    redis笔记 - 图40

    4、工作原理

    1.监控阶段

  • 用于同步各个节点的状态信息

    • 获取各个sentinel的状态(是否在线)
  • 获取master的状态
    • master属性
      • runid
      • role:master
      • 各个slave的详细信息
  • 获取所有slave的状态(根据master中的slave信息)
    • slave属性
      • runid
      • role:slave
      • master_host、master_port
      • offset

redis笔记 - 图41
redis笔记 - 图42

2.通知阶段

  • 各个哨兵将得到的信息相互同步(信息对称)

redis笔记 - 图43

故障转移

确认master下线
  • 当某个哨兵发现主服务器挂掉了,会将master中的SentinelRedistance中的master改为SRI_S_DOWN(主观下线),并通知其他哨兵,告诉他们发现master挂掉了。
  • 其他哨兵在接收到该哨兵发送的信息后,也会尝试去连接master,如果超过半数(配置文件中设置的)确认master挂掉后,会将master中的SentinelRedistance中的master改为SRI_O_DOWN(客观下线)

redis笔记 - 图44

推选哨兵进行处理
  • 在确认master挂掉以后,会推选出一个哨兵来进行故障转移工作(由该哨兵来指定哪个slave来做新的master)。
  • 筛选方式是哨兵互相发送消息,并且参与投票,票多者当选。

redis笔记 - 图45

具体处理
  • 由推选出来的哨兵对当前的slave进行筛选,筛选条件有:

    • 服务器列表中挑选备选master
    • 在线的
    • 响应快的
    • 与原master断开时间短的
    • 优先原则
      • 优先级
      • offset
      • runid
    • 发送指令( sentinel )
      • 向新的master发送slaveof no one(断开与原master的连接)
      • 向其他slave发送slaveof 新masterIP端口(让其他slave与新的master相连)

        十、集群

        1、简介

        集群架构

  • 集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果

    集群作用

  • 分散单台服务器的访问压力,实现负载均衡

  • 分散单台服务器的存储压力,实现可扩展性
  • 降低单台服务器宕机带来的业务灾难

    2、Redis集群结构设计

    数据存储设计

  • 通过算法设计,计算出key应该保存的位置

  • 将所有的存储空间计划切割成16384份,每台主机保存一部分 每份代表的是一个存储空间,不是一个key的保存空间
  • 将key按照计算出的结果放到对应的存储空间

redis笔记 - 图46
redis笔记 - 图47

  • 增强可扩展性 ——槽

redis笔记 - 图48

集群内部通讯设计

  • 各个数据库互相连通,保存各个库中槽的编号数据
  • 一次命中,直接返回
  • 一次未命中,告知具体的位置,key再直接去找对应的库保存数据

redis笔记 - 图49

十一、企业级解决方案

1、缓存预热

问题排查

  • 请求数量较高
  • 主从之间数据吞吐量较大,数据同步操作频度较高

    解决方案

  • 前置准备工作:

    • 日常例行统计数据访问记录,统计访问频度较高的热点数据
    • 利用LRU数据删除策略,构建数据留存队列 例如:storm与kafka配合
  • 准备工作:
    • 将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据
    • 利用分布式多服务器同时进行数据读取,提速数据加载过程
    • 热点数据主从同时预热
  • 实施:
    • 使用脚本程序固定触发数据预热过程
    • 如果条件允许,使用了CDN(内容分发网络),效果会更好

      总结

      缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

      2、缓存雪崩

      数据库服务器崩溃(1)

  1. 系统平稳运行过程中,忽然数据库连接量激增
  2. 应用服务器无法及时处理请求
  3. 大量408,500错误页面出现
  4. 客户反复刷新页面获取数据
  5. 数据库崩溃
  6. 应用服务器崩溃
  7. 重启应用服务器无效
  8. Redis服务器崩溃
  9. Redis集群崩溃
  10. 重启数据库后再次被瞬间流量放倒

    问题排查

  11. 在一个较短的时间内,缓存中较多的key集中过期

  12. 此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据
  13. 数据库同时接收到大量的请求无法及时处理
  14. Redis大量请求被积压,开始出现超时现象
  15. 数据库流量激增,数据库崩溃
  16. 重启后仍然面对缓存中无数据可用
  17. Redis服务器资源被严重占用,Redis服务器崩溃
  18. Redis集群呈现崩塌,集群瓦解
  19. 应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃
  20. 应用服务器,redis,数据库全部重启,效果不理想

    问题分析

  • 短时间范围内大量请求
  • 大量key集中过期

    解决方案(道)

  1. 更多的页面静态化处理
  2. 构建多级缓存架构 Nginx缓存+redis缓存+ehcache缓存
  3. 检测Mysql严重耗时业务进行优化 对数据库的瓶颈排查:例如超时查询、耗时较高事务等
  4. 灾难预警机制 监控redis服务器性能指标
    • CPU占用、CPU使用率
    • 内存容量
    • 查询平均响应时间
    • 线程数
  5. 限流、降级 短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问

解决方案(术)

  1. LRU与LFU切换
  2. 数据有效期策略调整
    • 根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟
    • 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
  3. 超热数据使用永久key
  4. 定期维护(自动+人工) 对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时
  5. 加锁 慎用!

    总结

    缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的出现 (约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
    redis笔记 - 图50

    3、缓存击穿

    数据库服务器崩溃(2)

  6. 系统平稳运行过程中

  7. 数据库连接量瞬间激增
  8. Redis服务器无大量key过期
  9. Redis内存平稳,无波动
  10. Redis服务器CPU正常
  11. 数据库崩溃

    问题排查

  12. Redis中某个key过期,该key访问量巨大

  13. 多个数据请求从服务器直接压到Redis后,均未命中
  14. Redis在短时间内发起了大量对数据库中同一数据的访问

    问题分析

  • 单个key高热数据
  • key过期

    解决方案(术)

  1. 预先设定
    以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长
    注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势
  2. 现场调整
    • 监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key
  3. 后台刷新数据
    • 启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失
  4. 二级缓存
    • 设置不同的失效时间,保障不会被同时淘汰就行
  5. 加锁 分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!

    总结

    缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可。

    4、缓存穿透

    问题排查

  6. Redis大面积出现未命中现象

  7. 出现非正常URL访问

    问题分析

    Redis获取到null数据未进行持久化,直接返回
    黑客攻击

    解决方案

  8. 缓存null

  9. 白名单
  10. 实时监控
  11. key加密

    十二 Redis管道

    Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
  • 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
  • 服务端处理命令,并将结果返回给客户端。

Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。

查看 redis 管道,只需要启动 redis 实例并输入以下命令:

  1. $(echo -en "PING\r\n SET runoobkey redis\r\nGET runoobkey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n"; sleep 10) | nc localhost 6379
  2. +PONG
  3. +OK
  4. redis
  5. :1
  6. :2
  7. :3