0%

Redis学习笔记

前言

Redis个人总结的学习笔记,后续继续补充知识点

1.Redis概述

Redis实现机制:单线程+多路IO复用(具有原子性的原因)

1.1 Redis基础命令

1.1.1 Redis手动启动方式

Redis服务器启动命令:redis-server

1.1.2 Redis后台自启动方式

  1. 复制redis.conf到任意新目录(自定义redis.conf)
  2. 修改redis.conf中的daemonize no改为yes,让服务自启动
  3. redis-server /新目录/redis.conf:启动服务
  4. redis-cli:连接客户端

1.1.3 Redis关闭方式:

  • 单实例关闭:redis-cli shutdown
  • 多实例关闭:
    1. ps -ef |grep redis
    2. redis-cli -p 指定端口号 shutdown

1.1.4 Redis键相关命令:

  • 查看当前库所有key:**key **
  • 用于查找所有符合给定模式 pattern 的 key:keys pattern
  • 迭代数据库中的数据库键:SCAN cursor pattern count
  • 设置键值对:set key value
  • 查找key对应value:get key
  • 只有在key不存在时设置key的值,成功添加返回1:setnx key value
  • 判断key是否存在:exist key
  • 查看key的数据类型:type key
  • 删除指定的key:del key
  • 根据value选择非阻塞删除:unlink key(仅将key从keyspace元数据中删除,真正的删除会在后续异步操作)
  • 将给定的value追加到原值的末尾:append key value
  • 获取值的长度:strlen key
  • 将key中存储的数字值增加1:incr key
  • 将key中存储的数字值减少1:decr key
  • 将key中存储的数字值增减自定义值:incrby/decrby key 步长
  • 为给定的key设置过期时间:expire key 指定时间
  • 查看key还有多久过期:ttl key(-1表示永不过期,-2表示已经过期)

1.1.5 操作数据库相关命令

  • 切换数据库:select 数字
  • 查看当前数据库的key的数量:dbsize
  • 清空所有库:flushdb

2. 常用数据类型

2.1 String字符串

注意:String的数据结构为简单动态字符串,是可以修改的字符串没类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配

常用命令:

  • 同时设置一个或多个key-value对:mset key1 value1 key2 value2

  • 同时获取一个或多个value:mget key1 key2 key3

  • 当且仅当所有给定的key都不存在时,同时设置一个或多个key-value对:msetnx key1 value1 key2 value2 (具有原子性,有一个失败则都会失败)

  • 获取范围内的值:getrange key 起始位置 结束位置

  • 用value覆写key所存储的字符串值中的字符,从起始位置开始:setrange key 起始位置 value

  • 设置键值的同时设置过期时间:setex key 过期时间 value

  • 获取key对应旧值的同时设置为新值value:getset key value

2.2 List列表

注意:List是单键多值的,底层是双向链表,可以添加元素到列表头部或者尾部
(Redis将链表和ziplist结合起来组成了quicklist,即将多个ziplist使用双向指针串起来使用)
常用命令:

  • 从左边/右边插入一个或多个值:lpush/rpush key value1 value2

  • 从左边/右边弹出一个值(值在键在):lpop/rpop key

  • 从key1列表右边弹出值插到key2列表左边:rpoplpush key1 key2

  • 按照索引下标获取范围内的元素:lrange key start stop(0左边第一个,-1右边第一个,0 -1表示获取所有)

  • 按照索引下标获取指定位置的元素:lindex key index

  • 获取列表长度:llen key

  • 在value的前面/后面插入值:linsert key before/after value newValue

  • 从左边开始删除n个相同的value:lrem key n value

  • 将列表key下标为index的值替换成value:lset key index value

2.3 Set无序集合

注意:Set是String类型的无序集合,底层是一个value为null的哈希表,添加、删除、查找的复杂度都为O(1)
常用命令:

  • 将一个或多个member元素加入到集合key中,已经存在的member元素将被忽略:sadd key value1 value2

  • 取出该集合中的所有值:smembers key

  • 判断集合key是否含有对应的value:sismember key value(有则为1,没有为0)

  • 返回该集合的元素个数:scard key

  • 删除集合中的某个元素:srem key value1 value2

  • 随机从集合中弹出一个值:spop key

  • 随机从集合中取出n个值,不会从集合中删除:srandmember key n

  • 把集合中一个值从集合1移动到集合2:smove 集合1 集合2 value

  • 返回两个集合的交集元素:sinter key1 key2

  • 返回两个集合的并集元素:sunion key1 key2

  • 返回两个集合的差集元素(key1中有的而key2没有的):sdiff key1 key2

2.4 Hash哈希表

注意:Redis的Hash是一个String类型的key-(field: value)的映射表(键值对集合),而Hash类型对应的数据结构有两种,当field:value长度较短且个数较少时使用ziplist,否则使用hashtable

常用命令:
给key集合中的field键赋值为value:hset key field value

从key集合的field中取出对应的value:hget key field

