Redis学习

107 阅读16分钟

Redis(Remote Dictionary Server) 远程字典服务

redis概念介绍

功能

  • 内存存储和持久化,内存是断电即失的,持久化很重要(RDB和AOF)

  • 效率高,可用于高速缓存

  • 发布订阅系统

  • 地图信息分析

  • 计时器和计数器(浏览量)

特性

  • 多样的数据类型

  • 持久化

  • 集群

  • 事务

安装相关的知识

redis的默认安装路径:/usr/local/bin

image.png

redis配置文件所在的路径:/usr/local/etc

基本命令初了解

  • 启动服务:redis-server (可用特定的配置文件启动)

  • 查看redis正在运行的进程:ps -ef|grep redis

image.png

  • 断开redis服务:redis-cli shutdown

断开redis服务:在redis命令行执行

image.png

  • 连接redis: redis-cli -h 127.0.0.1 -p 6379

  • 存入键值对:set key value

  • 根据可以获取值: get key

  • 获取所有的键: keys *

redis-benchmark是redis自带的压力测试工具

redis基本知识介绍

数据库个数:16个,可在命令行使用select切换数据库(默认是第一个数据库)

image.png

DBSIZE:查看当前数据库的大小

flushdb:清空当前的库

flushall:清除全部的数据库

redis是单线程的,redis的瓶颈是内存和网络带宽

redis使用单线程为什么还如此之快?

误区1:高性能的服务器一定使用的是多线程 误区2:多线程(CPU上下文会切换!!!)一定比单线程效率高 核心:redis是将所有的数据存入到内存中,对于内存系统来说,没有上下文切换,多次读写都在一个CPU,在内存系统中这个就是最佳的

redisKey的基本命令

基本的数据类型:String、List、Set、Hash、zset

redis key的介绍

set key value ##存入键值对
keys * ##查看所有的键
EXISTS key ##查看key这个键是否存在
move key 1 ##移除键
expire key time ##设置过期时间(time的单位为秒)
ttl key##查看key剩余的过期时间
type key##查看key所对应的值是什么类型

string类型介绍

value可以是字符串也可以是数字(可用于统计数量、浏览量)

APPEND key value  #向key中的值添加value,若key不存在则相当于set key
STRLEN key  #查看key对应value值的字符串长度
INCR key #key对应的值加1(播放量可以用redis实现)
DECR key #key对应的值减1
INCRBY key num #key对应的值一次性增加num
DECRBY key num #key对应的值一次性减少num
GETRANGE key 0 num #截取key对应的值的字符串从0到num
GETRANGE key 0 -1 #返回key对应的全部字符串
SETRANGE key num value #对key对应的value从下标num进行替换,只替换value个数的字符
setex key expireTime value #设置key value 和过期时间
setnx key value #(set not exist)不存在设置
mset k1 v1 k2 v2 k3 v3 #设置多个key value,原子性的操作
mget k1 k2 k3 #获取多个key的值,原子性的操作
mset user:1:name zhangsan user:1:age 3 #设置user的1对象的姓名和年龄
mget user:1:name user:1:age #获取设置的属性值
set user:2 {name:lisi,age:4} #设置user2的对象的name和age
set user:{id}:{fields} #巧妙的设置key
getset key value #第一次返回已有key的值(没有则为null),再将已有key的值改为现有的value,再次获取就是现有的value

List类型介绍

List可作为栈和队列 所有的List命令都是以L开头的 list相当于链表:before after left right都可以插入值 两边插入和改动值效率高 中间操作效率会相对低一点 使用场景:消息队列

LPUSH key value #将一个或者多个值插入列表的头部
LRANGE key startIndex endIndex #根据下标获取list的值
RPUSH key value #将一个或者多个值插入列表的尾部
LPOP key #从左边溢出列表中的元素
RPOP key #从右边移除列表中的元素
Lindex key index #获取list的index下标的值
LLen key #获取listName的长度
lrem key removeNum  removeValue #移除list中removeNum个removeValue
ltrim key startIndex endIndex #根据下标截取list
RPOPLPUSH key key1 #将list从右边移除一个元素,从左边插入list1
EXISTS key #判断数组是否存在
lset key index value #在数组index下标存在的情况下,将index下标的值改为value
Linsert key before value newValue #z在list的value元素前面插入newValue
Linsert key after value newValue #z在list的value元素后面插入newValue

set类型的介绍

set中的值不能重复

sadd key value #在集合中插入 key-value
SMEMBERS key #查看集合中的元素
SISMEMBER key value #查看value是否在set中存在
scard key #获取元素中的值
SRANDMEMBER key num #随机抽选指定个数的元素
SPOP key #移除key对应的一个元素
SMOVE key key1 value #移除key的value值到key1
sdiff key1 key2 #key1相比与key2的差集
SINTER key1 key2 #key1和key2的交集
SUNION key1 key2 #key1和key2的并集

