当前位置: 首页 > news >正文

Redis核心技术与实战指南

1、redis是什么?

Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库,由意大利人 Salvatore Sanfilippo 使用 C 语言编写。

Redis 是一种开源(BSD 许可)内存中数据结构存储,用作数据库、缓存、消息代理和流引擎。Redis 提供数据结构,例如 字符串、散列、列表、集合、带范围查询的排序集、位图、超级日志、地理空间索引和流。

Redis(Remote Dictionary Server)是一个开源的、基于内存的键值型NoSQL数据库,它以极高的性能和灵活的数据结构著称。作为内存中的数据存储系统,Redis不仅支持字符串、哈希、列表、集合、有序集合等丰富的数据类型,还提供了持久化、事务、发布/订阅、Lua脚本等高级功能。它常被用作高速缓存、消息队列、实时数据分析等场景,同时通过RDB和AOF机制保障数据可靠性。凭借其单线程事件驱动模型和高效的C语言实现,Redis能够处理每秒数十万级的读写请求,成为现代分布式系统中提升性能和扩展能力的核心组件之一。

2、Redis优势

性能极高:Redis 基于内存实现数据存储,它的读取速度是 110000次/s,写速度是 81000次/s;

多用途工具: Redis 有很多的用途,比如可以用作缓存、消息队列、搭建 Redis 集群等;

命令提示功能:Redis 客户端拥有强大的命令提示功能,使用起来非常的方便,降低了学习门槛;

可移植性:Redis 使用用标准 C语言编写的,能够在大多数操作系统上运行,比如 Linux,Mac,Solaris 等。

3、Redis缺点

内存限制:Redis的数据完全存储在内存中,这意味着存储容量受到物理内存的限制。

持久化机制:Redis提供了两种持久化机制(RDB快照和AOF日志),但它们都不是实时的。如果Redis在发生故障之前没有及时进行持久化,可能会丢失最新的数据。

数据一致性:由于Redis是一个分布式系统,它无法提供像关系型数据库那样的严格一致性。在某些条件下,Redis可能出现数据不一致或并发写入冲突的情况,需要开发人员自行处理。

4、redis的安装(Linux)

1、上传redis-7.4.0.tar.gz到服务器/opt文件中

2、解压redis-7.4.0.tar.gz

tar -zxvf redis-7.4.0.tar.gz

3、进入到redis-7.4.0中输入make进行编译

yum install -y gcc make
make distclean
make

4、进入src文件中进行安装,输入: make install

5、修改redis.conf配置文件

protected-mode 设置成no

daemonize改为yes 表明需要在后台运行

vim /etc/rc.d/rc.local
新增:
/opt/redis/bin/redis-server /opt/redis-7.4.0/redis.conf执行/opt/redis/bin/redis-server /opt/redis-7.4.0/redis.conf

如果报错:

echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
sysctl -p
/opt/redis/bin/redis-server /opt/redis-7.4.0/redis.conf
ps -ef | grep redis

设置redis的密码:

在配置文件里面找到 # requirepass foobared

开放端口

17、开放端口
sudo firewall-cmd --zone=public --add-port=6379/tcp --permanent
-- 重启防火墙
sudo firewall-cmd --reload
--  查看已开放的端口
sudo firewall-cmd --list-ports进入命令行
/opt/redis/bin/redis-cli -a 123456

5、Redis key键

Key(键)是用于唯一标识存储在数据库中的数据的名称。Redis是一个键值存储数据库,每个键都对应着一个特定的值。键在Redis中是非常重要的,因为它是数据的唯一标识符,用于对数据进行读取、写入和删除操作。

Key的命名规则

Redis的键是以字符串的形式存储的,可以是任意的字符串,包括字母、数字和特殊字符。

在命名键时,需要注意使用有意义的、易于理解的键名,以便于后续维护和管理。

Redis中的键是区分大小写的。

Key的命名空间

Redis的键具有全局命名空间,意味着在整个Redis实例中,所有的键都必须是唯一的。

因此,在设计键名时,要确保不会与其他键发生冲突,可以使用一定的命名规则或前缀来避免键名冲突。

Key的过期时间

Redis允许为每个键设置过期时间,称为过期时间(TTL)。

可以在设置键的同时指定过期时间,一旦到达过期时间,键将自动被删除,释放内存空间。

Key的生命周期

Redis的键可以有一个特定的生命周期,即在一段时间后自动过期并删除,也可以永久存在。

生命週期是通过设置TTL来控制的,当TTL为0时,键会立即被删除。

Key的操作

可以使用Redis提供的命令对键进行各种操作,如设置值、获取值、删除键等。

通过键的操作,可以实现对数据的增删改查和其他更复杂的数据处理操作。

Key的命名规范

在Redis中,键的命名是以字符串形式存储的,因此在键名中使用不同的符号,如冒号(:)和连字符(-),会对键的结构和用途产生不同的影响。

冒号(:)作为分隔符:

冒号常常用作命名空间的分隔符,将不同层级或类型的信息组织在一起,方便创建层次结构的键名。

使用冒号作为分隔符,可以将键划分为多个层级,创建类似命名空间的结构,有利于更好地组织和查找键。

示例:user:123:name, user:123:email, product:456:name, product:456:price

连字符(-)作为分隔符

连字符常用于创建键名中的自然语言风格,使得键名更加可读和直观。

使用连字符作为分隔符,可以使键名在一定程度上具有更好的可读性,尤其是对于人类来说。

示例:user-123-name, user-123-email, product-456-name, product-456-price

6、Redis的五种数据类型

字符串(String):

字符串是Redis中最简单的数据类型,可以存储任意类型的数据,比如文本、数字等。

可以对字符串执行常见的字符串操作,如获取、设置、追加、截取等。

由于字符串是二进制安全的,可以用于存储图片、音频等二进制数据。

哈希(Hash):

哈希类型类似于关联数组,它包含字段(field)和值(value)的映射表。

适用于存储对象或实体,每个对象的属性对应哈希中的字段,可以方便地读取、更新和删除对象的特定属性。

列表(List):