批量设置hash值:hmset key field1 value1 field2 value2

查看key中给定的field是否存在:hexists key field

列出该key集合中所有的field:hkeys key

列出该key结合中所有的value:hvals key

为key中的域field的值加上指定增量:hincrby key field 指定增量

将哈希表key中的域field的值设置为value,当且仅当域field不存在:hsetnx key field value

2.5 Zset有序集合

注意:没有重复元素的字符串集合(有序集合的每个成员都关联了一个score,score被用来按照从最低到最高的方式排序集合中的成员,集合中的成员是唯一的,但是score可以重复),其底层结构是Hash+跳跃表

常用命令:
将一个或多个member元素及其score值加入到有序集key中:zadd key score1 value1 score2 value2

返回有序集合key中下标在start和stop之间的元素,并让分数一起和值返回到结果集:zrange key start stop withscores

返回有序集合key中所有score值介于min和max之间的成员,有序集合成员按score

递增排列:zrangebyscore key min max

递减排序:zrevrangebyscore key max min

为元素的score加上增量:zincrby key 增量 value

删除有序集合key中指定值的元素:zrem key value

统计集合指定区间内的元素个数:zcount key min max

返回该值在集合中的排名,从0开始:zrank key value

3. Redis 6新数据类型

3.1 Bitmaps字符串

注意:Bitmaps字符串本身不是一种数据类型,只是对字符串进行位操作(以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量)

常用命令:
设置Bitmaps中某个偏移量的值(0或1):setbit key offset value

获取Bitmaps中某个偏移量的值:getbit key offset

统计字符串从start到end之间比特值为1的数量(0为左边第一个,-1为右边第一个):bitcount key start end

做多个Bitmaps的与或非操作并将结果保存在destkey中:bitop and(or/not/xor) destkey key1 key2…

3.2 HyperLogLog去重

注意:HyperLogLog去重是用来做基数统计,即统计不重复元素的算法;(计算基数所需的空间总是固定的并且很小)

常用命令:

添加指定元素到HyperLogLog中:pfadd key element (执行命令后如果近似基数发生变化则返回1,否则返回0)

计算HyperLogLog的近似基数:pfcount key…

将一个或多个HyperLogLog合并后的结果存储在另一个HyperLogLog中:pfmerge destkey sourcekey…

3.3 Geospatial经纬度

注意:Geospatial经纬度用于经纬度设置查询、范围查询、距离查询、经纬度Hash等操作

常用命令:

添加地理位置(经度、维度、名称):geoadd key longitude latitude member

获取指定地区的坐标值:geopos key member

获取两个位置之间的直线距离:geodist key member1 member2

以给定的经纬度为中心找出某一半径内的元素:georadius key longitude latitude radius

4. Redis的发布和订阅

概念:Redis发布订阅(pub/sub)是一种消息通信模式,发送者发送消息,订阅者接收信息,Redis客户端可以订阅任意数量的频道,当频道发布消息后消息就会推送给订阅者;

发布订阅实现:

  • 订阅频道:subscribe channel名

  • 推送内容:publish channel名 ’推送内容‘

5. Jedis操作Redis 6

Jedis使用步骤:

  1. 先在pom/xml引入jedis相关依赖!

  2. 创建Jedis对象:Jedis jedis = new Jedis(“访问redis所在主机ip地址”,端口号)
    (注意:必须要先修改redis.conf允许远程访问,以及linux防火墙放行端口号)

  3. 创建成功后即可使用redis-api的相关方法

模拟验证码发送步骤与要求:

  1. 输入手机号点击发送后随机生成6位数字码,
    **random.nextInt(10)循环生成6位随机数字符串**
    
  2. 验证码2分钟有效,
    **把验证码放到redis中,expire设置过期时间为120秒**
    
  3. 输入验证码点击验证判断是否一致,
    **从redis获取验证码和输入的验证码进行比较**
    
  4. 每个手机号每天只能输入3次;
    **第一次发送setnx设置发送次数是1,incr每次发送后+1,当大于3时提交不能发送**
    

6. Redis与Spring Boot整合

整合步骤:

  1. 先引入依赖spring-boot-starter-data-redis和commons-pool2

  2. 在application.properties中配置redis配置

    • spring.redis.host:redis服务器地址
    • spring.redis.port:redis服务器连接端口
    • spring.redis.database:redis数据库索引
    • spring.redis.timeout:连接超时时间
    • spring.redis.lettuce.pool.max-active:连接池最大连接数
    • spring.redis.lettuce.pool.max-wait:最大诸塞等待时间
    • spring.redis.lettuce.pool.max-idle:连接池中最大空闲连接数
    • spring.redis.lettuce.pool.min-idle:连接池中最小空闲连接数
  3. 创建Redis配置类RedisConfig

7. Redis的事务操作和锁

