当前位置 : 主页 > 网络编程 > 其它编程 >

再见Redis

来源:互联网 收集:自由互联 发布时间:2023-07-02
此再见非彼再见在之前的一篇文章中学习了Redis的安装和基本的使用在边实习边学习的过程中加深了对其的理解在之前的一篇文章中学习了 Redis 的安装和基本的使用在边实习边学习的过
此再见非彼再见在之前的一篇文章中学习了Redis的安装和基本的使用在边实习边学习的过程中加深了对其的理解在之前的一篇文章中学习了 Redis 的安装和基本的使用在边实习边学习的过程中加深了对其的理解所以在这里总结一哈。

文章目录

      • 回顾 Redis
      • 键 Key 的管理
      • 五种数据类型
        • String
        • Hash
        • Java 操作 Redis
        • List
        • Set
        • ZSet
      • Redis 发布订阅
      • Redis 多数据库
      • Redis 事务
      • Redis 数据淘汰
      • Redis 持久化
      • Redis 缓存与数据库一致性
        • 实时同步
        • 异步队列
        • 第三方同步工具
        • 热点 key(缓存雪崩)
      • Redis 并发
        • 垂直扩展
        • 水平扩展
      • Redis 集群
        • 集群搭建


回顾 Redis

Redis 支持五种的数据类型 String字符串、Hash哈希、List列表、Set集合及 ZSetsorted set有序集合。


键 Key 的管理

命令

命令作用DEL keykey 存在时删除 keyDUMP key序列化给定 key 并返回序列化的值EXISTS key检查 key 是否存在EXPIRE key seconds为给定 key 设置过期时间PEXPIRE key milliseconds同上TTL key以秒为单位返回给定 key 的剩余生存时间PTTL key以毫秒为单位同上PERSIST key移除 key 的过期时间key 将持久保持KEYS pattern查找所有符合给定模式的 key * RENAME key newkey修改 key 的名称MOVE key db将当前数据库的 key 移动到给定的数据库 db 中TYPE key返回 key 所存储的值的类型

最常用的就是 EXPIRE key seconds应用场景

  • 限时优惠活动信息
  • 需要定时更新的数据积分榜
  • 手机验证码

key 的命名建议

  • 不要太长redis 单个 key 存入 512M 大小且会降低查找效率
  • 使用统一的命名模式提高 key 的可读性如 studen,userId

五种数据类型

String

最基本的 Redis 存储类型一个 key 对应一个 value。

二进制安全是指在传输数据时编码、解码发生在客户端保证二进制数据的信息安全不会被篡改或者破译。

而 String 类型是二进制安全的也就是说可以保存任何的数据如图片或者序列化的对象等。

命令

命令作用SET key value设置给定 key 的值如果已经存在则覆盖SETNX key value只在 key 不存在时设置 key 的值MSET key value [key value]同时设置多个 key-valueGET key获取指定 key 的值不存在返回 nilGETRANGE key start end获取获取指定 key 中字符串的子字符串GETBIT key offset对 key 所储存的字符串值获取指定偏移量上的位MGET key1 [key2]获取多个给定 key 的值GETSET设置指定 key 的值返回旧值STRLEN key返回 key 所储存的字符串值的长度DEL key删除指定的 keyINCR key将 key 中储存的数字加1如果不存在则初始化为0再操作INCR key 增量指定 key 的自增量DECR keyDECR key 减量APPEND key value在指定 key 值的末尾追加

虽然 String 可以保存任何数据但我们还是需要选择合适的储存类型这样才便于我们操作数据。 String 类型的应用场景

  • 单个字符串或 JSON 字符串数据
  • 图片文件
  • 计数器自增自减命令都具有原子特性

Hash

相比于 String 类型Hash 类型更适合储存一个 JavaBean 对象。

User(id,name,age,sex)

命令

命令作用HSET key field/value为指定 key 指定field valueHMSET key field value [field value]同上多个HGET key field获取储存在 hash 中的值根据 field 得到 valueHMGET key field[field1]同上多个HGETALL key返回 hash 表中所有的字段和值HKEYS key获取所有哈希表中的字段HLEN key获取哈希表中字段的数量HDEL key field[field1]删除一个或多个 hash 表字段HSETNX key field value只有在字段 field 不存在时设置哈希表字段的值HINCRBY key field increment为 hash 表 key 中的指定整数字段加上增量HINCRBYFLOAT key field increment同上浮点字段HEXISTS key field查看 hash 表 key 中指定的字段是否存在

