Redis

Author Avatar
kevin
发表:2020-07-22 17:09:00
修改:2024-10-09 17:09:32

Redis

远程连接使用参数 -h

Redis是一个开源的key-value存储系统

Redis的名字是Remote Dictionary Server的缩写。

数据类型

数据类型应用场景
string分布式Session存储 分布式数据库ID 计数器:统计网站访问量
hash存储对象信息(购物车中的商品信息) 存储表的信息
list实现队列、栈操作 汇总日志 粉丝列表 关注的人列表
set签到 打卡 点赞
zset排行榜 百度热点搜索
geospatial获取地理位置信息 两地之间的距离
hyperloglogs基数统计
bitmaps统计用户访问次数

操作都是原子性的

  • String
    • 二进制安全,可以包含任何数据,最大容量是512M
  • list,技巧, l开头
    • 底层是双向链表
    • 有序可以重复的数据类型
    • 值在键在,值光键亡
  • set类型,技巧,s开头
    • string类型的无序集合
    • 它底层其实是一个value为null的hash表
    • 会自动去重
  • hash类型,h开头
    • 可以当做Java中的Map<String,String>对待
    • 特别适合用于存储对象
  • zset类型
    • string类型元素的集合,且不允许重复的成员
    • 关联一个double类型的分数
    • 通过分数从小到大的排序
    • 分数(score)却可以重复

数据类型使用

  • KEY操作
    • 多个单词用“:”分开。例如:“user:token:session:id”
    • Redis命令不区分大小写,Key区分大小写
    • keys *查看当前库所有key
    • exists key判断某个key是否存在
    • type key 查看你的key是什么类型
    • del key 删除指定的key数据--------重点
    • unlink key 根据value选择非阻塞删除
    • EXPIRE KEY SECONDS(多少秒过期) 重点,过期会被Redis移除。
    • ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
    • select number 切换数据库,默认有0-15个
    • flushdb清空当前库
    • flushall通杀全部库
  • string操作
    • SET key value (重点) 添加键值对
    • GET key(重点) 获取指定 key 的值
    • APPEND KEY VALUE 追加到原值的末尾
    • STRLEN KEY 返回字符串长度
    • SETNX key value 只有在 key 不存在时设置 key 的值。
    • INCR key(重点) 将 key 中储存的数字值增一,如果为空,新增值为1
      • incrby 自定义步长
    • DECR key 将 key 中储存的数字值减一。如果为空,新增值为-1
      • decrby 自定义步长
    • MSET KEY VALUE 同时设置一个或多个key-value对,
    • MGET KEY 同时获取一个或多个value
    • MSETNX KEY VALUE 原子性,有一个失败则都失败
    • GETRANGE KEY START END 类似java中的substring,前包,后包
    • SETEX key seconds value(重点) 设置键值的同时,设置过期时间,单位秒
  • list操作
    • lpush或rpush key value 从左边或右边插入一个或多个值 重点
      • lpush类似于入栈,左边开始放,rpush右边开始放
    • lpop或rpop key 从左边或右边吐出一个值,值在键在,值光键亡
      • 没有值之后,键也没了
    • LRANGE key start stop 按照索引下标获得元素左0开头,右-1开头
    • LINDEX key index 按照索引下标获得元素(从左到右)
    • LLEN key 返回指定key所对应的list中元素个数
      • length
    • linsert key BEFORE或AFTER value newvalue 第一个遇到value,插入新值
    • LREM key count value 从左边删除n个value
      • remove
    • LSET key index value 把指定索引位置的元素替换为另一个值
  • Set操作,里面的成员称为member,自动去重
    • SADD key value 将一个或多个member 元素加入到集合 key 中
    • SMEMBERS key 取出该集合的所有值
    • SISMEMBER key value 判断是否含有该值
    • SCARD key 返回集合中元素的数量
    • SREM key value 删除集合某个元素
    • SPOP key 随机从该集合中吐出一个值
    • SINTER key1 key2 返回两个集合的交集元素
    • SUNION key1 key2 返回两个集合的并集元素。
    • SDIFF key1 key2 返回两个集合的差集元素
  • Hash操作
    • HSET key field value 将哈希表 key 中的字段 field 的值设为 value
    • HGET key field 获取存储在哈希表中指定字段的值
    • HMSET key field value 批量设置hash的值
    • HEXISTS key field 判断是否存在某个字段
    • HKEYS key 获取所有哈希表中的字段
    • HVALS key 获取哈希表中所有值
  • zset操作
    • ZADD key score1 value1 增加一个或多个元素
    • ZRANGE key start stop [WITHSCORES] 获得排名在某个范围的元素列表
      • WITHSCORES(可选),可以让分数一起和值返回到结果集
    • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
      • 在分数的指定区间内返回数据
    • ZREM key value 删除元素,可以删除多个
    • ZRANK key value 先对分数进行升序排序,返回集合中的排名。排名从0开始

Jedis

为了使java程序访问jedis,禁用Linux的防火墙

redis.conf中注释掉bind 127.0.0.1(75行) ,然后 protected-mode(94行) 的值设置为no

引入Jedis的依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

使用代码

//指定Redis服务器的IP地址和端口号
Jedis jedis = new Jedis("Jedis服务器的IP地址", 6379);

//jedis的方法是与jedis的命令一一对应的
//存储一个键值对
jedis.set("tom:code","123456");

