Redis

基础知识

基础操作

redis一共有16个数据库,默认使用第0个。

命令 注释
keys * 查看当前库所有key
flushdb 清空当前库所有key
FLUSHALL 清空所有库的所有key
select [number] 切换至n号数据库(一共有16个)
incr 自增1
decr 自减1
getrange key start end 截取字符串的子字符串
setrange key offset value 替换指定位置的字符串
setex key seconds value 设置过期时间
setnx key value 如果key不存在,创建key,如果存在,创建失败

redis是单线程的(面试知识)

Redis为什么单线程还这么快?

redis是将所有数据全部放在内存中的,所以用单线程去操作效率最高,多线程(CPU上下文会切换:耗时操作),对于内存系统来说,如果没有上下文切换效率就是最高!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案。


五大命令操作

redis的数据结构

  • redis存储的是:key,value核是的数据,其中key都是字符串,value有5种不同的数据结构
    • value的数据结构:
      • 字符串类型 string
      • 哈希类型 hash: map格式
      • 列表类型 list:linkedlist格式。支持重复元素
      • 集合类型 set:不允许重复元素
      • 有序集合类型 sortedset:不允许重复元素,且元素有顺序

字符串类型 string

  • 插入:
    • 设置单个值:set key value
    • 同时设置多个值:mset key value key value …
    • msetnx:是一个原子性操作,要么一起成功,要么一起失败
  • 获取:
    • 获取单个值:get key
    • 获取多个值:mget key key …
  • 删除:del key

列表类型 list

  • 插入:
    • 将一个或多个值放到列表的头部:lpush key value
    • 将一个或多个值放到列表的尾部:rpush key value
    • 将一个值插入到某元素的前或后:linsert key before|after pivot value
  • 获取:
    • 获取具体的值:lrange key start stop
    • 获取所有的值:lrange key 0 -1
    • 通过 index 获取值:lindex key index
    • 返回列表的长度:llen key
  • 删除:
    • 删除 list 的第一个元素:lpop key
    • 删除 list 的最后一个元素:rpop key
    • 删除指定元素:lrem key count value
    • 截取指定长度元素,删除其他未被截取元素:ltrim key start stop
  • 更新:
    • 更新指定元素:lset key index value
  • 组合命令: | 命令 | 注释 | | :—-: | :—-: | | rpoplpush source destination | 从source中删除最后一个元素,再把这个元素存放到destination的头部 | | | | | | |

集合类型 set(无序)

  • 插入:sadd key member
  • 获取:
    • 获取指定set的所有值:smembers key
    • 获取指定set的内容个数:scard key
    • 查询指定set的指定值是否存在:sismember key member
    • 随机抽取一个元素:srandmember key
    • 随机抽取多个元素:srandmember key number
  • 删除:
    • 删除指定元素:srem key member
    • 随机删除一个元素:spop key
  • 移动:
    • 移动指定的元素到另一个集合中:smove source destination member
  • 集合:
    • 差集:sdiff key1 key2
    • 交集(可实现共同好友):sinter key1 key2
    • 并集:sunion key1 key2

集合类型Zset(有序)

  • 插入:zadd key
  • 获取:

    • 获取所有元素:zrange key 0 -1
    • 获取全部元素,从大到小: zrevrange key 0 -1
    • 获取全部元素,从小到大,并附带值:(inf是无穷)

      1. 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
      2. 1) "xiaohong"
      3. 2) "2500"
      4. 3) "ysh"
      5. 4) "2500"
      6. 5) "zhangsan"
      7. 6) "5000"
    • 获取元素个数:zcard key

    • 获取指定区间的元素个数:zcount key min max
  • 删除:
    • 删除指定元素:zrem key member

哈希类型 hash

  • 存储:
    • 插入一个key-value:hset key field value
    • 插入多个key-value:hmset key field value [field value …]
  • 获取:
    • 获取一个字段值:hget key field
    • 获取多个字段值:hmget key field
    • 获取全部数据:hgetall key field
    • 获取字段数量:hlen key
    • 获取所有的key:hkeys key
    • 获取所有的value:hvals key
  • 删除:
    • 删除指定的key字段:hdel key field
  • 增减:
    • hincrby key field increment

Hyperloglog

什么是基数?

两个集合中不重复的元素称为基数

简介

优点:占用的内存是固定的,2^64不同的元素的技术,只需要花费12KB内存!如果要从内存角度来比较,Hyperloglog为首选。

作用:用于页面统计(0.81%错误率)

  • 存储:
    • pfadd key element
  • 获取:
    • 统计key中元素个数:pfcount key
  • 合并(并集):
    • PFMERGE destkey sourcekey

Bitmap

位存储

Bitmaps的值只有0和1,是二进制运算