列表是一个有序的字符串元素集合,可以在列表的头部或尾部进行元素的添加和删除。

列表适用于实现栈、队列等数据结构,也可以用于实现消息队列等场景。

集合(Set):

集合是一个无序、不重复的字符串元素集合。

可以执行集合的交集、并集、差集等集合操作,支持快速地判断元素是否存在。

有序集合( zset ):

有序集合类似于集合,每个元素都有一个分数(score)与之关联。

元素根据分数进行排序,支持根据分数范围获取元素,适用于排行榜、计分系统等场景。

1、Redis-字符串(String)

# 设置键值
SET username "john_doe"
# 获取值
GET username
# 追加字符串
APPEND username "_123"# 设置数值并自增
SET counter 10
#自增1
INCR counter 
INCRBY counter 5
#自减1
DECR counter
DECRBY counter 5# 设置过期时间 3600 秒(1小时)
SETEX session:value 3600 "user_data"#分布式锁
SETNX  product:10001  true
SETNX  product:10001  true
DEL  product:10001# 批量设置获取
MSET key1 "value1" key2 "value2"
MGET key1 key2

2、哈希(Hash)

# 设置用户信息
HSET user:1000 name "Alice" age 30 email "alice@example.com"# 获取单个字段
HGET user:1000 name# 获取所有字段和值
HGETALL user:1000# 更新字段
HSET user:1000 age 31# 如果不存在则设置
HSETNX user:1000 age 32# 增加数值字段
HINCRBY user:1000 age 1# 删除字段
HDEL user:1000 email# 检查字段是否存在
HEXISTS user:1000 name

3、列表(List)

# 从左侧插入元素
LPUSH tasks "send_email"
LPUSH tasks "process_data"# 从右侧插入元素
RPUSH tasks "generate_report"# 获取列表范围
LRANGE tasks 0 -1# 从左侧弹出元素
LPOP tasks# 从右侧弹出元素
RPOP tasks# 获取列表长度
LLEN tasks# 修剪列表 用于修剪(截取)列表,只保留指定范围内的元素
LTRIM tasks 0 1# 根据索引获取元素
LINDEX tasks 0

4、集合(Set)

# 添加元素
SADD tags "redis" "database" "nosql"# 获取所有元素
SMEMBERS tags# 检查元素是否存在
SISMEMBER tags "redis"# 移除元素
SREM tags "nosql"# 随机弹出元素
SPOP tags# 集合运算
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"# 交集
SINTER set1 set2# 并集
SUNION set1 set2# 差集
SDIFF set1 set2# 获取集合大小
SCARD tags

5、有序集合( zset )

# 添加带分数的成员
ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"# 按分数升序获取
ZRANGE leaderboard 0 -1 WITHSCORES# 按分数降序获取
ZREVRANGE leaderboard 0 -1 WITHSCORES# 新增成员分数,存在则修改,不存在则新增
ZINCRBY leaderboard 50 "player1"# 获取成员排名(从0开始)
ZRANK leaderboard "player2"  # 升序排名
ZREVRANK leaderboard "player2"  # 降序排名# 获取分数范围内的成员
ZRANGEBYSCORE leaderboard 120 250# 获取集合大小
ZCARD leaderboard# 统计分数范围内的成员数
ZCOUNT leaderboard 100 200# 移除成员
ZREM leaderboard "player3"

7、SpringBoot整合redis的步骤

1、创建SpringBoot项目导入依赖,并创建目录结构

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、在 application.yml 或 application.properties 中配置 Redis 服务器信息

spring:redis:host: 127.0.0.1       # Redis 服务器地址port: 6379            # Redis 端口password: 123456      # 密码(若无密码可省略)database: 0           # 默认数据库索引lettuce:pool:max-active: 200   # 连接池最大连接数max-idle: 10      # 最大空闲连接min-idle: 0       # 最小空闲连接max-wait: -1ms    # 最大阻塞等待时间(-1表示无限制)

3、自定义配置RedisTemplate的 JSON 序列化

