Redis是一个基于内存的key-value 键值存储的、可持久化的数据库,且提供了非常丰富的数据结构,同时还支持非常丰富的功能特性。
- 键值型:存储的数据都是以key.value对的形式存储,value可以是字符串,数值,甚至json。
- NoSql(Not Only SQL):非关系型、灵活、高性能、水平扩展和高容错性的数据库系统。存储的数据,没有类似Mysql那么严格的约束,比如唯一性,是否可以为null等等。
一、初识Redis
Redis诞生于2009年全称是Remote Dictionary Server 远程词典服务器,是一个基于内存的键值型NoSQL数据库。
特征:
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存.IO多路复用.良好的编码)。
- 支持数据持久化
- 支持主从集群.分片集群
- 支持多语言客户端
二、redis的安装
大多数企业都是基于Linux服务器来部署项目。括单机部署、主从部署、哨兵部署、集群部署的安装以及相应的架构介绍。
(1)单机部署
(2)主从部署(Master-Slave Replication)
主从复制:将一台Redis服务器的数据,复制到其他的Redis服务器。前者称主节点(Master),后者为从节点(Slave)。
数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或无),但一个从节点只能有一个主节点。
- 主从复制的作用:
- 数据冗余:主从复制实现了数据的热备份。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
- 主节点不需要做改变,从节点都需要修改配置,配置完成后,可以再主库检查从节点信息
- 添加主节点信息
replicaof 192.168.75.129 6379
- 主从复制缺点
- 复制延时,信号衰减:所有写操作都在master上进行,然后同步更新,所以从master同步到slave上有一定的延迟,当系统繁忙时,延迟问题会更加严重,slave机器数量的增加也会使这个问题更加严重。
- master挂了如何办?默认情况下,不会在slave节点中自动重选一个master,每次都要人工干预。
(3)哨兵部署(Sentinel)
主从复制主要用于实现数据的冗余备份和读分担,并不是为了提供高可用性。
哨兵模式:通过在独立的哨兵节点上运行特定的哨兵进程来实现的。这些哨兵进程监控主从节点的状态,并在发现故障时自动完成故障发现和转移,并通知应用方,实现高可用性。
- 哨兵
启动时,每个哨兵节点会执行选举过程,其中一个哨兵节点被选为领导者(leader),负责协调其他哨兵节点。
- 选举过程:
每个哨兵都可成为领导者,每个哨兵会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者;当其它哨兵收到此命令时,可以同意或拒绝;若哨兵发现自己在选举的票数大于等于num(sentinels)/2+1时,将成为领导者,若没有超过,将继续选举。
- 监控主从节点:
哨兵周期性发送命令检查主从节点的健康状态,包括主节点是否在线、从节点是否同步等。若哨兵发现主节点不可用,它会触发一次故障转移。
- 故障转移:
一旦主节点被判定为不可用,哨兵节点会执行故障转移操作。它会从当前的从节点中选出一个新的主节点,并将其他从节点切换到新的主节点。因此无需人工干预系统可照常工作。
- 故障转移过程:
由Sentinel节点定期监控发现主节点是否出现了故障:sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已不可用。
- 确认主节点:
过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点;
选择从节点优先级最高的;
选择复制偏移量最大,此指复制最完整的从节点;
当主节点出现故障,由领导者负责处理主节点的故障转移;
- 客户端重定向:
哨兵节点会通知客户端新的主节点的位置,使其能够与新的主节点建立连接并发送请求。确保了客户端可以无缝切换到新的主节点。此外还负责监控从节点的状态。若从节点出现故障,哨兵可以将其下线,并在从节点恢复正常后重新将其加入集群。
- 客观下线
当主观下线的节点是主节点时,此时该哨兵节点会通过指令sentinelis-masterdown-by-addr寻求其它哨兵节点对主节点的判断,当超过quorum(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵都同意下线操作,也就是客观下线。
- 哨兵模式部署整体架构图
- 哨兵使用建议
- 数量应为多个,哨兵本身应该集群,保证高可用;
- 节点数应是奇数(避免投票平局);
- 各个哨兵结点的配置应一致;
- 若部署在Docker等容器里面,尤其要注意端口号的正确映射。
- 哨兵模式:并不能保证数据零丢失
- 复制延迟:在主从复制中,主从之间通过异步复制数据。但当主节点故障时,从节点未完全同步最新的数据,可导致数据丢失。
- 故障检测和转移时间:Sentinel检测到主节点故障并执行故障转移需要时间。在该时间内,主节点可能接收了一些写操作,但操作未被复制到从节点。
- 网络分区:在发生网络分区的情况下,一部分节点可能与主节点失去联系。若此时主节点继续处理写操作,那么在网络恢复之前,这些操作可能不会被 复制到从节点。
- 多个从节点同时故障:若所有的从节点同时故障或在故障转移之前与主节点失联,那么在主节点故障时,将没有可用的从节点来提升为主节点。
(4)集群部署(Cluster)
集群是Redis的一种分布式运行模式,它通过分片(sharding)来提供数据的自动分区和管理,从而实现数据的高可用性和可扩展性。
在集群模式下,数据被分割成多个部分(称槽/slots),分布在多个Redis节点上
集群中的节点分主节点和从节点:主节点负责读写请求和集群信息的维护;从节点只进行主节点数据和状态信息的复制。
- Redis集群的作用
- 数据分区(分片):最核心的功能。
- 集群将数据分散到多个节点,突破了Redis单机内存大小的限制,存储容量大大增加;
- 每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。
- Redis单机内存大小受限问题:
- 持久化问题:Redis 的持久化通过 fork() 创建子进程来完成。fork() 会复制主进程的内存页表,若单机内存过大可导致导致主进程阻塞时间长,或者内存占用翻倍,从而无法处理客户端请求,导致服务延迟或超时。
- 主从复制问题:主节点故障后,哨兵会选举新主节点,其他从节点需重新同步新主的数据,这时若从节点需全量复制新主的数据,且数据量很大,则同步过程耗时极长,此时同步期间从节点是不可用的,会导致集群整体可用性下降,尤其在高并发场景下可能引发雪崩。
- 全量复制阶段主节点复制缓冲区溢出:主从复制时,会先将写命令存入复制缓冲区,此时缓冲区大小受限,可能溢出。
- 高可用:集群支持主从复制和主节点的自动故障转移(与哨兵类似);当任一节点发生故障时,集群仍然可以对外提供服务。
- 数据分区(分片):最核心的功能。
- Redis集群的数据分片
集群引入了哈希槽的概念,有16384个哈希槽(编号0-16383),集群的每个节点负责一部分哈希槽,每个Key通过CRC16校验后对16384取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
- 以3节点组成的集群为例:A包含0到5460号哈希槽节点,B包含5461到10922号哈希槽,C包含10923到16383号哈希槽;
- Redis集群的主从复制模型集群中具有A、B、C三个节点,若B失败,整个集群就会因缺少5461-10922这个范围的槽而不可用。为每个节点添加一个从节点A1、B1、C1,整个集群便有三个Master节点和三个slave节点组成,在节点B失败后,集群选举B1位为的主节点继续服务。当B和B1都失败后,集群将不可用。
- Redis Cluster环境简述
三主三从模式:集群的一种典型部署架构,6 个 Redis 节点分布在多台服务器上。主节点各存储部分数据(哈希槽分片),负责读写请求,从节点就负责数据复制和部分读操作。
- redis集群配置准备
- redis 集群数据读写
三、客户端连接Redis
(1)使用官方Redis Insight
- Redis Insight
Redis官方推荐的客户端工具,功能齐全,但不支持中文,下载地址:https://redis.io/insight/。
Redis支持多个数据库,默认情况下有16个数据库(编号从0到15),可以使用SELECT命令切换不同的数据库。每个数据库之间是相互隔离的,可以在不同数据库中存储不同的数据。
SELECT θ #切换到数据库θ
SET key1 value1 #在数据库θ中设置键值对
(2)使用第三方工具连接Redis
Tiny RDM(推荐),UI好看,支持中文,支持字体设置。
官网地址:https://redis.tinycraft.cc/zh/
基友网地址:https://github.com/tiny-craft/tiny-rdm/tree/main
(3)Java客户端连接Redis
- Jedis快速入门
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
private Jedis jedis;
@BeforeEach
void setUp() {
// 1.建立连接
// jedis = new Jedis("192.168.150.101", 6379);
jedis = JedisConnectionFactory.getJedis();
// 2.设置密码
jedis.auth("123321");
// 3.选择库
jedis.select(0);
}
@Test
void testString() {
// 存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
- Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。
public class JedisConnectionFacotry {
private static final JedisPool jedisPool;
static {
//配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
//创建连接池对象
jedisPool = new JedisPool(poolConfig,
"192.168.150.101",6379,1000,"123321");
}
//提供一个静态方法,用于从连接池中获取Jedis实例
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
//建立连接时
@BeforeEach
void setUp() {
//1.建立连接
jedis = JedisConnectionFactory.getJedis();
//2.设置密码
jedis.auth("cjn");
//3.选择库
jedis.select(0);
}
- Redis的Java客户端-SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。
官网地址:https://spring.io/projects/spring-data-redis
提供了对不同Redis客户端的整合(Lettuce和Jedis)
提供了RedisTemplate统一API来操作Redis
支持Redis的发布订阅模型
支持Redis哨兵和Redis集群
支持基于Lettuce的响应式编程
支持基于JDK.JSON.字符串.Spring对象的数据序列化及反序列化
支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100ms #连接等待时间
- redis数据序列化器
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化:
缺点:可读性差、内存占用较大
所以可自定义RedisTemplate的序列化方式,代码如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// 设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
- StringRedisTemplate
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
为了减少内存的消耗,可以采用手动序列化的方式,即不借助默认的序列化器,而是由自己来控制序列化的动作,同时,只采用String的序列化器,这样,在存储value时,我们就不需要在内存中就不用多存储数据,从而节约我们的内存空间。
这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
省去了自定义RedisTemplate的序列化方式的步骤,而是直接使用:
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testString() {
// 写入一条String数据
stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");
// 获取string数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("虎哥", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:200", json);
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
此时再来看存储的数据,就会发现class数据已经不在了,节约了空间。
RedisTemplate的两种序列化实践方案:
- 方案一:
- 自定义RedisTemplate
- 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
- 方案二:
- 使用StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把读取到的JSON反序列化为对象
- 引入依赖
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Json序列化依赖-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
- 配置文件
spring:
data:
redis:
#host: 192.168.75.129#Redis服务器地址
#port: 6379#Redis服务器连接端口
#password:#Redis服务器连接密码(默认为空)
database: 0 #Redis数据库索引(默认为e)
url: redis://192.168.75.129:6379 #Redis服务器的连接URL,在Spring中相当于是password+ip+port
timeout: 60s # 连接空闲超过N(s秒、ms毫秒,不加单位时使用毫秒)后关闭,θ为禁用,这里配置值和tcp-keepalive值一致
#Lettuce连接池配置
lettuce:
pool:
max-active: 10 # 允许最大连接数,默认8(负值表示没有限制),推荐值:大于cpu*2,通常为(cpu*2)+2
max-idle: 8 # 最大空闲连接数,默认8,推荐值:cpu *2
min-idle: 0 # 最小空闲连接数,默认e
max-wait: 5s # 连接用完时,新的请求等待时间(s秒、ms毫秒),超过该时间抛出异常,默认-1(负值表示没有限制)
- RedisTemplate的使用
// Redis相关Bean配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置键序列化器为 StringRedisSerializer,所有的键都会被序列化为字符串
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置值序列化器为 GenericJackson2JsonRedisSerializer,所有的值都会被序列化为 Json 格式
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
四、数据结构与操作
1、基本数据结构
(1)字符串(String)
Redis中最简单且常用的数据结构,是一种二进制安全的数据结构,可以用来存储任何类型的数据。如字符串、整数、浮点数、图片、序列化后的对象。
- 应用场景:
- 缓存 session、token、图片地址、序列化后的对象(相比较于Hash存储更节省内存)。
相关命令:SET、GET。
- 需要计数的场景:
用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数。
相关命令:SET、GET、INCR、DECR。
- 分布式锁
利用 SETNX key value 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。
- 基本操作:
SET key value | 设置指定的key值 |
GET key | 获取指定key的值 |
SETNX sey value | 只要key不存在时设置key的值 |
SETEX key seconds value | 将值value关联到key,并将key的过期时间设为seconds(以秒为单位) |
GETSET key value | 将给定key的值设为value,并返回key的旧值(old value) |
INCR key | 将key中存储的数字值增1 |
DECR key | 将key存储的数字值减1 |
STRLENkey | 返回key所存储的字符串的长度 |
APPEND key value | 如果key已经存在并且是一个字符串,APPEND命令将value追加到key原来的值的末尾 |
GETRANGE key start end | 返回key中字符串值的子字符 |
GETBIT key offset | 对key所存储的字符串值,获取指定偏移量上的位(bit) |
SETBIT key offset value | 对key所存储的字符串值,设置或清除指定偏移量上的位(bit) |
MGETkey1[key2] | 获取所有(一个或多个)给定key的值 |
MSET key value[key value.] | 同时设置一个或者多个键值对 |
SETRANGEkeyoffsetvalue | 用value参数覆写给定key所存储的字符串量,从偏移量开始 |
PSETEX key milliseconds value | 和sETEX命令相似,但它以毫秒为单位设置key的生存时间,而不是像SETEX命令那样,以秒为单位 |
INCRBYkeyincrement | 将key所存储的值加上给定的增量值(increment) |
INCRBYFLoATkeyincrement | 将key所存储的值加上给定的浮点增量值(increment) |
DECRBYkey decrement | key所存储的值减去给定的减量值 |
(2)列表(List)
简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
- 应用场景:
- 信息流展示
举例:最新文章、最新动态。
相关命令:LPUSH、LRANGE。
- 实现栈(先进后出)
相关命令:LPUSH、LPOP。
- 实现队列(先进先出)
相关命令:RPUSH、LPOP。
- 基本操作
指令 | 含义 |
LPUSH key value1 [ value2 ] | 将一个或者多个值插入到列表头部 |
RPUSH key value1 [ value2 ] | 在列表中添加一个或者多个值尾部 |
LINDEX key index | 通过索引获取列表中的元素 |
LLEN key | 获取列表长度 |
LRANGE key start stop | 获取列表指定范围内的元素 |
LPOP key | 移出并获取列表的第一个元素 |
BLPOP key1 [ key2 ] timeout | 移出并获得列表的第一个元素,如果列表没有元素会阻塞列表知道等待超时或发现可弹出元素为止 |
BRPOP key1 [ key2 ] timeout | 移出并获得列表的最后一个元素,如果列表没有元素会阻塞列表知道等待超时或发现可弹出元素为止 |
BRPOPLPUSH source destination timeout | 弹出一值,弹出的元素会插入另一列表并返回;若无元素则阻塞,直到超时或发现可弹出元素为止 |
LINSERT key BEFORE | AFTER pivot value | 在列表的元素前或者后插入元素 |
LPUSHX key value1 [ value2 ] | 将一个或者多个值插入到已存在的列表头部 |
LREM key count value | 移除列表元素 |
LSET key index value | 通过索引设置列表元素的值 |
LTRIM key start stop | 对一个列表进行修剪(trim),就是说列表只保留指定区间内的元素,不在指定区间内的元素都将被删除 |
RPOP key | 移除并获取列表最后一个元素 |
RPOPLPUSH source destination | 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
RPUSHX key value | 为已存在的列表添加值 |
(3)哈希(Hash)
Redis中的Hash是一个String类型的field-value(键值对)的映射表,特别适合用于存储对象,也可以直接修改对象中的某些字段值。
- 应用场景:
- 对象数据存储场景
举例:用户信息、商品信息、文章信息。
相关命令:HSET(设置一个/多个字段的值)、HGET(获取单个字段的值)、HMGET(获取多个字段的值)。
- 购物车信息:
相关命令:HSET(加购物车)、HINCR(加数量)、HLEN(获取所有商品数量)、HDEL(删除商品)、HGETALL(获取所有商品)。
- 基本操作
指令 | 含义 |
HVALS key | 获取哈希表中所有的值 |
HSET key field value | 将哈希表key中的字段field的值设为value |
HGET key field | 获取存储在哈希表中指定字段的值 |
HSETNX key field value | 只有在字段field不存在时,设置哈希表字段的值 |
HDEL key field2 [ field2 ] | 删除一个或者多个哈希表字段 |
HEXISTS key field | 查看哈希表中key,指定的字段是否存在 |
HGETALL key | 获取在哈希表中指定key的所有字段和值 |
HINCRBY key field increment | 为哈希表key中指定字段的整数值添加上增量increment |
HKEYS | 获取所有哈希表中的字段 |
HLEN key | 获取哈希表中字段的数量 |
HMGET key field1 [ field2 ] | 获取所有给定字段的值 |
HMSET key field1 value1 [ field2 value2 ] | 同时将多个field-value(域-值)对设置到哈希表key中 |
HSCAN key cursor [ MATCH pattem ] [ COUNT count ] | 迭代哈希表中的键值对 |
HINCRBYFLOAT key field increment | 为哈希表key中指定字段的浮点数值添加上增量increment |
(4)集合(Set)
一种无序集合,集合中的元素唯一,也就是集合中的元素是无重复的,有点类似于Java 中的HashSet。
- 应用场景
- 需要随机获取数据源中的元素的场景
举例:抽奖系统、随机。
相关命令:SADD(加入抽奖系统)SMEMBERS(查看所有抽奖用户)SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。
- 需要存放的数据不能重复的场景
举例:文章点赞、动态点赞等场景。
相关命令:SADD(点赞)SREM(移除点赞)SISMEMBER(检查用户是否点赞过)SMEMBERS(获取点赞用户列表)SCARD(获取点赞用户数量)
- 基本操作
指令 | 含义 |
SADD key member1 [ member2 ] | 向集合添加一个或者多个成员 |
SCARD key | 获取集合的成员数 |
SMEBERS key | 返回集合中的所有成员 |
SPOP key | 移除并返回集合中的一个随机元素 |
SRANDMEMBER key [ count ] | 返回集合中一个或者多个随机数 |
SREM key member1 [ member2 ] | 移除集合中一个或者多个成员 |
SUNION key1 [ key2 ] | 返回所有给定集合的并集 |
SINTER key1 [ key2 ] | 返回所有给定集合的交集 |
SDIFF key1 [ key2 ] | 返回给定所有集合的差集 |
SUNIONSTORE destination key1 [ key2 ] | 所有给定集合的并集存储在destination集合中 |
SDIFFSTORE destination key1 [ key2 ] | 返回给定所有集合的差集并存储在destination中 |
SINTERSTORE destination key1 [ key2 ] | 返回给定所有集合的交集并存储在destination中 |
SISMEMBER key member | 判断menber元素是否是集合key的成员 |
SMOVE source destination menber | 将member元素从source集合移动到destination集合 |
SSCAN key cursor [ MATCH pattem ] [ COUNT count ] | 迭代集合中的元素 |
(5)有序集合(Sorted Set)
Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个 double 类型的分数,使得集合中的元素能够按分数进行有序排列。
- 应用场景:
- 需要随机获取数据源中的元素根据某个权重进行排序的场景
举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
相关命令:ZINCR(每点击一次进行加一)、ZREVRANGE(从大到小排序)、ZUNIONSTORE(多日搜索汇总)。
- 基本操作:
指令 | 含义 |
ZADD key score menber1 [ score2 menber2 ] | 向有序集合添加一个或者多个成员,或者更新已存在的成员分数 |
ZCARD key | 获取有序集合的元素个数 |
ZRANGE key start stop [ WITHSCORES ] | 通过索引区间返回有序集合成指定区间内的成员 |
ZREM key menber [ member ] | 移除有序集合中的一个或多个成员 |
ZSCORE key member1 | 获取指定有序集合中指定元素的 score 值 |
ZREVRANGE key start end | 按分数从高到低返回有序集合 |
ZCOUNT key min max | 可计算在有序集合中指定区间分数的成员 |
ZINCRBY key increment menber | 有序集合中对指定成员的分数加上增量increment |
ZINTERSTORE destination numkeys key [ key… ] | 计算给定的一个或者多个有序集的交集并将结果集存储在新的有序集合key中 |
ZUNIONSTORE destination numkeys key [ key… ] | 计算给定的一个或者多个有序集的并集并将结果集存储在新的有序集合key中 |
ZDIFF destination numkeys key [ key… ] | 计算给定的一个或者多个有序集的差集并将结果集存储在新的有序集合key中 |
ZRANGEBYSCORE key min max [ WITHSCORES ] [ LIMIT ] | 通过分数返回有序集合指定区间内的成员 |
ZLEXCOUNT key min max | 在有序集合中计算指定字典区间内成员数量 |
ZRANGEBYLEX key min max [ LIMIT offset count ] | 通过字典区间返回有序集合的成员 |
ZRANK key menber | 返回有序集合中指定成员的索引 |
2、高级数据结构
(1)位图(Bitmaps)
- 应用场景:
- 需要保存状态信息(0/1即可表示)的场景
举例:用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
相关命令:SETBIT、GETBIT、BITCOUNT、BITOP。
- 基本操作:
指令 | 含义 |
setbit key offset val | 给指定key的值的第offset赋值val 时间复杂度:O(1) |
getbit key offset | 获取指定key的第offset位 时间复杂度:O(1) |
bitcount key start end | 返回指定key中[ start,end ]中为1的数量 时间复杂度:O(n) |
bitop operation destkey key | 对不同的二进制存储数据进行运算(AND、OR、NOT、XOR) 时间复杂度:O(1) |
(2)超日志(HyperLogLog)
一种有名的基数计数概率算法,并不是Redis 特有的,Redis 只是实现了这个算法并提供了一些开箱即用的APl。
Redis提供的HyperLogLog占用空间非常非常小,只需要12k的空间就能存储接近2^64个不同元素。
- 应用场景:
- 数量巨大(百万、千万级别以上)的计数场景
举例:热门网站每日/每周/每月访问ip数统计、热门帖子uv统计
相关命令:PFADD、PFCOUNT
PFADD key element [element] # 添加一个或多个元素到 HyperLogLog 中
PFCoUNT key [key...] # 获取一个或者多个 HyperLogLog 的唯一计数。
PFMERGE destkey sourcekey [sourcekey...] # 将多个HyperLogLog 合并到destkey 中,destkey 会结合多个源,算出对应的唯一计数。
(3)地理空间(Geospatial)
Geospatial index(地理空间索引l,简称GEO):用于存储地理位置信息,基于Sorted Set实现。
通过GEO可轻松实现两个位置距离的计算、获取指定位置附近的元素等功能。
- 应用场景:
- 需要管理使用地理空间数据的场景
举例:附近的人。
相关命令:GEOADD、GEORADIUS、GEORADIUSBYMEMBER
- GEOADD key longitude1 latitude1 member1 [longitude latitude member ...]
# 添加一个或多个元素对应的经纬度信息到 GEO 中
- GEOPOS key member [member ...]
# 返回给定元素的经纬度信息
- GEODIST key member1 member2 [m|km|ft|mi]
# 计算两个位置之间的距离。
- GEOHASH key member [member ...]
# 返回一个或多个位置对象的 geohash 值。
- GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
# 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
- GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
# 根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
- 获取指定位置范围内的…
- 使用Zset命令的操作(
(4)发布/订阅(Pub/Sub)
一种消息传模式,其中发布者发送消息,而订阅者接收消息,传递消息的通道称为channel。
- 应用场景:
- 简易的实时消息传递场景(无法持久化):
通知系统:例如在社交媒体平台上,当有新评论或新点赞时,可以通过 Pub/Sub 通知相关用户。
- 基本操作:
指令 | 含义 |
PSUBSCRIBE | 订阅一个或多个符合给定模式的频道 |
PUBSUB | 查看订阅与发布系统状态 |
PUBLISH | 将信息发送到指定的频道 |
PUNSUBSCRIBE | 退订所有给定模式的频道 |
SUBSCRIBE | 订阅给定的一个或多个频道的信息 |
UNSUBSCRIBE | 指退订给定的频道 |
(5)流 (Streams)
主要用于消息队列(MQ,Message Queue),Redis 本身是有一个Redis 发布订阅(pub/sub)来实现消息队列的功能,但有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃,而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
- 应用场景:
- 消息队列
RedisStreams可以用作消息队列,支持发布/订阅模式和消费者组,确保消息能够可靠地传递给多个消费者。例如:
任务调度系统:不同的生产者将任务发布到Stream中,不同的消费者可以并行处理这些任务。
异步处理:在电商平台中,订单生成后,将其放入Stream中进行后续的库存更新、通知发送等异步处理。
- 基本操作:
- XADD key ID field value [field value ...]
添加消息到末尾
- XTRIM key MAXLEN [~] count
对流进行修剪,限制长度
- XDEL key ID [ID ...]
删除消息
- XLEN key
获取流包含的元素数量,即消息长度
- XRANGE key start end [COUNT count]
获取消息列表,会自动过滤已经删除的消息
- XREVRANGE key end start [COUNT count]
反向获取消息列表,ID 从大到小
- XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
以阻塞或非阻塞方式获取消息列表
消费者组相关命令:
- XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
创建消费者组
- XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key...] ID [ID...]:读取消费者组中的消息
XACK <stream> <group> <ID> [ID...]
将消息标记为"已处理"
- XGROUP SETID <stream> <group> <id>
为消费者组设置新的最后递送消息ID
- XGROUP DELCONSUMER <stream> <group> <consumer>
删除消费者
- XGROUP DESTROY <stream> <group>
删除消费者组
- XPENDING <stream> <group> [start] [end] [count] [consumer]
显示待处理消息的相关信息
- XCLAIM <stream> <group> <consumer> <min-idle-time> <ID> [ID ...] [IDLE <milliseconds>] [TIME <mstime>] [RETRYCOUNT <count>] [FORCE] [JUSTID]
转移消息的归属权
- XINFO <subcommand> <key>
查看流和消费者组的相关信息;
- XINFO GROUPS <stream>
打印消费者组的信息;
- XINFO STREAM <stream>
打印流信息
3、Redis的基本操作指令
指令 | 含义 |
keys* | 查看当前库中的所有key |
existskey | 判断某个key是否存在 |
typekey | 查看key的类型 |
delkey | 删除指定的key数据 |
ttlkey | 查看剩余过期时间 |
expirekeyseconds | 给指定key设置过期时间 |
movekeydbindex[0-15] | 移动指定key至某个库 |
selectdbindex[0-15] | 切换至指定数据库,默认θ |
dbsize | 查看当前数据库key的数量 |
flushdb | 清空当前库 |
flushall | 清空所有库 |