作用:统计用户信息,活跃、不活跃!登录、未登录!打卡!

  • 存储:
    • setbit key offset value
  • 获取:
    • 获取key中指定元素的值:getbit key offset
    • 统计key中值为1的元素个数:bitcount key

事务

Redis事务本质:一组命令的集合,或者说是一个命令队列。一个事务中的所有命令都会被序列化,在执行事务的过程中,会按照顺序执行。

注意!

  • Redis事务没有隔离级别的概念
  • 所有的命令在事务中,并没有被直接执行,只有发起执行命令的时候才会执行
  • Redis单条命令是原子性的,但是事务不保证原子性

Redis的事务

  • 开启事务(multi)
  • 命令入列(……)
  • 执行事务(exec)
  • 取消事务(discrad)

正常执行事务

  1. 127.0.0.1:6379> multi
  2. OK
  3. 127.0.0.1:6379> set k1 v1
  4. QUEUED
  5. 127.0.0.1:6379> set k2 v2
  6. QUEUED
  7. 127.0.0.1:6379> set k3 v3
  8. QUEUED
  9. 127.0.0.1:6379> get k1
  10. QUEUED
  11. #127.0.0.1:6379> discrad - 取消事务
  12. 127.0.0.1:6379> exec
  13. 1) OK
  14. 2) OK
  15. 3) OK
  16. 4) "v1"
  17. 127.0.0.1:6379> keys *
  18. 1) "k3"
  19. 2) "k2"
  20. 3) "k1"

编译型异常

  1. 127.0.0.1:6379> multi
  2. OK
  3. 127.0.0.1:6379> set k1 v1
  4. QUEUED
  5. 127.0.0.1:6379> set k2 v2
  6. QUEUED
  7. 127.0.0.1:6379> getset k1 #错误的命令
  8. (error) ERR wrong number of arguments for 'getset' command #编译报错
  9. 127.0.0.1:6379> set k3 v3
  10. QUEUED
  11. 127.0.0.1:6379> set k4 v4
  12. QUEUED
  13. 127.0.0.1:6379> exec #执行报错
  14. (error) EXECABORT Transaction discarded because of previous errors.
  15. 127.0.0.1:6379> keys * #事务直接失败,其他语句也不能正常执行
  16. (empty list or set)

运行时异常

  1. 127.0.0.1:6379> set k1 "v1"
  2. OK
  3. 127.0.0.1:6379> get k1
  4. "v1"
  5. 127.0.0.1:6379> multi
  6. OK
  7. 127.0.0.1:6379> incr k1 #命令正确,语法错误
  8. QUEUED
  9. 127.0.0.1:6379> set k2 v2
  10. QUEUED
  11. 127.0.0.1:6379> set k3 v3
  12. QUEUED
  13. 127.0.0.1:6379> get k3
  14. QUEUED
  15. 127.0.0.1:6379> exec
  16. 1) (error) ERR value is not an integer or out of range #报错
  17. 2) OK #其他语句正常执行
  18. 3) OK
  19. 4) "v3"
  20. 127.0.0.1:6379> keys *
  21. 1) "k3"
  22. 2) "k2"
  23. 3) "k1"

乐观锁与悲观锁

监控watch(面试常问)

悲观锁:

  • 很悲观,认为什么时候都会出问题,无论做什么都会加锁

乐观锁:

  • 很乐观,认为什么时候都不会出问题,所以不会上锁更新数据的时候去判断一下,在此期间是否有人修改过数据
  • 获取version
  • 更新的时候比较version
  • 开启监控:watch key
  • 关闭监控:unwatch key

Jedis

使用Java来操作Redis

  1. <!-- jedis -->
  2. <dependency>
  3. <groupId>redis.clients</groupId>
  4. <artifactId>jedis</artifactId>
  5. <version>3.4.1</version>
  6. </dependency>

SpringBoot整合

说明:在SpringBoot2.x之后,原来使用的jedis被替换成了lettuce

  • jedis:采用直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池(BIO)
  • lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据(NIO)

源码分析

  1. @Bean
  2. @ConditionalOnMissingBean(name = "redisTemplate") //可以自己定义redisTemplate来替换这个默认的
  3. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
  4. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  5. //默认的RedisTemplate没有过多的设置,redis对象都是需要序列化的
  6. //两个泛型都是Object,需要强转
  7. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  8. template.setConnectionFactory(redisConnectionFactory);
  9. return template;
  10. }
  11. @Bean
  12. @ConditionalOnMissingBean //由于String是redis最常使用的类型,所以单独提出来了一个bean
  13. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
  14. public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
  15. StringRedisTemplate template = new StringRedisTemplate();
  16. template.setConnectionFactory(redisConnectionFactory);
  17. return template;
  18. }