事务定义:Redis是一个单独的隔离操作,事务中的所有命令都会序列化并按顺序执行,事务在执行的过程中不会被其他客户端发送来的命令请求所打断(串联执行多个命令并防止别的命令插队

7.1 执行命令

  • 输入Multi命令:输入的命令依次进入命令队列中但不会执行;(组队阶段,组队过程可通过discard命令放弃组队)

  • 输入Exec命令:之前命令队列中的命令依次执行;(执行阶段)

7.2 事务错误处理

一、组队阶段出现错误:
组队阶段有任意某个命令出现错误,则所有组队的命令都不会被执行,进行回滚;

二、执行阶段出现错误:
执行阶段任意某个命令出现错误,则只有错误的命令不会被执行,而其他命令会继续执行,不会进行回滚;

7.3 Redis事务特性

  1. 单独的隔离操作:事务执行过程中不会被其他客户端发送来的命令请求所打断;

  2. 没有隔离级别概念:事务提交前任何指令都不会被实际执行;

  3. 不保证原子性:事务中有一条命令执行失败其他命令仍然会被执行,没有回滚;

7.4 乐观锁解决事务冲突问题

Watch key1, key2…:在执行Multi命令前,先执行watch key命令监视一个或多个key,如果在事务执行之前这些key先被其他命令所改动,那么事务将被打断

Unwatch:取消Watch命令对所有key的监视;

8. 日志持久化AOF与RDB

RDB(Redis DataBase)

含义:在指定的时间间隔内将内存中的数据集快照写入磁盘;

持久化流程:
Redis单独创建一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束后再用这个临时文件替换上次持久化好的文件;(写时复制技术)

注意:RDB最后一次持久化后的数据可能会丢失

AOF(Append Only File)

含义:以日志形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来,不记录读操作,只许追加文件但不可以改写文件,Redis启动后就会重新覆盖新文件,追加保存每次写操作到文件末尾

持久化流程:

  1. 客户端的请求写命令会被append追加到AOF缓冲区内,
  2. AOF缓冲区根据AOF持久化策略操作sync同步到磁盘的AOF文件中,
  3. 当AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量
  4. Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

注意:AOF默认不开启,可以在redis.conf中配置文件名称,默认为appendonly.aof,AOF文件保存路径和RDB的路径一致(当AOF和RDB同时开启时,系统默认读取AOF的数据,因为AOF的数据不会存在对视

9. 主从复制

主从模型

主从复制原理:

  1. 当从服务器连接上主服务器之后,从服务器向主服务器发送进行数据同步消息;

  2. 主服务器接到从服务器发送过来的同步消息后,把主服务器数据进行持久化生成rdb文件,把rdb文件发送给从服务器,从服务器拿到rdb文件后进行读取完成复制;

  3. 每次主服务器进行写操作后主动发起,会和从服务器进行数据同步;

主从复制步骤:

  1. 配置好多个从服务器redis,conf文件,分别redis -server redis.conf启动服务器

  2. 在从服务器客户端操作命令:slaveof 主服务器ip 主服务器端口,操作命令后即可形成一主多从结构

  3. 在主服务器输入命令:info replication即可查看连接的从服务器的具体数量;

哨兵模式

概念:能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库(原来的主机就算后来恢复正常也是作为原来从机的从库)

哨兵模式使用步骤:

  1. 新建sentinel.conf配置文件,在文件中输入内容:sentinel monitor 监控服务器名称 服务器端口号 1,1为至少有多少个哨兵同意才进行迁移的数量

  2. redis.sentinel /目录/sentinel.conf:启动哨兵

10. Redis集群

集群概念:Redis集群实现了对Redis的水平扩容,即启动N个Reids节点将整个数据库分布存储在这M个节点中,每个节点存储总数据的1/N;

11. Redis应用问题解决

缓存穿透

出现现象:

  1. redis命中率降低,导致一直查询数据库,应用服务器压力变大

出现原因:

  1. redis中查询不到,一直访问数据库
  2. 出现大量非正常url访问

解决方法:
(1)对空值缓存
(2)设置可访问的白名单(bitmaps定义可访问名单)
(3)采用布隆过滤器
(4)进行实时监控(设置黑名单)

缓存击穿

产生原因:redis中的某个key过期了,瞬时大量访问使用这个key

解决方法:
(1)预先设置热门数据到redis中,加大热门数据key的时长
(2)实时调整key的过期时长
(3)使用锁

缓存雪崩

产生原因:在极少时间段内,出现大量查询key的集中过期情况

解决方法:
(1)构建多级缓存架构:nginx缓存+redis缓存+其他缓存(ehcache等)
(2)使用锁或队列(可以使用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,但是不适用高并发情况)
(3)设置过期标志更新缓存(设置提前量,如果快过期就触发通知另外的线程在后台去更新实际key的缓存)
(4)把缓存失效时间分散开,随机数生成过期时间

实现分布式锁

Redis实现分布式锁的步骤:

  1. 使用setnx key value上锁,通过del key释放锁

  2. 锁如果一直没有释放则设置key过期时间
    上锁同时设置过期时间实现原子操作:set key value nx ex 过期时间