Redis自学笔记整理
Redis 笔记
一、基础知识
-
redis不区分大小写命令
-
默认16个数据库,切换命令使用
select
,下标0开始,最大15 -
DBSIZE 查看目前数据库容量
-
EXISTS 查看key name 是否存在
-
FLUSHDB 清空当前数据库
-
FLUSHALL 清空全部数据库
-
设置账号密码config set requirepass "123456",获取密码config get requirepass
-
Redis所有的操作都是原子性的,是单线程的。Redis是将所有数据全部放在内存中的,所以说使用单线程操作效率是最高的,多线程(CPU上下文会切换,耗时操作),对应内存系统来说,如果没有上下文切换,效率是最高,多次读写都在同一个CPU下
二、数据类型
- String (字符串)
- get 、set、mget、mset、append、strlen
- 自增incr、自减decr, 设置指定值incrby key 10
- 设置数据具有指定的生命周期setex、psetex 例如:setex temp 30 "test" 重复执行该语句可刷新时间。ttl命令:查看存活时间,-2表示不存在 ,-1表示永久存在。
- setnx 不存在时创建==(set if not exists) 当返回值为0 创建失败
- 字符串截取 getrange [ getrange name 0 4 ]字符串替换 setrange **[ **setrange name 1 test ]
- getset 先get 后set
- List (一般应用于具有操作先后顺序的数据控制)
- 左边添加/修改数据lpush 获取数据lrange 获取全部值 [ lrange list1 0 -1 ]
- 通过下标获取lindex 和长度llen
- 右边添加rpush
- 获取并移除数据lpop rpop
- 移除指定数据lrem,相当于remove的意思,移除找到的第一个one**[ **lrem list 1 one ]
- 通过下标截取ltirm, [ ltrim mylist 1 2 ]
- 将列表中指定下标的值更新掉lset, [ lset list 2 test ]
- 往指定字段前插值linsert [ linsert list before "hello1" "hello3" ] before 还可以after
- 应用场景 :消息排队、消息队列(Lpush,Rpop),栈(Lpush Lpop)
- sadd 添加 、smembers查看、srem删除、srandmember num随机获取num个、spop随机删除、smove向其他集合移动
- sdiff 差集、sinsert 交集,sunion 并集
- scrd 获取集合大小 sismember 集合中是否包含指定数据
- Zset (有序集合)
- 添加zadd、zrange获取、zrem删除
- 排序后按条件 zrangebyscore key min max [WITHSCORES] [LIMIT]
- zrevrangebyscore 降序排序
- 获取数据对应的索引(排名)zrank 、zrevrank
- zscore获取成员在排序设置相关比分
- zcard查询个数 zcount获取指定区间的个数
- zinterstore交集 、zunionstore并集
- Hash (哈希、散列)
- 添加hset 、获取hget和hgetall、批量mhset和mhget、删除hdel
- 获取大小hlen、是否存在hexists
- 获取所有键hkeys 、获取所有值 hvals
- 特殊数据类型
- 地理位置geospatial
- 基数统计Hyperloglog
- 位存储bitmaps,有setbit、getbit、bitcount
三、通用指令
补充:
- persist key 切换key从时效性转换为永久性
- sort 对所有key排序
- help @generic 查询所有通用操作
- echo message 打印信息
四、Jedis
- 添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
2.测试
public class TestPing {
public static void main(String[] args) {
// 1、new Jedis 对象即可
Jedis jedis = new Jedis();
}
}
3.了解事务
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 开启事务
Transaction multi = jedis.multi();
try {
multi.set("user1", "xiaoming");
multi.set("user2", "xiaohong");
multi.exec();
}catch (Exception e){
e.printStackTrace();
multi.discard(); // 放弃事务 回滚
}finally {
jedis.close(); // 关闭连接
}
}
五、Spring-data-redis
spring-data-redis 针对 jedis 提供了如下功能:
-
连接池自动管理,提供了一个封装的RedisTemplate类 。
-
针对 jedis 中api 进行了归类封装,将同一类型操作封装为 operation 接 口
- ValueOperations:简单 K-V 操作
-
SetOperations:set 类型数据操作
-
ZSetOperations:zset 类型数据操作
-
HashOperations:针对 map 类型的数据操作
-
ListOperations:针对 list 类型的数据操作
但是,在SpringBoot2.X之后,jedis被lettuce替换了,采用netty,更像NIO模式
- 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- yml配置
spring:
redis:
host: 127.0.0.1
port: 6379
- 自定义Bean
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
- 使用
wupublic class RedisTest implements ApplicationRunner {
@Autowired
private RedisTemplate redisTemplate;
@Override
public void run(ApplicationArguments args) throws Exception {
log.error((String) redisTemplate.opsForValue().get("user1"));
}
}
六、持久化
1.什么是持久化,为什么持久化?
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。
防止数据的意外丢失,确保数据安全性
2.RDB(Redis DataBase)(关注点在数据)
启动方式:
- sava 指令 (手动执行一次保存操作)。但执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用。
- save second changes second :监控时间范围 changes: 监控key的变化量
- bgsave指令。手动启动后台保存操作,但不是立即执行,是针对save阻塞问题做的优化。Redis内部涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用。
- 特殊启动:全量复制;debug reload 服务器运行过程中重启;shutdown save 关闭服务器时指定保存数据,默认情况下执行shutdown命令时,自动执行bgsave(如果没有开启AOF持久化功能)
相关配置:
dbfilename dump.rdb #rdb文件命名为dump.rdb
dir ./ #保存到当前目录下
rdbcompression yes #是否压缩.rdb文件(持久化文件
rdbchecksum yes #保存rdb文件的时候,进行错误的检查校验!
stop-writes-on-bgsave-error yes #后台存储过程中如果出现错误现象,是否停止保存操作,通常默认为开启状态
不足:存储数据量较大,效率较低 ;基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低;大数据量下的IO性能较低 基于fork创建子进程,内存产生额外消耗 ;宕机带来的数据丢失风险。
3.(Append Only File)(关注点在数据的操作过程)
将我们的所有命令(日志)都记录下来,恢复的时候就把这个文件全部执行一遍
三种策略:always(每次)(性能低),everysec(每秒),no(系统控制)(不可控)
配置:
appendonly yes|no #功能开启关闭,默认关闭
appendfsync always|everysec|no #AOF写数据策略
appendfilename filename #文件名
dir #文件路径
redis-check-aof --fix appendonly.aof #检查修复AOF文件
AOF重写:解决AOF文件太大。就是将对同一个数据的若干个条命令执行结果转化成最终结果数据对应的指令进行记录。
bgrewriteaof #手动重写
auto-aof-rewrite-min-size size #自动重写
auto-aof-rewrite-percentage percentage #自动重写
不足:数据文件 aof远大于rdb,修复速度比rdb慢 ; aof启动慢,运行效率也比rdb慢;两者:
选择:1.如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF。如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB。
2.双保险策略,同时开启 RDB 和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据的量。
七、事务
事务:一个队列中,一次性、顺序性、排他性的执行一系列命令
multi #开启事务
set k1 v1 #入队
set k2 v2 #入队
exec #执行事务
discard #取消事务
两种情况:
- 编译错误,一般指语法错误。如果定义的事务中所包含的命令存在语法错误,整体事务中所有命令均不会执行。
- 运行错误,命令格式正确,但是无法正确的执行。能够正确运行的命令会执行,运行错误的命令不会被执行。
八、锁
1.监视锁watch
watch key1 [key2……] #对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行
unwatch #取消对所有 key 的监视(解锁)
2.分布式锁
setnx lock-key value #根据返回值特征,有值则返回设置失败,无值则返回设置成功
#对于返回设置成功的,拥有控制权,进行操作
#对于返回设置失败的,不具有控制权,排队或等待
del #释放锁
九、删除策略
Redis是一种内存数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
- XX :具有时效性的数据
- -1 :永久有效的数据
- -2 :已经过期的数据 或 被删除的数据 或 未定义的数据
过期的数据真的删除了吗?
-
定时删除
创建一个定时器,由定时器任务执行对过期时间到达的键的删除操作
-
惰性删除
数据到达过期时间,不做处理。等下次访问该数据时,如果未过期,返回数据,发现已过期,删除,返回不存在
-
定期删除
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
比较:
当新数据进入redis时,如果内存不足怎么办?
影响数据逐出的相关配置
maxmemory #最大可使用内存
maxmemory-samples #每次选取待删除数据的个数
maxmemory-policy #删除策略
注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息
逐出算法:
- 检测易失数据 4 种
- 检测全库数据 3种
- 放弃数据驱逐no-enviction,会引发错误OOM(Out Of Memory)
十、主从复制
主从复制,是指将主节点(master/leader)的数据,复制到从节点(slave/follower),
特征:
- 数据的复制是单向的。
- Master以写为主,Slave以读为主。
- 一个master可以拥有多个slave,一个slave只对应一个master
作用:
- 读写分离
- 负载均衡,提高Redis服务器并发量与数据吞吐量
- 故障恢复
- 数据冗余(数据热备份)
- 高可用:基于主从复制,构建哨兵模式与集群,实现Redis的高可用
工作流程:
大致分为三个阶段:
环境准备:一台电脑的话,复制修改conf,用端口多开几个客户端,必须修改点如下,如设置端口6378
pidfile /var/run/redis6378.pid # 修改进程ID
logfile "6378.log" # 修改日志
port 6378 # 修改端口
dbfilename dump6378.rdb # 修改rdb持久化
搭建一主二从
- 建立连接阶段:建立slave到master的连接,使master能够识别slave,并保存slave端口号
slaveof <masterip> <masterport> #slaveof 认老大
127.0.0.1:6378> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379 # 信息查看 已认老大 主机信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6378,state=online,offset=253,lag=1 # 6378小第
slave1:ip=127.0.0.1,port=6377,state=online,offset=253,lag=1 # 6377小弟
master_repl_offset:267
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:266
-
数据同步阶段
slave启动成功连接到master后会发送一个sync命令
全量复制:master 发给slave服务RDB文件,清空数据,执行RDB文件恢复过程,将其存盘并加载到内存中。
增量复制:master 发送复制缓冲区信息给slave,执行bgrewriteaof,恢复数据的过程
-
命令传播阶段
十一、哨兵模式
手动谋朝篡位 slaveof no one 使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据不会被丢弃。
提出
当主服务器宕机后,需要手动把一台服务器切换为主服务器,这需要人工干预,费时费力。需要一个谋朝篡位自动版。
哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
哨兵工作原理分三个阶段:
-
监控。检查master和slave是否正常运行
-
通知(提醒)。当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
-
自动故障转移。断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服
务器地址
redis-sentinel sentinel-端口号.conf #启动哨兵
配置可参考
https://www.jianshu.com/p/06ab9daf921d
十二、后补
缓冲穿透、缓存击穿、缓存雪崩、集群Cluster