hash类型的介绍

Map集合[key-value(key-value)] 存储变更的数据(用户信息的保存) 适合对象的存储

hset key valueKey value #在hash中存入key和value
hget key valueKey #获取key对应的键值对中key为keyvalue的
hmset key valueKey value #在hash中存入key和value
hmget key valueKey #获取key对应的键值对中key为keyvalue的
HGETALL key #获取key对应下全部的键值对
hdel key valueKey #删除key下面对应的value中key为valueKey的键值对
hlen key #获取key对应的键值对
hexists key valueKey #判断key对应下的键值对的key为valueKey的是否存在
hkeys key #获取所有的key
hvals key #获取所有的值
HINCRBY key field increment #field对应的值自增increment

zset类型的介绍

有序集合,在set的基础上增加了一个值 有序集合可以做排行榜

ZADD key score member #增加key-value并根据score排序
ZRANGE key min max #根据下标获取set的值
ZRANGEBYSCORE key min max #根据成绩大小正序排列
ZRANGEBYSCORE key min max WITHSCORES #根据成绩大小正序排序并将score打印出来
zrem key member #删除key对应下的值
ZCARD key #判断key下值的个数
ZRANGE key 0 -1 #根据score从大到小排序
zcount key min max #score在min到max之间总共有几个元素

redis事务相关

相关概念

redis的单条命令是保证原子性的,redis的事务不保证原子性 redis事务的本质:一组命令的集合,事务所有的命令都会被序列化,事务会按照顺序执行。一次性、顺序性、排他性 redis事务没有隔离的概念,所有命令在事务中,没有被直接执行,只有发起执行命令的时候才会被执行。Exec

开启事务的步骤

  • 开启事务(MULTI)
  • 命令入队()
  • 执行事务(exec) 放弃事务:discard

异常

编译型异常(代码错误)---事务中的命令都不会被执行

运行时异常(1/0 语法错误)---事务中除了运行错误的代码都会被执行,错误的命令会抛出异常

乐观锁

悲观锁:无论做什么都去加锁

乐观锁:在更新的时候判断数据是否被更新,用version判断(MVCC)1.获取version 2.更新的时候判断version是否改变

watch key :在事务期间,如果事务没有提交,key的值发生变化,事务提交不会成功(这是一个乐观锁,在监控key之后,当提交事务,会比较key的值在监控前后是否改变,如果改变,事务的提交就不会成功)

jedis

使用java操作redis

jedis是使用java操作redis的中间件

springboot集成redis

说明:在spring2.X中,原来使用的Jedis被替换为lettuce jedis:采用的是直连,多个线程访问则不安全,可以使用jedis的连接池 lettuce:采用的是netty,实例可以在多个线程中共享,不存在线程不安全的问题

redis涉及的配置类: RedisAutoConfiguration

image.png 上图:当项目中不存在redisTemplate,则RedisTemolate生效 源码分析:

@Bean
@ConditionalOnMissingBean(name = "redisTemplate") //可以自己写一个redisTemplate可以替换默认的redisTemplate
//redis对象是需要序列化的
//参数的类型是Object,所以必须要强制类型转换
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
      throws UnknownHostException {
   RedisTemplate<Object, Object> template = new RedisTemplate<>();
   template.setConnectionFactory(redisConnectionFactory);
   return template;
}

@Bean
@ConditionalOnMissingBean
//redis最常用的是string类型,所以为string类型单读提出来一个方法
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
      throws UnknownHostException {
   StringRedisTemplate template = new StringRedisTemplate();
   template.setConnectionFactory(redisConnectionFactory);
   return template;
}

redis的连接池 image.png 整合的步骤:

  1. 导入依赖
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
compile 'org.redisson:redisson-spring-data-21:3.15.3'
implementation 'org.redisson:redisson-spring-boot-starter:3.15.3'
  1. 配置文件
redis:
  host: ${SPRING_REDIS_HOST:127.0.0.1}
  port: ${SPRING_REDIS_PORT:6379}
  timeout: ${SPRING_REDIS_TIMEOUT:3s}
  1. 测试
@SpringBootTest
public class RedisTest {
    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void testRedisConnect() {
        redisTemplate.opsForValue().set("myKey", "123");
        System.out.println(redisTemplate.opsForValue().get("test"));
    }
}

redis对象的序列化

image.png

默认是用JDK进行序列话,可以自己定义一个配置类,使用json进行序列化 自己定义一个RedisTemplate

举例说明配置序列化的方式:

设置key的序列化,会提供不同的序列化方式:默认的是jdk的序列化