为什么不用 String 存储一个对象 首先我们来看下用 String 类型来储存一个用户对象的方式

  • 将用户 ID 作为查找 key而其他信息以序列化的方式存储在操作数据时需要进行序列化或反序列化同时需要考虑并发问题。
  • 将对象所有的成员都以 key-value 的形式存储用 ID属性名来作为 key这样虽然没有了上一种方法的缺点但造成大量内存的浪费。

反观hash 类型它是最接近关系数据库结构的数据类型可以将数据库一条记录转换成一个 hashmap 存放到 redis 中。


Java 操作 Redis

常用的两种

  • Jedis是Redis官方推荐的面向Java的操作Redis的客户端
  • RedisTemplate 是 SpringDataRedis中对JedisApi 的高度封装

Jedis 1.引入 JAR

redis.clientsjedis2.4.2

2.连接 Redis

//直接 new Jedis 对象Jedis jedis new Jedis("127.0.0.1",6379);jedis.auth("123456");//通过 redis poolJedisPoolConfig poolConfig new JedisPoolConfig();poolConfig.setMaxTotal(5);//最大连接数poolConfig.setMaxIdle(1);//最大空闲数String host "127.0.0.1";int port 6379;JedisPool pool new JedisPool(poolConfig,host,port);Jedis jedis pool.getResource();jedis.auth("123456");

3.使用 Jedis 客户端