package com.lw.redisdemo.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;/*** Redis 配置*/
@Configuration
public class RedisConfig {/*** 创建一个 RedisTemplate 对象,用于与 Redis 进行交互* @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {// 创建一个 RedisTemplate 对象,用于与 Redis 进行交互RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 创建一个 RedisSerializer 对象,用于序列化 和 反序列化 key 和 valueRedisSerializer<String> redisSerializer = new StringRedisSerializer();// 设置 RedisTemplate 的连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置 RedisTemplate 的 key 序列化器redisTemplate.setKeySerializer(redisSerializer);// 设置 RedisTemplate 的 hash key 序列化器redisTemplate.setHashKeySerializer(redisSerializer);// 设置 RedisTemplate 的 value 序列化器redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());return redisTemplate;}/*** 创建一个 RedisCacheManager 对象,用于管理 Redis 缓存*/@Beanpublic RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer());RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair).entryTtl(Duration.ofSeconds(10));return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);}/*** 创建一个 Jackson2JsonRedisSerializer 对象,用于序列化和反序列化 Redis 中的数据*/private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {// 创建一个新的 Jackson2JsonRedisSerializer,类型为 ObjectJackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);// 创建一个新的 ObjectMapperObjectMapper objectMapper = new ObjectMapper();// 设置 ObjectMapper 的可见性,显示所有属性objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 设置 ObjectMapper 不失败未知属性objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);// 启用 ObjectMapper 的默认类型objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);// 设置 ObjectMapper 给 Jackson2JsonRedisSerializerjsonRedisSerializer.setObjectMapper(objectMapper);// 返回 Jackson2JsonRedisSerializerreturn jsonRedisSerializer;}}

4、编写Redis工具类

package com.lw.redisdemo.util;import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/*** RedisUtil工具类*/
@Component
@Slf4j
public class RedisUtil {@Resourceprivate RedisTemplate redisTemplate;/*** 设置缓存中的key-value对** @param key   缓存的key* @param value 缓存的值*/public void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}/*** 如果key不存在,则设置缓存中的key-value对,并设置过期时间** @param key      缓存的key* @param value    缓存的值* @param time     过期时间* @param timeUnit 时间单位* @return 如果设置成功则返回true,否则返回false*/public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {// 添加成功返回truereturn redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);}/*** 获取缓存中的值** @param key 缓存的key* @return 缓存的值,如果key不存在则返回null*/public Object get(String key) {return redisTemplate.opsForValue().get(key);}/*** 检查是否存在指定的key** @param key 要检查的缓存key* @return 如果key存在则返回true,否则返回false*/public boolean exist(String key) {return redisTemplate.hasKey(key);}/*** 删除指定的key** @param key 要删除的缓存key* @return 如果key被成功删除则返回true,否则返回false*/public boolean del(String key) {return redisTemplate.delete(key);}/*** 删除一个或多个key** @param keys 要删除的key数组* @return 成功删除的key数量*/public Long del(String... keys) {return redisTemplate.delete(Arrays.asList(keys));}/*** 将 key 中储存的数字值增一 (INCR)** @param key 键名* @return 执行 INCR 命令之后 key 的值*/public Long incr(String key) {return redisTemplate.opsForValue().increment(key);}/*** 将 key 中储存的数字值增加指定的增量 (INCRBY)** @param key       键名* @param increment 增量值* @return 执行 INCRBY 命令之后 key 的值*/public Long incrBy(String key, long increment) {return redisTemplate.opsForValue().increment(key, increment);}/*** 将 key 中储存的数字值减一 (DECR)** @param key 键名* @return 执行 DECR 命令之后 key 的值*/public Long decr(String key) {return redisTemplate.opsForValue().decrement(key);}/*** 将 key 中储存的数字值减少指定的减量 (DECRBY)** @param key       键名* @param decrement 减量值* @return 执行 DECRBY 命令之后 key 的值*/public Long decrBy(String key, long decrement) {return redisTemplate.opsForValue().decrement(key, decrement);}/*** 向有序集合中添加元素** @param key   有序集合的key* @param value 要添加的元素* @param score 元素的分数* @return 如果添加成功则返回true,否则返回false*/public Boolean zAdd(String key, String value, Long score) {return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));}/*** 计算有序集合的元素数量** @param key 有序集合的key* @return 有序集合的元素数量*/public Long countZset(String key) {return redisTemplate.opsForZSet().size(key);}/*** 获取有序集合中指定范围的元素** @param key   有序集合的key* @param start 起始索引* @param end   结束索引* @return 指定范围内元素的集合*/public Set<String> rangeZset(String key, long start, long end) {return redisTemplate.opsForZSet().range(key, start, end);}/*** 从有序集合中删除指定的元素** @param key   有序集合的key* @param value 要删除的元素* @return 如果删除成功则返回1,否则返回0*/public Long removeZset(String key, Object value) {return redisTemplate.opsForZSet().remove(key, value);}/*** 批量从有序集合中删除指定的元素** @param key   有序集合的key* @param value 要删除的元素集合*/public void removeZsetList(String key, Set<String> value) {value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));}/*** 获取有序集合中指定元素的分数** @param key   有序集合的key* @param value 要查询分数的元素* @return 元素的分数,如果元素不存在则返回null*/public Double score(String key, Object value) {return redisTemplate.opsForZSet().score(key, value);}/*** 获取有序集合中指定分数范围内的元素** @param key   有序集合的key* @param start 分数起始范围* @param end   分数结束范围* @return 指定分数范围内元素的集合*/public Set<String> rangeByScore(String key, long start, long end) {return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));}/*** 为有序集合中的指定元素增加分数** @param key   有序集合的key* @param obj   要增加分数的元素* @param score 增加的分数* @return 增加后的分数*/public Object addScore(String key, Object obj, double score) {return redisTemplate.opsForZSet().incrementScore(key, obj, score);}/*** 获取有序集合中指定元素的排名** @param key 有序集合的key* @param obj 要查询排名的元素* @return 元素的排名,如果元素不存在则返回null*/public Object rank(String key, Object obj) {return redisTemplate.opsForZSet().rank(key, obj);}/*** Hash操作 - 设置Hash字段值*/public void hSet(String key, String field, Object value) {redisTemplate.opsForHash().put(key, field, value);}/*** Hash操作 - 只有当字段不存在时设置Hash字段值*/public boolean hSetNx(String key, String field, Object value) {return redisTemplate.opsForHash().putIfAbsent(key, field, value);}/*** Hash操作 - 获取Hash字段值*/public Object hGet(String key, String field) {return redisTemplate.opsForHash().get(key, field);}/*** Hash操作 - 获取所有字段和值*/public Map<Object, Object> hGetAll(String key) {return redisTemplate.opsForHash().entries(key);}/*** Hash操作 - 删除一个或多个Hash字段*/public Long hDel(String key, Object... fields) {return redisTemplate.opsForHash().delete(key, fields);}/*** Hash操作 - 判断Hash中是否存在某字段*/public boolean hExists(String key, String field) {return redisTemplate.opsForHash().hasKey(key, field);}/*** Hash操作 - 为Hash中数值字段增加指定值*/public Long hIncrBy(String key, String field, long increment) {return redisTemplate.opsForHash().increment(key, field, increment);}// 在RedisUtil.java中添加以下List操作方法/*** List操作 - 从左侧插入元素** @param key   列表的key* @param value 要插入的值* @return 插入后列表的长度*/public Long lPush(String key, String value) {return redisTemplate.opsForList().leftPush(key, value);}/*** List操作 - 从右侧插入元素** @param key   列表的key* @param value 要插入的值* @return 插入后列表的长度*/public Long rPush(String key, String value) {return redisTemplate.opsForList().rightPush(key, value);}/*** List操作 - 获取列表范围** @param key   列表的key* @param start 起始索引* @param end   结束索引* @return 指定范围内的元素集合*/public List<String> lRange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}/*** List操作 - 从左侧弹出元素** @param key 列表的key* @return 弹出的元素*/public String lPop(String key) {return (String) redisTemplate.opsForList().leftPop(key);}/*** List操作 - 从右侧弹出元素** @param key 列表的key* @return 弹出的元素*/public String rPop(String key) {return (String) redisTemplate.opsForList().rightPop(key);}/*** List操作 - 获取列表长度** @param key 列表的key* @return 列表的长度*/public Long lLen(String key) {return redisTemplate.opsForList().size(key);}/*** List操作 - 修剪列表,只保留指定范围内的元素** @param key   列表的key* @param start 起始索引* @param end   结束索引*/public void lTrim(String key, long start, long end) {redisTemplate.opsForList().trim(key, start, end);}/*** List操作 - 根据索引获取元素** @param key   列表的key* @param index 元素索引* @return 指定索引的元素*/public String lIndex(String key, long index) {return (String) redisTemplate.opsForList().index(key, index);}// 在RedisUtil.java中添加以下Set操作方法/*** Set操作 - 添加一个或多个元素到集合** @param key    集合的key* @param values 要添加的值* @return 成功添加的元素数量*/public Long sAdd(String key, String... values) {return redisTemplate.opsForSet().add(key, values);}/*** Set操作 - 获取集合所有元素** @param key 集合的key* @return 集合中所有元素*/public Set<String> sMembers(String key) {return redisTemplate.opsForSet().members(key);}/*** Set操作 - 判断元素是否在集合中** @param key   集合的key* @param value 要检查的值* @return 如果元素存在返回true,否则返回false*/public Boolean sIsMember(String key, Object value) {return redisTemplate.opsForSet().isMember(key, value);}/*** Set操作 - 移除集合中一个或多个元素** @param key    集合的key* @param values 要移除的值* @return 成功移除的元素数量*/public Long sRem(String key, Object... values) {return redisTemplate.opsForSet().remove(key, values);}/*** Set操作 - 随机移除并返回集合中的一个元素** @param key 集合的key* @return 被移除的元素*/public String sPop(String key) {return (String) redisTemplate.opsForSet().pop(key);}/*** Set操作 - 获取集合的交集** @param keys 要比较的集合key数组* @return 交集元素集合*/public Set<String> sInter(String... keys) {return redisTemplate.opsForSet().intersect(Arrays.asList(keys));}/*** Set操作 - 获取集合的并集** @param keys 要比较的集合key数组* @return 并集元素集合*/public Set<String> sUnion(String... keys) {return redisTemplate.opsForSet().union(Arrays.asList(keys));}/*** Set操作 - 获取集合的差集** @param keys 要比较的集合key数组* @return 差集元素集合*/public Set<String> sDiff(String... keys) {return redisTemplate.opsForSet().difference(Arrays.asList(keys));}/*** Set操作 - 获取集合大小** @param key 集合的key* @return 集合的元素数量*/public Long sCard(String key) {return redisTemplate.opsForSet().size(key);}// 在RedisUtil.java中添加以下ZSet操作方法/*** 批量添加有序集合元素** @param key          有序集合的key* @param scoreMembers 分数与成员的Map* @return 成功添加的元素数量*/public Long zAdd(String key, Map<String, Double> scoreMembers) {Set<ZSetOperations.TypedTuple<String>> tuples = scoreMembers.entrySet().stream().map(entry -> new DefaultTypedTuple<>(entry.getKey(), entry.getValue())).collect(Collectors.toSet());return redisTemplate.opsForZSet().add(key, tuples);}/*** 按分数升序获取有序集合范围(带分数)** @param key   有序集合的key* @param start 起始索引* @param end   结束索引* @return 带分数的元素集合*/public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) {return redisTemplate.opsForZSet().rangeWithScores(key, start, end);}/*** 按分数降序获取有序集合范围(带分数)** @param key   有序集合的key* @param start 起始索引* @param end   结束索引* @return 带分数的元素集合*/public Set<ZSetOperations.TypedTuple<String>> zRevRangeWithScores(String key, long start, long end) {return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);}/*** 增加成员的分数** @param key       有序集合的key* @param member    成员* @param increment 增加的分数* @return 增加后的分数*/public Double zIncrBy(String key, String member, double increment) {return redisTemplate.opsForZSet().incrementScore(key, member, increment);}/*** 获取成员的降序排名(从0开始)** @param key    有序集合的key* @param member 成员* @return 降序排名,如果成员不存在返回null*/public Long zRevRank(String key, String member) {return redisTemplate.opsForZSet().reverseRank(key, member);}/*** 获取分数范围内的成员(升序)** @param key 有序集合的key* @param min 最小分数* @param max 最大分数* @return 成员集合*/public Set<String> zRangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().rangeByScore(key, min, max);}/*** 统计分数范围内的成员数量** @param key 有序集合的key* @param min 最小分数* @param max 最大分数* @return 成员数量*/public Long zCount(String key, double min, double max) {return redisTemplate.opsForZSet().count(key, min, max);}
}

5、SpringBoot中使用Redis进行操作

1、Redis存储字符串
package com.lw.redisdemo.controller;import com.lw.redisdemo.bean.Result;
import com.lw.redisdemo.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Slf4j
@RestController
@RequestMapping("/redis")
public class RedisDemoController {@Resourceprivate RedisUtil redisUtil;/*** 字符串类型基本操作*/@PostMapping("/basic")public Result basicOperations() {// SET username "john_doe"String key = "username";redisUtil.set(key, "john_doe");// GET usernameString username = (String) redisUtil.get(key);log.info("从redis中获取存入的key: {},value:{}", key, username);// 模拟APPEND - RedisUtil没有直接提供append方法,我们可以组合实现String newUsername = username + "_123";redisUtil.set(key, newUsername);String getnewUsername = (String) redisUtil.get(key);log.info("从redis中获取存入的key: {},value:{}", key, getnewUsername);return Result.success("0001", "Redis字符串类类型基本操作成功");}/*** 计数器操作*/@PostMapping("/counter")public Result counterOperations() {// SET counter 10redisUtil.set("counter", 10);log.info("从redis中获取存入的key: {},value:{}", "counter", redisUtil.get("counter"));// INCR counter - 使用原子性操作Long counter = redisUtil.incr("counter");log.info("操作:使用自增1::::从redis中获取存入的key: {},value:{}", "counter", counter);// INCRBY counter 5 - 使用原子性操作counter = redisUtil.incrBy("counter", 5);log.info("操作:计数器自增5::::从redis中获取存入的key: {},value:{}", "counter", counter);// DECR counter - 使用原子性操作counter = redisUtil.decr("counter");log.info("操作:使用自减1::::从redis中获取存入的key: {},value:{}", "counter", counter);// DECRBY counter 5 - 使用原子性操作counter = redisUtil.decrBy("counter", 5);log.info("操作:计数器自减5::::从redis中获取存入的key: {},value:{}", "counter", counter);return Result.success("0002", "Redis计数器操作成功", counter);}/*** 过期时间操作*/@PostMapping("/expire")public String expireOperations() {// 使用setNx模拟SETEX session:value 3600 "user_data"boolean result = redisUtil.setNx("session:value", "user_data", 3600L, TimeUnit.SECONDS);return "Set with expire result: " + result + ", TTL: 3600s";}/*** 分布式锁模拟*/@PostMapping("/lock")public Result lockOperations() throws InterruptedException {String lockKey = "product:1001:lock";String requestId = UUID.randomUUID().toString();try {long expireTime = 60L; // 30秒boolean lock = redisUtil.setNx(lockKey, requestId, expireTime, TimeUnit.SECONDS);log.info("{} 获取锁结果:{}", lockKey, lock);if (!lock) {log.info("lockOperations::获取锁失败,判断是否存在key: {},value:{}", lockKey, redisUtil.get(lockKey));return Result.error("0006", "Redis分布式锁操作失败,请稍后再试");}log.info("{}获取锁成功", lockKey);log.info("lockOperations::执行业务逻辑......");Thread.sleep(20000);  //20秒log.info("lockOperations::执行业务逻辑结束。。。。");return Result.success("0003", "Redis分布式锁操作成功");} catch (Exception e) {log.error("lockOperations::执行业务逻辑异常", e);return Result.error("0005", "Redis分布式锁操作异常,请稍后再试");} finally {String uuidKey = (String) redisUtil.get(lockKey);log.info("lockOperations::释放锁,判断是否存在key: {},value:{},requestId:{}", lockKey, uuidKey, requestId);// 释放锁if (StringUtils.isNotBlank(uuidKey) && requestId.equals(uuidKey)) {//  详细输出日志log.info("lockOperations::释放锁,判断是否存在key: {},value:{}", lockKey, uuidKey);redisUtil.del(lockKey);}}}
}

2、哈希(Hash)
package com.lw.redisdemo.controller;import com.lw.redisdemo.util.RedisUtil;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Map;@RestController
@RequestMapping("/redis/hash")
public class RedisHashController {@Resourceprivate RedisUtil redisUtil;/*** 1. 设置用户信息 HSET user:1000 name "Alice" age 30 email "alice@example.com"* @return*/@PostMapping("/user")public String setUserInfo() {String userKey = "user:1000";redisUtil.hSet(userKey, "name", "Alice");redisUtil.hSet(userKey, "age", 30);redisUtil.hSet(userKey, "email", "alice@example.com");return "用户信息设置成功";}/*** 2. 获取单个字段 HGET user:1000 name* @return 用户名称信息*/@GetMapping("/user/name")public String getUserName() {String userKey = "user:1000";return "用户信息 " + redisUtil.hGet(userKey, "name");}/*** 3. 获取所有字段和值 HGETALL user:1000* @return*/@GetMapping("/user/all")public Map<Object, Object> getAllUserInfo() {String userKey = "user:1000";return redisUtil.hGetAll(userKey);}/*** 4. 更新字段 HSET user:1000 age 31* @return*/@PutMapping("/user/age")public String updateUserAge() {String userKey = "user:1000";redisUtil.hSet(userKey, "age", 31);return "用户的年龄更新 " + redisUtil.hGet(userKey, "age");}/*** 如果不存在则设置 HSETNX user:1000 age 32* @return*/@PostMapping("/user/age/setnx")public String setUserAgeIfAbsent() {String userKey = "user:1000";boolean result = redisUtil.hSetNx(userKey, "age", 32);return "HSETNX 结果: " + result + ", 当前年龄: " + redisUtil.hGet(userKey, "age");}/*** 增加数值字段 HINCRBY user:1000 age 1* @return*/@PostMapping("/user/age/increment")public String incrementUserAge() {String userKey = "user:1000";Long newAge = redisUtil.hIncrBy(userKey, "age", 1);return "用户年龄增加到:" + newAge;}/*** 7. 删除字段 HDEL user:1000 email* @return*/@DeleteMapping("/user/email")public String deleteUserEmail() {String userKey = "user:1000";Long result = redisUtil.hDel(userKey, "email");return "电子邮件字段已删除,结果: " + result;}/*** 8. 检查字段是否存在 HEXISTS user:1000 name* @return*/@GetMapping("/user/check/name")public String checkNameExists() {String userKey = "user:1000";boolean exists = redisUtil.hExists(userKey, "name");return userKey+"是否存在"+exists;}
}
3、列表(List)
package com.lw.redisdemo.controller;import com.lw.redisdemo.util.RedisUtil;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;@RestController
@RequestMapping("/redis/list")
public class RedisListController {@Resourceprivate RedisUtil redisUtil;private static final String TASK_LIST_KEY = "tasks";/*** 1. 从左侧插入元素 LPUSH tasks "send_email" "process_data"* @return*/@PostMapping("/left")public String leftPushTasks() {redisUtil.lPush(TASK_LIST_KEY, "send_email");redisUtil.lPush(TASK_LIST_KEY, "process_data");return "任务已成功从左侧插入";}/*** 2. 从右侧插入元素 RPUSH tasks "generate_report"*/@PostMapping("/right")public String rightPushTask() {redisUtil.rPush(TASK_LIST_KEY, "generate_report");return " 任务已成功从右侧插入";}/*** 3. 获取列表范围 LRANGE tasks 0 -1* @return*/@GetMapping("/all")public List<String> getAllTasks() {return redisUtil.lRange(TASK_LIST_KEY, 0, -1);}/*** 4. 从左侧弹出元素 LPOP tasks* @return*/@GetMapping("/pop/left")public String popLeftTask() {return "从左侧弹出的任务是:" + redisUtil.lPop(TASK_LIST_KEY);}/*** 5. 从右侧弹出元素 RPOP tasks* @return*/@GetMapping("/pop/right")public String popRightTask() {return "从右侧弹出的任务是:" + redisUtil.rPop(TASK_LIST_KEY);}/***  6. 获取列表长度 LLEN tasks* @return*/@GetMapping("/length")public Long getTaskListLength() {return redisUtil.lLen(TASK_LIST_KEY);}/*** 7. 修剪列表 LTRIM tasks 0 1* @return*/@PostMapping("/trim")public String trimTaskList() {redisUtil.lTrim(TASK_LIST_KEY, 0, 1);// 中文输出return "任务列表已修剪到前两个元素";}/*** 8. 根据索引获取元素 LINDEX tasks 0* @param index* @return*/@GetMapping("/index/{index}")public String getTaskByIndex(@PathVariable long index) {// 中文输出return "任务列表中索引为 " + index + " 的任务为:" + redisUtil.lIndex(TASK_LIST_KEY, index);}/***  9. 删除列表元素 LREM tasks 0 "send_email"* @return*/@DeleteMapping("/clear")public String clearTaskList() {redisUtil.del(TASK_LIST_KEY);// 中文输出return "任务列表已清空";}
}
4、集合(Set)
package com.lw.redisdemo.controller;import com.lw.redisdemo.util.RedisUtil;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Set;/*** Redis 集合操作控制器,提供对 Set 类型数据的基础操作接口。*/
@RestController
@RequestMapping("/redis/set")
public class RedisSetController {@Resourceprivate RedisUtil redisUtil;/*** 存储标签的键名。*/private static final String TAGS_KEY = "tags";/*** 第一个集合的键名。*/private static final String SET1_KEY = "set1";/*** 第二个集合的键名。*/private static final String SET2_KEY = "set2";/*** 1. 添加元素到标签集合中。* 对应命令:SADD tags "redis" "database" "nosql"** @return 返回添加成功的元素数量及提示信息。*/@PostMapping("/tags")public String addTags() {Long addedCount = redisUtil.sAdd(TAGS_KEY, "redis", "database", "nosql");return "成功添加了 " + addedCount + " 个标签";}/*** 2. 获取标签集合中的所有元素。* 对应命令:SMEMBERS tags** @return 返回标签集合的所有元素。*/@GetMapping("/tags")public Set<String> getAllTags() {return redisUtil.sMembers(TAGS_KEY);}/*** 3. 检查指定的标签是否存在。* 对应命令:SISMEMBER tags "redis"** @param tag 标签名称* @return 返回是否包含该标签。*/@GetMapping("/tags/{tag}")public Boolean checkTagExists(@PathVariable String tag) {return redisUtil.sIsMember(TAGS_KEY, tag);}/*** 4. 移除指定的标签元素。* 对应命令:SREM tags "nosql"** @param tag 要移除的标签名称* @return 返回成功移除的元素数量。*/@DeleteMapping("/tags/{tag}")public Long removeTag(@PathVariable String tag) {return redisUtil.sRem(TAGS_KEY, tag);}/*** 5. 随机弹出一个标签元素。* 对应命令:SPOP tags** @return 返回被弹出的标签名称。*/@GetMapping("/tags/pop")public String popRandomTag() {return "随机弹出了标签: " + redisUtil.sPop(TAGS_KEY);}/*** 6. 初始化用于集合运算的两个集合数据。* 向 set1 和 set2 中分别插入测试数据。** @return 返回初始化完成的提示信息。*/@PostMapping("/sets/init")public String initSetsForOperation() {redisUtil.sAdd(SET1_KEY, "a", "b", "c");redisUtil.sAdd(SET2_KEY, "b", "c", "d");return "集合初始化完成,可用于后续集合运算";}/*** 7. 获取两个集合的交集。* 对应命令:SINTER set1 set2** @return 返回两个集合的交集结果。*/@GetMapping("/sets/inter")public Set<String> getIntersection() {return redisUtil.sInter(SET1_KEY, SET2_KEY);}/*** 8. 获取两个集合的并集。* 对应命令:SUNION set1 set2** @return 返回两个集合的并集结果。*/@GetMapping("/sets/union")public Set<String> getUnion() {return redisUtil.sUnion(SET1_KEY, SET2_KEY);}/*** 9. 获取两个集合的差集。* 对应命令:SDIFF set1 set2** @return 返回两个集合的差集结果。*/@GetMapping("/sets/diff")public Set<String> getDifference() {return redisUtil.sDiff(SET1_KEY, SET2_KEY);}/*** 10. 获取标签集合的大小。* 对应命令:SCARD tags** @return 返回集合中元素的数量。*/@GetMapping("/tags/size")public Long getTagsSize() {return redisUtil.sCard(TAGS_KEY);}/*** 11. 清空所有集合数据。* 删除 tags、set1、set2 键对应的数据。** @return 返回清空完成的提示信息。*/@DeleteMapping("/clear")public String clearAllSets() {redisUtil.del(TAGS_KEY, SET1_KEY, SET2_KEY);return "所有集合数据已清除";}
}
5、有序集合( zset )
package com.lw.redisdemo.controller;import com.lw.redisdemo.util.RedisUtil;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.*;/*** Redis 有序集合操作控制器,提供对 ZSet 类型数据的基础操作接口。*/
@RestController
@RequestMapping("/redis/zset")
public class RedisZSetController {@Resourceprivate RedisUtil redisUtil;/*** 存储排行榜的键名。*/private static final String LEADERBOARD_KEY = "leaderboard";/*** 1. 初始化排行榜数据。* 对应命令:ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"** @return 返回添加成功的玩家数量及提示信息。*/@PostMapping("/init")public String initLeaderboard() {Map<String, Double> players = new HashMap<>();players.put("player1", 100.0);players.put("player2", 200.0);players.put("player3", 150.0);Long added = redisUtil.zAdd(LEADERBOARD_KEY, players);return "成功向排行榜中添加了 " + added + " 个玩家";}/*** 2. 按分数升序获取排行榜(包含分数)。* 对应命令:ZRANGE leaderboard 0 -1 WITHSCORES** @return 返回按分数升序排列的玩家及其分数。*/@GetMapping("/asc")public Map<String, Double> getLeaderboardAsc() {Set<ZSetOperations.TypedTuple<String>> tuples =redisUtil.zRangeWithScores(LEADERBOARD_KEY, 0, -1);return convertTuplesToMap(tuples);}/*** 3. 按分数降序获取排行榜(包含分数)。* 对应命令:ZREVRANGE leaderboard 0 -1 WITHSCORES** @return 返回按分数降序排列的玩家及其分数。*/@GetMapping("/desc")public Map<String, Double> getLeaderboardDesc() {Set<ZSetOperations.TypedTuple<String>> tuples =redisUtil.zRevRangeWithScores(LEADERBOARD_KEY, 0, -1);return convertTuplesToMap(tuples);}/*** 4. 增加指定玩家的分数。* 对应命令:ZINCRBY leaderboard 50 "player1"** @param player 玩家名称* @param score  要增加的分数* @return 返回更新后的总分数。*/@PostMapping("/incr/{player}/{score}")public Double incrementScore(@PathVariable String player,@PathVariable double score) {return redisUtil.zIncrBy(LEADERBOARD_KEY, player, score);}/*** 5. 获取指定玩家的升序排名。* 对应命令:ZRANK leaderboard "player2"** @param player 玩家名称* @return 返回该玩家在排行榜中的升序排名(从 0 开始)。*/@GetMapping("/rank/{player}")public Long getPlayerRank(@PathVariable String player) {return (Long) redisUtil.rank(LEADERBOARD_KEY, player);}/*** 6. 获取指定玩家的降序排名。* 对应命令:ZREVRANK leaderboard "player2"** @param player 玩家名称* @return 返回该玩家在排行榜中的降序排名(从 0 开始)。*/@GetMapping("/revrank/{player}")public Long getPlayerRevRank(@PathVariable String player) {return redisUtil.zRevRank(LEADERBOARD_KEY, player);}/*** 7. 获取指定分数范围内的所有玩家。* 对应命令:ZRANGEBYSCORE leaderboard 120 250** @param min 最低分数* @param max 最高分数* @return 返回在指定分数区间内的所有玩家。*/@GetMapping("/range/{min}/{max}")public Set<String> getPlayersByScoreRange(@PathVariable double min,@PathVariable double max) {return redisUtil.zRangeByScore(LEADERBOARD_KEY, min, max);}/*** 8. 获取排行榜中的玩家总数。* 对应命令:ZCARD leaderboard** @return 返回排行榜中的玩家数量。*/@GetMapping("/size")public Long getLeaderboardSize() {return redisUtil.countZset(LEADERBOARD_KEY);}/*** 9. 统计指定分数范围内内的玩家数量。* 对应命令:ZCOUNT leaderboard 100 200** @param min 最低分数* @param max 最高分数* @return 返回满足条件的玩家数量。*/@GetMapping("/count/{min}/{max}")public Long countPlayersInRange(@PathVariable double min,@PathVariable double max) {return redisUtil.zCount(LEADERBOARD_KEY, min, max);}/*** 10. 移除指定玩家。* 对应命令:ZREM leaderboard "player3"** @param player 要移除的玩家名称* @return 返回成功移除的元素数量。*/@DeleteMapping("/remove/{player}")public Long removePlayer(@PathVariable String player) {return redisUtil.removeZset(LEADERBOARD_KEY, player);}/*** 11. 清空排行榜数据。* 删除 leaderboard 键对应的数据。** @return 返回是否删除成功。*/@DeleteMapping("/clear")public Boolean clearLeaderboard() {return redisUtil.del(LEADERBOARD_KEY);}/*** 辅助方法:将 TypedTuple 集合转换为 Map。** @param tuples TypedTuple 集合* @return 返回转换后的 Map。*/private Map<String, Double> convertTuplesToMap(Set<ZSetOperations.TypedTuple<String>> tuples) {Map<String, Double> result = new LinkedHashMap<>();if (tuples != null) {tuples.forEach(tuple -> {result.put(tuple.getValue(), tuple.getScore());});}return result;}
}

8、如何保证mysql与Redis的数据一致性?

保证MySQL与Redis的数据一致性是在使用这两种数据库的同时,确保数据在两个数据库中的状态保持一致,避免数据的不一致性。由于MySQL是持久化数据库,而Redis是内存数据库,两者的数据特性有所不同,因此需要采取一些措施来保持一致性。以下是一些常用的方法来实现MySQL与Redis的数据一致性:

数据同步策略:

在应用层实现数据同步策略,即在数据写入MySQL后,再将相应的数据更新或写入Redis。这可以确保数据先写入持久化数据库,再写入缓存数据库,保持数据一致性。

使用消息队列:

将数据的写入操作发送到消息队列中,然后由消息队列消费者负责将数据写入MySQL和Redis。这样可以将MySQL和Redis的写入操作解耦,提高系统的可靠性和稳定性。

写回策略:

对于频繁读取的数据,可以采用写回策略。即,当数据发生更新时,先更新Redis中的数据,并异步将更新操作写入MySQL。这样可以提高读取性能,同时保证数据的最终一致性。

设置过期时间:

对于Redis中缓存的数据,可以设置适当的过期时间,确保缓存数据的及时更新。当数据过期后,再从MySQL中读取最新数据并更新Redis,避免数据过期导致的不一致性。

使用事务:

在某些场景下,可以使用事务来确保MySQL和Redis的数据操作是原子性的。即,要么同时成功,要么同时失败,保持数据的一致性。

定期数据校验:

定期对MySQL和Redis中的数据进行校验,确保数据的一致性。如果发现数据不一致,及时进行修复。

如何保证MySQL和Redis数据一致性:

数据一致性问题的根源主要原因在于:

更新策略的选择困境:是先更新数据库还是先更新缓存?

并发操作导致的竞态条件:多线程/多进程同时读写同一数据

系统故障带来的中断:操作过程中任何一步失败都可能导致数据不一致

先删除缓存

多线程情况下:

问题:

  • 线程1修改了数据,首先删除redis中的数据,但是因为网络问题延迟了
  • 线程2此时来查询数据,首先查询缓存为空
  • 线程2查询数据库(查到旧数据)
  • 线程2这将旧数据放入缓存
  • 接下来的线程拿到的数据都是旧数据,而数据库是新数据,得等到缓存过期才能拿到新数据

延迟双删策略

根据CAP 原理(CAP Theorem),又称 布鲁尔定理(Brewer's Theorem),我们如果要保证操作一致那么这个操作就要是原子性的,我们可以通过加分布式锁解决,但是既然我们引入缓存不就是为了提高系统吞吐量,所以在AP和CP之间我们采用最终一致性而不是强一致性。

什么是延迟双删?

延迟双删策略的核心流程如下:

  1. 先删除缓存
  2. 再更新数据库延迟一段时间后
  3. 再次删除缓存

先操作数据库

  1. 先更新数据库
  2. 删除redis缓存

多线程情况下:

通过这个流程的分析我们知道只有线程2会发生数据不一致,但是可以保证数据库的最终一致性。如果发生数据不一致那肯定是删除缓存这步挂了,但是这种概率比较低

redis删除失败

为保证高可靠性,可引入消息队列机制,当删除失败的时候发送一个消息到消息队列中进行失败重试,实现数据的最终一致性。

9、为什么要使用Redis呢?

高性能和低延迟:

Redis是基于内存的数据库,数据存储在内存中,因此读写速度非常快,可以达到亚毫秒级的响应时间。这使得Redis成为处理实时数据和高并发请求的理想选择。

支持丰富的数据结构:

Redis支持多种数据结构,包括字符串、哈希、列表、集合和有序集合。这使得Redis能够满足各种不同场景下的数据存储需求,从简单的缓存到复杂的数据结构处理。

持久化支持:

Redis提供持久化功能,可以将内存中的数据保存到硬盘上,确保数据的安全性和持久性。这使得Redis即使在重启后也能保留之前存储的数据。

缓存应用:

Redis被广泛用作缓存数据库,将常用的数据缓存到内存中,减轻后端数据库的负载,提高系统的响应速度和性能。

分布式支持:

Redis支持分布式架构,可以配置成多个节点组成的集群,实现数据的水平扩展和高可用性。

发布/订阅功能:

Redis支持发布/订阅模式,可以实现消息的发布和订阅,用于实现实时消息推送和事件通知。

原子性操作:

Redis的命令是原子性的,可以确保多个操作的原子性执行,避免并发操作产生的竞态条件。

简单易用:

Redis的命令简单、易用,学习成本低,可以快速上手和使用。

10、Redis为什么快呢?

基于内存:Redis是基于内存的数据库,所有数据都存储在内存中,而不是像传统的关系型数据库那样存储在磁盘上。由于内存的读写速度远远高于磁盘,因此Redis能够实现极快的读写操作。

非阻塞IO:Redis采用单线程的事件循环模型,通过使用非阻塞IO来处理并发请求。这意味着Redis能够在处理一个请求时,不会被其他请求阻塞,从而提高了系统的响应速度。

高效的数据结构:Redis支持多种数据结构,如字符串、哈希、列表、集合和有序集合等。这些数据结构都经过优化,能够高效地进行数据操作,满足不同场景的需求。

内部优化算法:Redis在实现过程中采用了许多优化算法,比如跳跃表(Skip List)用于实现有序集合,哈希表用于快速存取数据等。这些内部优化使得Redis能够高效地处理大量数据。

单线程模型:虽然Redis采用单线程模型,但由于其非阻塞IO和高效的数据结构,可以在单个线程下处理成千上万的并发请求,而不会出现性能瓶颈。

持久化策略:Redis支持多种持久化策略,包括快照(Snapshotting)和日志(AOF)。这些策略使得Redis能够在数据宕机或重启后恢复数据,保证数据的持久性和可靠性。

相关文章:

  • 【kafka】rebalance机制详解
  • ubuntu install vncserver
  • Altair:用Python玩转声明式可视化(新手友好向)
  • 《tqdm:让你的代码会“喘气”的神奇进度条!》
  • 在 Java 中实现一个标准 Service 接口,并通过配置动态选择具体实现类供 Controller 调用
  • 【计算几何】几何邻近
  • Ubuntu 24.04 上安装与 Docker 部署 Sentinel
  • vue封装移动端日历,可折叠展开,以及考勤
  • openeuler系统(CentOs)图形化桌面黑屏/丢失(开启VNC服务冲突)
  • 蚁群算法(Ant Colony Optimization)原理与应用解析
  • ABP vNext + Spark on Hadoop:实时流处理与微服务融合
  • vue中的v-model指令和组件通信机制
  • 【Python 算法零基础 6.贪心算法】
  • Linux基本指令(包含vim,用户,文件等方面)超详细
  • 小白理财 - 指数基金定投
  • Proof of Talk专访CertiK联创顾荣辉:全周期安全方案护航Web3生态
  • 【前端面试】八、工程化
  • RV1126+OPENCV在视频中添加LOGO图像
  • 在QT中使用OpenGL
  • 使用Apache POI操作Word文档:从入门到实战
  • 做家政网上推广网站/品牌推广方式都有哪些
  • 有没有交流做服装的网站/站长统计入口
  • 广东网站设计流程/百度推广公司怎么代理到的
  • 苏州网站推广建设/杭州百度seo
  • 哪个网站可以做制图兼职/抖音seo优化系统招商
  • 牡丹江网站建设公司/整合营销名词解释