image.png

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setStringSerializer(stringSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        return template;
    }

}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Object> template = new RedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    //json的序列化配置
    Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new      Jackson2JsonRedisSerializer<>(Object.class);
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    objectJackson2JsonRedisSerializer.setObjectMapper(om);
    template.setKeySerializer(objectJackson2JsonRedisSerializer);
    //String的序列化配置
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    template.setKeySerializer(stringRedisSerializer);
    template.setValueSerializer(objectJackson2JsonRedisSerializer);
    template.afterPropertiesSet();
    return template;
}

对象必须先序列化

Redis 配置文件详解

文件位置:/usr/local/etc

单位

image.png

包含

image.png

相当于spring 的import

network

 ##绑定的IP
bind 127.0.0.1 ::1 
##受保护的模式
protected-mode yes  
##端口号
port 6379         

通用 GENERAL

##如果是yes则以守护进程的方式运行
daemonize no 

##如果以守护进程执行,则必须指定一个进程文件
pidfile /var/run/redis_6379.pid 

## debug (a lot of information, useful for development/testing)

## verbose (many rarely useful info, but not a mess like the debug level)

## notice (moderately verbose, what you want in production probably)

## warning (only very important / critical messages are logged)

loglevel notice

##日志生成的文件名
logfile "" 

##数据库的数量
databases 16  

##是否总是显示启动logo
always-show-logo no  

快照 SNAPSHOTTING

持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb文件 .aof文件 redis是内存数据库,没有持久化,就会断电即失

## 在3600内,至少有一个key修改就进行持久化
save 3600 1  

## 如果300s内,至少100个key进行了修改,进行持久化操作
save 300 100

## 如果60s内,至少10000个key进行了修改,进行持久化操作
save 60 10000

## 持久化出现错误是否继续工作
stop-writes-on-bgsave-error yes

##是否压缩rdb文件,需要消耗cpu的资源
rdbcompression yes

## 保存rdb文件事,进行错误的校验检查
rdbchecksum yes

##rdb文件保存的目录
dir /usr/local/var/db/redis/

REPLICATION 主从复制

可在从机的配置文件中配置 image.png

SECURITY 安全

image.png

如上图所示:默认的密码是空的

设置密码

image.png

验证密码

image.png

CLIENTS 客户端

##设置连接redis服务器的客户端的个数,默认是10000个
maxclients 10000

##redis设置的最大内存配置
maxmemory <bytes>

##内存达到上限的处理策略
maxmemory-policy noeviction

APPEND ONLY MODE aof的配置

##默认不开启aof,默认是使用rdb持久化的
appendonly no 

##持久化文件的名字
appendfilename "appendonly.aof"

## 每次修改就会同步 ,耗费内存
# appendfsync always

## 每秒执行一次,可能丢失1秒的数据
appendfsync everysec

## 不同步
# appendfsync no

Redis 持久化

Redis是内存数据库,如果不将内存的数据保存到磁盘,一旦服务器的进程退出,服务器的数据库状态会消失,所以Redis提供了持久化功能。

RDB(Redis DataBase)

rdb保存的文件都是dump.rdb,在配置文件的快照中进行配置

举一个小例子 在redis的配置文件中做配置

image.png

image.png

删除已有的dump.rdb

image.png

存入键值对,断开数据库,重新连接获取值

image.png

image.png

触发机制

  1. save规则满足的情况下,会自动触发rdb规则
  2. 执行flushdb,触发rdb规则
  3. 退出redis,触发rdb规则 备份会自动生成dump.rdb

恢复rdb文件 将dump.rdb放在redis的启动目录下,redis启动时会自动检查dump.rdb恢复其中的数据

通过

config get dir #获取redis的启动目录

image.png

总结 优点:

  1. 适合大规模的数据恢复
  2. 对数据的完整性要求不高 缺点:
  3. 需要一定的时间间隔,意外宕机,最后一次的修改就丢失
  4. 会占用一定的内存空间

工作原理

在指定的时间间隔内将内存中的数据集快照写入磁盘,即Snapshot快照,恢复是将快照直接读到内存中

Redis会单独fork一个子进程进行持久化,将数据写入一个临时的文件中,持久化过程结束后,用临时文件替换上次持久化好的文件。在上述的过程中,主进程不进行任何的IO操作,确保了极高的性能。

AOF(Append Only File)

将所有的命令记录下来,history,恢复时就是整个文件全部执行一遍 已日志的形式记录每个写操作,将Redis执行过程中所有的写操作记录下来,只能追加文件但是不能改写文件,redis执行之初会读取该文件重新构建数据,即根据日志文件将指令从前到后全部执行一遍以完成数据恢复的工作。

aof保存的文件是:appendonly.aof

image.png

默认是不开启的,需要手动进行配置,将appendonly改为yes就开启的aof

举例

配置文件的配置:

image.png

操作:放入键值对->查看持久化文件

image.png

image.png

aof文件出现问题,redis不能启动,必须进行修复,redis提供了工具:redis-check-aof --fix