自定义RedisTemplate

  1. @Configuration
  2. public class RedisConfig {
  3. /**
  4. * 自定义redisTemplate
  5. * @param redisConnectionFactory
  6. * @return
  7. */
  8. @Bean
  9. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  10. // 为了方便开发,一般直接使用<String, Object>
  11. RedisTemplate<String, Object> template = new RedisTemplate<>();
  12. template.setConnectionFactory(redisConnectionFactory);
  13. // Json序列化配置
  14. Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
  15. ObjectMapper objectMapper = new ObjectMapper();
  16. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  17. objectMapper.activateDefaultTyping(
  18. LaissezFaireSubTypeValidator.instance ,
  19. ObjectMapper.DefaultTyping.NON_FINAL,
  20. JsonTypeInfo.As.WRAPPER_ARRAY);
  21. objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  22. // String的序列化
  23. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  24. // key采用String的序列化方式
  25. template.setKeySerializer(stringRedisSerializer);
  26. // hash的key采用String的序列化方式
  27. template.setHashKeySerializer(stringRedisSerializer);
  28. // value序列化方式采用jackson
  29. template.setValueSerializer(objectJackson2JsonRedisSerializer);
  30. // hash的value序列化方式采用jackson
  31. template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
  32. template.afterPropertiesSet();
  33. return template;
  34. }
  35. }

Redis.conf

配置详解

单位

image-20210105153508899.png

网络

  1. bind 127.0.0.1 # 绑定的ip
  2. protected-mode yes # 保护模式
  3. port 6379 # 端口设置

设置redis密码

注意:redis密码默认为空

  1. 127.0.0.1:6379> config set requirepass "123456" # 设置密码
  2. OK
  3. 127.0.0.1:6379> auth 123456 # 登录
  4. OK

Redis持久化

  1. Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。(断电即失)

RDB(Redis DataBase)

什么是RDB

在主从复制中,rdb就是备用的,在从机上使用rdb
image-20210105160240638.png

RDB保存的是dump.rdb文件

触发机制

  • save的规则满足的情况下,会自动触发rdb规则
  • 执行flushall命令,也会触发rdb规则
  • 退出redis,也会产生rdb文件

备份就自动生成一个dump.rdb
image-20210105160716782.png

如何恢复rdb文件

  • 只需要将rdb备份文件放在redis启动目录即可,redis启动时会自动检查dump.rdb恢复其中的数据
  • 查看需要存在的位置
    1. 127.0.0.1:6379> config get dir
    2. 1) "dir"
    3. 2) "/usr/local/bin"

优缺点

优点:

  • 适合大规模的数据恢复
  • 对数据的完整性要求不高

缺点:

  • 需要一定时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有了
  • fork进程,会占用一定的空间

AOF(Append Only File)

什么是AOF

image-20210105165424648.png
以日志的形式来记录每个写操作,将Redis执行过的所有写操作指令记录下来,只许追加文件但不可以改写文件,redis启动之时会读取该文件重新构建数据,即redis重启的话会根据这个日志文件的内容将指令从前到后执行一次,以完成数据的恢复工作。

AOF保存的是appendonly.aof文件

修复aof文件

  • 如果这个aof文件有错误,redis是启动不起来的,我们需要修复这个文件
  • redis给我们提供了一个工具:redis-check-aof —fix 文件名

重写

  1. no-appendfsync-on-rewrite no # 默认是关闭
  2. auto-aof-rewrite-percentage 100
  3. auto-aof-rewrite-min-size 64mb # 如果aof文件大于64mb,就会fork一个新的进程来重写文件

优缺点

  1. appendonly no # 默认是不开启aof模式,默认是使用rdb
  2. appendfilename "appendonly.aof" # aof模式下的文件名
  3. # appendfsync always # 每次修改都会sync(加锁)
  4. appendfsync everysec # 每秒执行一次sync,可能会丢失这1s的数据
  5. # appendfsync no # 不执行sync,这个时候操作系统自己同步数据,效率最高

优点:

  • 每一次修改都同步,文件的完整性会更好
  • 默认开启每秒同步一次,可能会丢失一秒的数据
  • 从不同步,效率最高

缺点:

  • 相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
  • aof运行效率也要比rdb慢,所以redis默认配置就是rdb持久化

Redis发布订阅

命令

这些命令被广泛用于构建即时通信应用,比如聊天室和实时广播、实时提醒等
image-20210108151715771.png


Redis主从复制

概念

  1. 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);**数据的复制时单向的,只能由主节点到从节点**。Master以写为主,Slave以读为主。
  2. 默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用主要包括:

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器压力;尤其在写少读多的场景下,通过多个节点分担读压力,可以大大提高Redis服务器的并发量。
  4. 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础。

image-20210108152541793.png
主从复制,读写分离。80%的情况下都是在进行读操作!可以减缓服务器的压力,架构中经常使用,一主二从!

环境配置