//根据key获取值
System.out.println(jedis.get("tom:code"));    

//关闭连接
jedis.close();

Jedis的连接池

//测试使用Jedis连接池
    //1. 创建连接池
    JedisPool jedisPool = new JedisPool("192.168.141.136",6379);

    //2. 我们可以对连接池进行配置
    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
    //最大连接数
    poolConfig.setMaxTotal(20);
    //最大闲置连接数
    poolConfig.setMaxIdle(20);
    //最小闲置连接数
    poolConfig.setMinIdle(0);
    poolConfig.setMaxWaitMillis(3000);

    //3. 使用连接池获取连接
    Jedis jedis = jedisPool.getResource();

    System.out.println(jedis.get("username"));

    //归还连接
    jedis.close();

Redis事务控制

命令名作用
MULTI表示开始收集命令,后面所有命令都不是马上执行,而是加入到一个队列中。
EXEC执行MULTI后面命令队列中的所有命令。
DISCARD放弃执行队列中的命令。
WATCH“观察“、”监控“一个KEY,在当前队列外的其他命令操作这个KEY时,放弃执行自己队列的命令,相当于加入事务
UNWATCH放弃监控一个KEY

入队过程中某个命令出现了报告错误,执行时整个的所有队列都会被取消。

如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。

主从复制机制

主机数据更新后根据配置和策略, 自动同步到备机的master/slave机制,

Master以写为主,Slave以读为主

主从复制的目的

  • 性能优化:主服务器专注于写操作,可以用更适合写入数据的模式工作;同样,从服务器专注于读操作,可以用更适合读取数据的模式工作。
  • 强化数据安全,避免单点故障:由于数据同步机制的存在,各个服务器之间数据保持一致,所以其中某个服务器宕机不会导致数据丢失或无法访问。从这个角度说参与主从复制的Redis服务器构成了一个集群

搭建思路

集群在运行时使用的是同一个可执行文件(redis-server),只是对应的配置文件(redis.conf)不同。

  • 拷贝多个redis.conf文件include(写绝对路径)
  • 开启daemonize yes
  • Pid文件名字pidfile
  • 指定端口port
  • Log文件名字
  • dump.rdb名字dbfilename
  • Appendonly 关掉
  • replica-priority 10
    • 设置从机的优先级,值越小,优先级越高

运行redis开启服务后进入服务 查看主从关系

info replication

配置主从关系(否则默认每台都是主机)

从机上指定主机的地址和端口号即可

SLAVEOF 127.0.0.1 6000

取消主从关系

从机上执行命令SLAVEOF NO ONE即可,那么此时从机就自己变成了主机

有以下现象

  • 主机关闭之后,会等待主机上线

  • 从机关闭之后,需要从新设置主从关系

  • 设置之后,之前的命令同步

哨兵模式

主机故障后,从机根据优先级,自动升级为主机,原主机上线后成为从机

新建sentinel.conf文件

sentinel monitor 主机名称 127.0.0.1 主机端口号 1

1是指需要几个哨兵同意

执行redis-sentinel sentinel.conf

缓存穿透

  • 服务器压力突然增大
    • 大量的请求
  • redis命中率降低
    • 一直查询不存在的数据
    • 缓冲中没有该数据
  • 一直查询数据库
    • 缓冲查询不到则查询数据库导致崩溃

key对应的数据在数据源并不存在,请求都会压到数据源,可能压垮数据库

解决办法

  • 对空值缓存
    • 不管是数据是否不存在
    • 空结果(null)进行缓存
    • 设置空结果的过期时间,不超过五分钟
    • 临时应急方案
  • 设置可访问的名单(白名单)
    • 使用bitmaps类型(位操作)定义一个可以访问的名单
    • 如果访问id不在bitmaps里面,进行拦截
    • 效率不高
  • 采用布隆过滤器
    • 布隆过滤器可以用于检索一个元素是否在一个集合中
    • 缺点是有一定的误识别率和删除困难
  • 进行实时监控
    • 排查访问对象和访问的数据
    • 设置黑名单限制服务

缓存击穿

  • 数据库访问压力瞬间增加
  • redis没有发现大量key过期
  • redis正常运行
  • 原因
    • redis某个key(比较热门的key)过期了,大量访问使用这个key
    • 这个时候大并发的请求可能会瞬间把后端数据库压垮。

数据存在,但在redis中过期,若有大量并发请求过来,

请求发现缓存过期一般都会从后端数据库加载数据并回设到缓存

这个时候大并发的请求可能会瞬间把后端DB压垮

解决办法

  • 预先设置热门数据
    • 热门数据提前存入到redis里面,加大这些热门数据key的时长
  • 实时调整
    • 现场监控哪些数据热门,实时调整key的过期时长
  • 使用锁
    • 效率低

缓存雪崩

  • 数据库压力变大
    • 造成服务器崩溃
  • 原因
    • 极少时间段,查询大量key的集中过期情况

缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key

正常访问

解决办法

  • 构建多级缓存架构
    • nginx缓存 + redis缓存 +其他缓存(ehcache等)
  • 使用锁或队列
    • 不适用高并发情况
  • 设置过期标志更新缓存
    • 记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
  • 将缓存失效时间分散开
    • 原有的失效时间基础上增加一个随机值
    • 1-5分钟随机,缓存的过期时间的重复率就会降低
评论