zoukankan      html  css  js  c++  java
  • Redis介绍及使用(六)

    前言

    1、目的:熟练使用redis并了解redis常见问题及其解决方案。

    2、要点:

    • 什么是Redis
    • 安装
    • 使用场景
    • 工具类
    • 常见问题及解决方案

    一 什么是Redis

    1.1 简介

    1.1.1概念

    1、Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。(非关系型缓存数据库)

    • 非关系型数据库:Nosql,redis,内存,太多了影响效率。
    • 关于redis,在后台操作时,并不一定需要对缓存进行添加,创建处理,但要保证缓存中的数据与数据库中的数据一致,避免出现脏数据。 redis缓存的产生,是在前台访问时(要结合业务与实际来考虑是否创建缓存)。

    1.1.2 五种数据类型及常用命令

     Redis五种数据类型分别是string(字符串),hash(哈希),list(列表),set(集合)及sortset-Zset(有序集合)。

    1.String

    常用命令: set,get,decr,incr,mget 等。

    String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用; 常规计数:微博数,粉丝数等。

    2.Hash

    常用命令: hget,hset,hgetall 等。

    hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。比如下面我就用 hash 类型存放了我本人的一些信息:

    key=JavaUser293847 
    value={ 
    “id”: 1, 
    “name”: “SnailClimb”, 
    “age”: 22, 
    “location”: “Wuhan, Hubei” 
    }

    3.List

    常用命令: lpush,rpush,lpop,rpop,lrange等

    list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。

    4.Set

    常用命令: sadd,spop,smembers,sunion 等

    set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。

    比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程,具体命令如下:

    sinterstore key1 key2 key3     将交集存在key1内

    5.Sorted Set

    常用命令: zadd,zrange,zrem,zcard等

    和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。

    举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。

    1.1.3 设置过期时间

      Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。  

      我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。  

      如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

    定期删除+惰性删除。

      通过名字大概就能猜出这两个删除方式的意思了。

    • 定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
    • 惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!

      但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。

    1.1.4redis 内存淘汰机制

    (MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?)

    redis 配置文件 redis.conf 中有相关注释,我这里就不贴了,大家可以自行查阅或者通过这个网址查看:http://download.redis.io/redis-stable/redis.conf

    redis 提供 6种数据淘汰策略:

    1.   volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    2.   volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
    3.   volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
    4.   allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
    5.   allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
    6.        no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

    4.0版本后增加以下两种:

      7.         volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰

      8.         allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key

    1.1.5redis 持久化机制

    (怎么保证 redis 挂掉之后再重启数据可以进行恢复)

      很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。  

      Redis不同于Memcached的很重一点就是,Redis支持持久化,而且支持两种不同的持久化操作。Redis的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file,AOF)。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。  

    1、快照(snapshotting)持久化(RDB)  

      Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。  

      快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:

    save 900 1           #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。  
    save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
    save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

    2、AOF(append-only file)持久化

      与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:

    appendonly yes

      开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。  

      在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

    appendfsync always    #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度 
    appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
    appendfsync no #让操作系统决定何时进行同步

      为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

    3、Redis 4.0 对于持久化机制的优化

      Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。  

      如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

    4、补充内容:AOF 重写

      AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小。

      AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或者写入操作。  

      在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作

    1.1.6 redis 事务

      Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。 

      在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。

      注意:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

    参看链接:https://www.cnblogs.com/baichunyu/p/11631660.html

    1.2深入了解

    参看链接:https://www.cnblogs.com/jelly12345/category/1892402.html

    1.3Linux上安装Redis

    1、安装Redis

    参看安装手册。

    2、本地安装Redis客户端

    参看安装手册。

    二、使用Redis

    2.1使用场景

    • 电商中存购物车信息;电商项目(分布式)中存用户信息;
    • flink项目中存分摊复核的结果,供用户系统轮询读取;
    • 当缓存使用,存放系统常用的如合约配置信息(分摊复核时用到了),当做缓存使用,设置过期时间(系统编写了Redis的缓存工具类),当缓存中没有数据时,才从数据库中查询,提高效率及用户体验。

    2.2 SSM架构使用

    zbj项目中的简单使用。引入依赖,添加配置文件,操作Redis。

    代码示例:

    2.3 分布式项目中使用

    1、添加查看购物车信息,用到了redis,购物车中的商品信息是存放在redis中的。因为添加、查看、删除购物车的操作比较频繁,这样就减少了与数据库的频繁交互,提高用户体验。

    2、因为在分布式项目中,不同的模块之间是属于跨服务器的跨域访问,要想实现一次登录,处处使用,原来单服务器的方式就不能用了。将用户信息放到redis中来实现。

    参看链接:https://www.cnblogs.com/szrs/p/12157083.html

    2.4 Spring-boot使用

    2.4.1 简单使用

    编写redis常用工具类,使用方式,常见问题。

    2.4.2jedis

    操作常见的五种数据类型。

    参看链接:

    • https://blog.csdn.net/lovelichao12/article/details/75333035
    • https://blog.csdn.net/qq_40667143/article/details/83313556
    • https://blog.csdn.net/weixin_40288381/article/details/88926592

    2.4.3连接池

    2.4.4 缓存工具

    三、 常见问题及其解决方案

    3.1 缓存雪崩和缓存穿透问题解决方案

    1、缓存雪崩  

      简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。  

      解决办法:  

    •   事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
    •   事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
    •   事后:利用 redis 持久化机制保存的数据尽快恢复缓存

    2、缓存穿透  

      简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。  

      解决办法:

      有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    3.2 如何解决 Redis 的并发竞争 Key 问题

    所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同!  

      推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)  

      基于zookeeper临时有序节点可以实现的分布式锁。大致思想为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。  

      在实践中,当然是从以可靠性为主。所以首推Zookeeper。

    3.3 如何保证缓存与数据库双写时的数据一致性?

      你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?  

      一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况。

      串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

    如果错过太阳时你流了泪,那你也要错过群星了。
    在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
    不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。
  • 相关阅读:
    (转)通过Javascript得到URL中的参数(query string)
    (转)对存储过程进行加密和解密(SQL 2008/SQL 2012)
    (转)怎样玩转千万级别的数据
    (转)mongodb学习(翻译1)
    (转)Web API 强势入门指南
    (转)正则表达式—RegEx(RegularExpressio)(三)
    学习进度-16 python爬虫
    学习进度-15 变量类型的转换
    学习进度-14
    学习进度-13
  • 原文地址:https://www.cnblogs.com/szrs/p/13424772.html
Copyright © 2011-2022 走看看