String key "moke";if(jedis.exists(key)){String res jedis.get(key);System.out.println("Redis数据"res);}else{//查询数据库获取到 resString res "数据库数据";jedis.set(key,result);System.out.println("MySQL数据"res);}jedis.close();

这一步就是我们平时在项目中的应用为了减轻数据库的访问压力首先判断 Key 是否存在如果存在则直接返回如果不存在才查询数据库然后将结果存到 redis 中下次调用就不需要访问数据库。

当然我们可以将获取 Jedis 客户端的操作封装成一个工具类

public class JedisUtils{private static final JedisPool JEDIS_POOL;static{JedisPoolConfig config new JedisPoolConfig();poolConfig.setMaxTotal(5);poolConfig.setMaxIdle(1);JEDIS_POOL new JedisPool(poolConfig,"127.0.0.1",6379);}public static Jedis getJedis(){Jedis jedis JEDIS_POOL.getResource();jedis.auth("123");return jedis;}}

我们用上面的工具类来看下如何使用 Jedis 来操作 hash 类型

//user selectById(id)Jedis jedis JedisUtils.getJedis();String key user.getName() user.getId();if(jedis.exist(key)){Map hash jedis.hgetAll(key);User u new User();u.setId(Integer.parseInt(hash.get("id")));u.setName(hash.get("name"));u.setAge(hash.get("age"));u.setSex(hash.get("sex"));System.out.println("Redis中数据"u);}else{User user userService.getBy(id);Map hash new HashMap();hash.put("id",u.getId()"");hash.put("name",u.getName());hash.put("age",u.getAge());hash.put("sex",u.getSex());jedis.hmset(key,hash);System.out.println("MySQL中数据"user);}

从上面我们可以发现jedis 操作 Redis 的方法名其实就是我们之前学的那些 Redis 的命令。 也可以发现用 Jedis 操作比较繁琐所以就有了 RedisTemplate 模板封装了 redis 连接池管理的逻辑无需关心连接的获取与释放。

RedisTemplate 使用过程可以在之前的文章中看地址 这里主要说下在 Spring 中配置 redistemplate 时配置 key 或者 value 的序列化不然可能会乱码


List

类似于 LinkedList主要用于存储一组数据可以很方便实现分页也可以用于实现任务消息队列。

命令

命令作用LPUSH key value [value1]将一个或多个值插入到列表头部RPUSH key value [value1]将一个或多个值插入到列表尾部LPUSHX key value将一个值插入到已存在的列表头部如果不存在则无效RPUSHX key value将一个值插入到已存在的列表尾部如果不存在则无效LLEN key获取列表长度LINDEX key index通过所以获取列表中的元素LRANGE key start stop获取列表指定范围内的元素LPOP key移除并获取列表的第一个元素RPOP key移除并获取列表的最后一个元素BLPOP key1 [key2] timeout移除并获取列表第一个元素如果列表没有元素会阻塞到有或者超时LTRIM key start stop对一个列表进行修剪不在区间的元素删去LSET key index value通过索引设置列表元素的值LINSERT key BEFOREAFTER wordl valueRPOPLPUSH source detination移除列表最后一个元素并添加到另一个列表返回BRPOPLPUSH source destination timeout从列表中弹出一个值并插入另一个列表如果前一个列表没有元素则会阻塞到有或超时

任务队列 list 一般会用来实现一个消息队列

任务队列 在处理请求时某些操作的执行时间可能会比较长为了避免用户一直等待通过将其放入队列并在之后对队列进行处理这种将工作交给任务处理器来执行的做法被称为任务队列。

而任务队列则可以使用 BRPOPLPUSH 命令来实现。


Set

类似 Java 中的 HashTable 集合是 String 类型的无序集合集合成员是唯一的。

Set 底层存储结构使用了 intset 和 hashtable 两种前者为数组而后者则是哈希表

  • intset 只有保存整数元素时才使用使得它可以通过二分查找元素。
  • hashtable 的value永远为null实际就是通过计算hash的方式来快速排重的这也是set能提供判断一个成员是否在集合内的原因。

命令

命令作用SADD key member1 [member2]向集合添加一个或多个成员SCARD key获取集合的成员数SMEMBERS key返回集合中的所有成员SISMEMBER key member判断 member 元素是否是集合 key 的成员SRANDMEMBER key[count]返回集合中一个或多个随机数SREM key member1 [member2]移除集合中一个或多个成员SPOP key[count]移除并返回集合中的一个随机元素SMOVE source destination member将 member 元素移到 destination 集合SDIFF key1 [key2]返回给定所有集合的差集左侧SDIFFSTORE destination key1 [key2]结果存储在 destination 中SINTER key1 [key2]返回给定所有集合的交集共有SINTERSTORE destination key1 [key2]结果存储在 destination 中SUNION key1 [key2]返回所有给定集合的并集SUNION STORE destination key1 [key2]结果存储在 destination 中

应用场景

  • 集合运算共同好友、共同关注等
  • 唯一性统计访问网站的IP

ZSet

ZSet即 sorted set因为 redis 中有序集合的操作命令都以 z 开头。

ZSet 和 Set 不同的是每个元素都会关联一个 double 类型的分数通过分数对集合中的成员进行从小到大的排序。

有序集合的成员是唯一的但分数score可以重复。

命令

命令作用ZADD key score1 member1 [score2 member2]向有序集合添加一个或多个成员或更新已存在成员的分数ZCARD key获取有序集合的成员数ZCOUNT key min max计算指定区间的成员数ZRANK key start stop从低到高返回指定索引区间的成员ZREVRANGE key start stop从高到低返回指定索引区间的成员del key移除集合ZREMRANGEBYRANK key start stop移除有序集合中给定的排名区间的所有成员ZREMRANGEBYSCORE key min max移除有序集合中给定的分数区间的所有成员

应用场景排行榜、定时任务


Redis 发布订阅

发布/订阅模式(Pub/Sub)是一种消息模式,它有 两个参与者 : 发布者和订阅者 。发布者向 某个信道发布一条消息订阅者绑定这个信道当有消息发布至信道时就会接收到一个通知。最重要的一点是 发布者和订阅者是完全解耦的彼此并不知晓对方 的存在。两者仅仅共享一个信道名称。

从定义上可以看出发布订阅模式里双方是完全解耦的而在观察者模式里目标对象管理这观察者双方是耦合的这是最主要的区别而在发布订阅模式中多了一个中间层信道频道。

命令

命令作用SUBSCRIBE chaannel […]订阅给定的一个或多个频道的信息PSUBSCRIBE pattern […]订阅一个或多个符合给定模式的频道PUBLISH channel message将消息发送到指定频道UNSUBSCRIBE channel […]退订给定的频道PUNSUBSCRIBE pattern […]退订给定模式的频道

应用场景构建实时消息系统如即时聊天。


Redis 多数据库

Redis 下数据库是由一个整数索引标识默认情况下客户端连接到数据库 0。

在 Redis 配置文件中

database 16

表示从 0 开始有16个数据库

命令

命令作用select 索引切换数据库move key 索引移动数据flushdb清除当前数据库的所有 keyflushall清除整个 Redis 的数据库所有的 key

Redis 事务

事务执行的阶段

  • 开始事务
  • 命令入队
  • 执行事务

Redis 会将一个事务中的所有命令序列化然后按顺序执行执行中不会被其他命令插入。

命令

命令作用DISCARD取消事务EXEC执行所有事务块内的命令MULTI标记一个事务块的开始UNWATCH取消 WATCH 命令对所有 key 的监视WATCH key […]监视 key如果事务执行前这个 key 被其他命令改动那么事务将被打断

事务的错误处理

  • 语法报告错误整个事务不会执行
  • 如果执行的某个命令报出错误则只有报错的命令不会被执行而其他命令依然执行不会回滚。

Redis 数据淘汰

当内存不足时Redis 会根据配置的缓存策略淘汰部分 Keys以保证写入成功。如果无淘汰策略或没找到合适淘汰的 Key 时会返回 out of memory 错误。

最大缓存配置 在配置文件中

maxmemory 1G

6 种淘汰策略

  • volatile-lru从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-lfu从已设置过期的 Keys 中删除一段时间内使用次数最少的。
  • volatile-ttl从已设置过期时间的数据集中挑选最近要过期的数据淘汰。
  • volatile-random从已设置过期时间的数据集中随机选择数据淘汰。
  • allkeys-lru从数据集中挑选最近最少使用的
  • allkeys-lfu从所有 Keys 中删除一段时间内使用次数最少的。
  • allkeys-random从数据集中随机选择数据淘汰。
  • no-enviction禁止淘汰数据返回错误信息

注 平时使用 Redis 时尽量主动设置 key 的 expire 时间有助于提高查询性能。


Redis 持久化

两种持久化机制

  • RDB
  • AOF

RDB redis 默认的持久化机制以快照的方式将内存中的数据写入二进制文件中dump.rdb

优点 保存数据和恢复数据极快适用于灾难备份。 缺点小内存机器不适合且 RDB 只有符合要求才会执行快照。

快照条件

  • 服务器正常关闭
  • 配置文件中设置
  • save 900 1 //每900秒至少一个key发送变化则产生快照

    AOF RDB 有一个致命缺陷就是如果 Redis 意外 down 掉的话就会丢失最后一次快照之后的所有修改。

    而 AOFAppend-only file是在 Redis 的每次写操作都通过 write 方法将数据追加到文件中当 Redis 重启时就会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

    三种方式

  • appendonly yes启用 aof 持久化方式
  • appendfsync always收到写命令就立即写入磁盘保存完全的持久化最慢
  • appendfsync everysec每秒写入磁盘一次折中
  • appendfsync no最快完全依赖系统
  • 缺点持久化的文件会越来越大且对于自增命令出现多少次自增就会有多少条命令。


    Redis 缓存与数据库一致性

    四种解决方案

    • 实时同步
    • 异步队列
    • 第三方同步工具阿里
    • UDF 自定义函数

    实时同步

    原理 查询不到缓存时才会从数据库查询并保存到缓存。而在更新缓存时先更新数据库再将缓存的设置过期时间。

    使用注解

    Cacheable CachePut CacheEvict Caching

    缓存穿透 在查询一个一定不存在的数据由于缓存在不命中时需要查询数据库查不到数据则不写入缓存这就会导致每次请求查询这个不存在的资源时每次都要查询数据库造成缓存穿透。 解决查询不到不是不缓存而是缓存空结果注意insert。

    异步队列

    对于并发程度较高的可采用异步队列的方式同步比如 kafka、ActiveMQ等消息中间件处理消息生产和消费。 1

    第三方同步工具

    使用阿里的 canal 实现方式是模拟 mysql slave 和 master 的同步机制主从复制通过监控 DB bitlog 的日志更新来触发缓存的更新 在这里插入图片描述

    热点 key(缓存雪崩)

    对于某个 key 访问平凡即使设置了失效时间在失效时有大量线程来构建缓存导致负载增加解决办法

    • 构建缓存的地方使用锁单机用 synchronizedlock等分布式用分布式锁。
    • 缓存过期时间不设置将时间设置在value中如果检查到value中的时间过期则异步更新缓存。
    • 设置标签缓存以及它的过期时间该标签过期后会异步更新实际缓存。

    Redis 并发

    一般不会使用一台 Redis 服务器原因

    • 单个 Redis 服务器容易发生单点故障
    • 单个服务器的性能与系统资源比较有限

    高可用 “高可用性”通常来描述一个系统经过专门的设计从而减少停工时间而保证其服务的高度可用性。

    高并发 高并发是指通过设计保证系统能够同时并行处理很多请求。

    关键字

  • 响应时间系统对请求做出响应的时间
  • 吞吐量单位时间内处理的请求数
  • 每秒查询率每秒响应请求数
  • 并发用户数同时承载正常使用系统功能的用户数量
  • 提高并发能力 主要有两种类型

    • 垂直扩展
    • 水平扩展

    垂直扩展

    垂直扩展就是提升单机处理能力例如

  • 增加单机硬件性能CPU、内存、网卡…
  • 提升单机架构性能缓存减少 IO 次数使用异步来增加吞吐量使用无锁数据结构减少响应时间
  • 优点 是最快最简单的方式。 缺点 提升是有限的

    水平扩展

    水平扩展则是只要增加服务器的数量就能线性扩充系统性能主要难点是在架构各层进行可水平扩展的设计。

    redis 是一个非关系型数据库其常见的水平扩展也和 mysql 一样可以实现主从复制如下图 在这里插入图片描述将一台 Redis 服务器作为主库其他三台作为从库主库只负责写数据每次有数据更新将更新的数据同步到它所有的从库而从库只负责数据。 注 一台主库可以有多个从库而一个从库只能附属一台主库。

    主从复制配置 1.主数据库不需要配置只需在传讲从数据库时指定主服务器就行了

    port 6380 //从服务器的端口号 slaveof 127.0.0.1 6379 //指定主服务器 也可以在启动时指定 ./usr/local/redis/redis.server ./redis.conf --port 6380 --slaveof 127.0.0.1 2.主从服务器客户端切换 slaveof on one //变回主 slaveof ip port //变回从


    Redis 集群

    常见的 Redis 集群搭建方案有三种

    • Twitter 的 twemproxy
    • 豌豆荚的 codis
    • redis 官方的 redis-cluster 至少3master3slave

    这里我们主要学习的 redis 官方的 redis-cluster其主要特点

  • 所有节点彼此互联内部使用二进制协议优化传输速度和带宽。
  • 节点的 fail 状态是检测到集群中超过半数的节点无法连接才会生效。
  • 客户端与 节点直连不需要中间代理层只需连接其中一个节点。
  • redis-cluster 把所有物理节点映射到[0-16383]slot上由它负责维护。 16384个哈希槽对 key 使用 crc16 算法再对16384求余数通过这样的方式将内容放到对应的槽中
  • 容错性 redis-cluster 投票机制投票过程是集群中所有 master 参与如果半数以上 master 与某个 master 节点通信超时则认为当前 master 节点挂掉。 如果集群超过半数以上 master 挂掉或者任意master挂掉且其没有slave则集群进入 fail 状态。

    集群搭建

    集群中至少有奇数个节点所以搭建集群最少需要 3 台主机同时每个节点至少有一个备份节点所以最少需要创建使用 6 台机器才能完成 Redis Cluster 集群主节点、备份节点由 redis-cluster 集群确定。

    1.对每个机器中的 redis 的配置文件进行修改 在这里插入图片描述2.创建集群 可以通过官方提供的 redis-trib.rb 来创建集群安装后直接

    redis-trib.rb create --replicas 1 197.168.1.101:7001 197.168.1.102:7002 197.168.1.103:7003 197.168.1.104:7004 197.168.1.105:7005 197.168.1.106:7006

    3.连接集群

    redis-cli -h 197.168.1.103 -c -p 7003

    只需要指定连接集群上的其中一个节点即可。

    4.查看集群信息

    Cluster Nodes Cluster Info

    在这里插入图片描述每个 Redis 的节点都有一个 ID 值被称为节点 ID。此 ID 被特定的 redis 实例永久使用以便实例再集群上下文中具有唯一的名称。

    上一篇:PY27中的range()和xrange()
    下一篇:没有了
    网友评论