redis-check-aof --fix appendonly.aof #修复命令

image.png

总结

优点

  1. 每一次修改会同步,文件的完整性比较好
  2. 每秒同步一次,可能会丢失一秒的数据
  3. 从不同步,效率最高 缺点
  4. 相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
  5. aof的运行速率比rdb慢,redis默认使用的是rdb

Redis的发布订阅

简单的小例子

订阅与发布

SUBSCRIBE channel #订阅通道
PUBLISH channel message #在通道发布消息

image.png

主从复制

概念

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器,前者称为主节点(master/leader),后者称为从节点(slave/follower).数据的复制是单向的,只能从主节点到从节点。Master以写为主,Slave以读为主。

一个主节点可以有多个从节点,一个从节点只能有一个主节点

默认一台服务器就是一个主节点

作用

数据冗余:主从实现了数据的热备份,是持久化之外的一种数据冗余的方法

故障恢复:当主节点发生故障,可以由从节点提供服务,实现快速的故障恢复;实际是服务的冗余

负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务,分担服务器的负载;在写少读多的场景下,通过多个从节点分担负载,可以大大提高服务器的并发量

高可用(集群)的基石:是哨兵和集群能够实施的基础(架构的最低配置是一主二从)

命令

查看当前库的信息

role:master #角色 master

connected_slaves:0 #没有从机

master_failover_state:no-failover

master_replid:0d7d5378ff7e4f44d742d244b6118708c443e7df

master_replid2:0000000000000000000000000000000000000000

master_repl_offset:0

second_repl_offset:-1

repl_backlog_active:0

repl_backlog_size:1048576

repl_backlog_first_byte_offset:0

repl_backlog_histlen:0

image.png

模拟集群(单机多集群)

复制3个配置文件,修改对应的信息,包括端口号、pid的名字、log文件的名字、rdbwen文件的名字

启动3个服务,通过进程信息查看

配置从机

 slaveof 127.0.0.1 6379 #认主机为127.0.0.1,端口号为6379的为主机

注意:真实的配置应该在配置文件中配置,用命令的配置都是暂时的

细节

主机可以写,从机只能读,主机的所有信息和数据都会更新到从机

主机断开,从机依旧可以连接到主机,但是没有写操作,主机再次回来,从机依旧可以获取主机写的信息

如果使用命令行配置的主从,从机重启就会变回主机,再次用命令变回从机,数据立马补全更新

复制原理

Slave启动成功连接到master后会发送一个sync命令

Master接到命令,启动后台的存盘进程,并收集所有接收到的用于修改数据集的命令,在后台进程执行完成之后,master传送整个数据文件到slave,完成一次的完全同步

全量复制:slave服务接收到数据库文件之后,将其存盘并加载到内存中

增量复制:master继续将新的收集到的修改命令依次传给slave,完成同步

只要重新连接到master,一次完全同步将被自动执行

层层链路

上一个M连接下一个S

M->S(M)->S

谋朝篡位

如果主机断开,通过执行下面的命令,重新选择一个主机,其他节点手动连接带新的主节点,之前断开的主机修复了,也没有用,必须重新连接

slaveof no one

哨兵模式

sentine

监控后台的主机是否故障,如果故障了会根据投票的数量自动将从库转换为主库

哨兵模式的原理是:哨兵是一个独立的进程,独立运行,原理是:通过发送一个命令,等待redis服务器的响应,从而监控运行多个redis实例

哨兵模式的配置组件

image.png

  1. 配置哨兵的配置文件(sentinel)
#sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1 # 1代表主机挂了,slave投票看谁可以接替主机,票数最多的就会成为主机
  1. 启动哨兵
redis-sentinel kconfig/sentinel.conf

failover(关键词---故障转移) 如果挂掉的主机再次恢复只能作为从机

优缺点

优点: 1.基于主从复制的模式

2.主从可以切换,故障可以转移、系统的可用性非常好

3.哨兵模式是主从复制的升级版本,从手动到自动,更加智能化

缺点:

在线扩容不方便

实现哨兵模式的配置非常麻烦

穿透与雪崩

查询操作:客户端的查询先到缓存查找,若没有在去数据库中查询

穿透(查不到):查询的内容在缓存和数据库中都没有,但是查询的访问量较大最后数据库宕机 解决方法:步隆过滤器(请求先经过过滤器)/缓存空对象(数据库没有则在缓存中存放一个空对象)

击穿(查询量太大,缓存过期):一个key非常热点(每个key有自己的过期时间,假设一个key的过期时间是60s,当这个key刚好过期的时候,这是有大批量的访问,就会击穿数据库的底层架构,使得宕机)

解决方法:key不过期/分布式锁

雪崩:缓存集体失效/redis服务器宕机

解决方法:

数据预热/限流降级