redis基本数据结构-string

ops/2025/1/12 15:53:24/

文章目录

    • 1. redis的string数据结构
    • 2. 常见的业务场景
      • 2.1 缓存功能
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.2 计数器
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.3 分布式锁
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.4 限流
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.5 共享session
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现

redisstring_1">1. redis的string数据结构

参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
redis 的 string 数据结构redis 中最基本的数据类型,它可以存储任何形式的数据,最大可以存储 512MB 的字符串。string 类型可以存储文本、数字和二进制数据等。
具有以下特性:

  1. 简单性:String 是最简单的类型,可以用来存储简单的键值对。
  2. 多样性:可以存储任意类型的数据,包括文本、数字、甚至是序列化后的对象。
  3. 原子性:对 String 的操作是原子性的,即在并发情况下操作串的安全性。

以下是一些与 String 相关的常用 Redis 命令:

  • SET key value:设置指定 key 的值。
  • GET key:获取指定 key 的值。
  • DEL key:删除指定 key。
  • EXISTS key:检查指定 key 是否存在。
  • INCR key:将 key 的值加 1。
  • DECR key:将 key 的值减 1。
  • MSET key1 value1 key2 value2 …:同时设置多个 key-value 对。
  • MGET key1 key2 …:同时获取多个 key 的值。
  • SETEX key seconds value:设置 key 的值,同时设置过期时间(以秒为单位)。
xxxxxx:6379> SET person:1 '{"name": "Alice", "age": 30, "city": "New York"}'
OKxxxxxx:6379> get person:1
"{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"xxxxxx:6379> exists person:1
(integer) 1xxxxxx:6379> exists person:2
(integer) 0xxxxxx:6379> set art:122 0
OKxxxxxx:6379> incr art:122
(integer) 1xxxxxx:6379> get art:122
"1"xxxxxx:6379> decr art:122
(integer) 0xxxxxx:6379> get art:122
"0"xxxxxx:6379> mget person:1 art:122
1) "{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"
2) "0"xxxxxx:6379> SETEX config:site 600 '{"theme": "dark", "language": "en"}'
OKxxxxxx:6379> mget person:1 art:122 config:site
1) "{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"
2) "0"
3) "{\"theme\": \"dark\", \"language\": \"en\"}"

2. 常见的业务场景

2.1 缓存功能

string类型常用于缓存经常访问的数据,如数据库查询结果、网页内容等,以提高访问速度和降低数据库的压力。一般多是用于读多写少的场景。比如,商品的价格,描述等信息,用户的资料等信息。

案例讲解
背景

在商品系统中,商品的详细信息如描述、价格、库存等数据通常不会频繁变动,但会被频繁查询。每次用户访问商品详情时,都直接从数据库查询这些信息会导致不必要的数据库负载。

优势
  1. 快速数据访问:Redis作为内存数据库,提供极速的读写能力,大幅降低数据访问延迟,提升用户体验。
  2. 减轻数据库压力:缓存频繁访问的静态数据,显著减少数据库查询,从而保护数据库资源,延长数据库寿命。
  3. 高并发支持:Redis设计用于高并发环境,能够处理大量用户同时访问,保证系统在流量高峰时的稳定性。
  4. 灵活的缓存策略:易于实现缓存数据的更新和失效,结合适当的缓存过期和数据同步机制,确保数据的实时性和一致性。
解决方案

使用Redis String类型来缓存商品的静态信息。当商品信息更新时,相应的缓存也更新或失效。
在这里插入图片描述

代码实现
go">package mainimport ("context""encoding/json""github.com/go-redis/redis/v8"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// generateProductCacheKey generates the cache key for the product.
func generateProductCacheKey(productID string) string {return "product:" + productID
}// cacheProductInfo caches the product information in Redis.
func cacheProductInfo(productID string, productInfo map[string]interface{}) {cacheKey := generateProductCacheKey(productID)// 序列化商品信息为JSON格式productJSON, _ := json.Marshal(productInfo)// 将序列化后的商品信息存储到Redis===> set product:apple {"name":"apple","price":100, "description":"a nice apple"}rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不过期,实际使用时可以设置过期时间
}// getProductInfoFromCache gets the product information from Redis.
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {cacheKey := generateProductCacheKey(productID)// 从Redis获取商品信息productJSON, err := rdb.Get(ctx, cacheKey).Result() // get product:appleif err != nil {return nil, err}if len(productJSON) == 0 {// 未在缓存中找到商品信息, 需要从数据库中获取, 同时更新缓存return nil, nil}// 反序列化JSON格式的商品信息var productInfo map[string]interface{}err = json.Unmarshal([]byte(productJSON), &productInfo)if err != nil {return nil, err}return productInfo, nil
}func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {// 更新数据库中的商品信息// 更新Redis缓存中的商品信息cacheProductInfo(productID, newProductInfo)
}

在日常实践中,感觉这里还是会有一点小问题:

  1. 如果是在商品信息插入,对于一个新商品可能缓存肯定是不存在的,如果某一个商城大量上新新产品,这个时候如果流量大量进来,是否会造成缓存击穿的情况?因此对于这种插入情况多基本没更新的时候,上面的流程可能有点问题。
  2. 在用户查询商品信息的时候,如果存在大量缓存失败的情况,也会导致数据库崩溃的情况,所以在日常实践中,如果在redis中查询不到,那可以直接返回。因此在写入的时候需要双写;且要异步数据同步。定义一个定时任务,每隔一段时间将数据库的信息增量同步到redis,一天全量同步一次商品信息插入的时候双写
go">package mainimport ("context""encoding/json""fmt""github.com/go-redis/redis/v8"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// generateProductCacheKey generates the cache key for the product.
func generateProductCacheKey(productID string) string {return "product:" + productID
}// cacheProductInfo caches the product information in Redis.
func cacheProductInfo(productID string, productInfo map[string]interface{}) {cacheKey := generateProductCacheKey(productID)// 序列化商品信息为JSON格式productJSON, _ := json.Marshal(productInfo)// 将序列化后的商品信息存储到Redis===> set product:apple {"name":"apple","price":100, "description":"a nice apple"}rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不过期,实际使用时可以设置过期时间
}// getProductInfoFromCache gets the product information from Redis.
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {cacheKey := generateProductCacheKey(productID)// 从Redis获取商品信息productJSON, err := rdb.Get(ctx, cacheKey).Result() // get product:appleif err != nil {return nil, err}// 反序列化JSON格式的商品信息var productInfo map[string]interface{}err = json.Unmarshal([]byte(productJSON), &productInfo)if err != nil {return nil, err}return productInfo, nil
}func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {// 三个步骤如果有任何一个步骤失败,都需要回滚// 更新数据库中的商品信息// 更新Redis缓存中的商品信息cacheProductInfo(productID, newProductInfo)// 查一遍缓存,确保缓存中的商品信息已经更新info, err := getProductInfoFromCache(productID)if err != nil {// 处理错误}// 处理infofmt.Printf("product info: %v\n", info)
}// 补充一个定时任务,可以使用crontab, 方便管理也可以使用airflow等工具

2.2 计数器

利用INCR和DECR命令,String类型可以作为计数器使用,适用于统计如网页访问量、商品库存数量等 。

案例讲解
背景

对于文章的浏览量的统计,每篇博客文章都有一个唯一的标识符(例如,文章ID)。每次文章被访问时,文章ID对应的浏览次数在Redis中递增。可以定期将浏览次数同步到数据库,用于历史数据分析。在这里插入图片描述

优势
  • 实时性:能够实时更新和获取文章的浏览次数。
  • 高性能:Redis的原子操作保证了高并发场景下的计数准确性。
解决方案

通过Redis实现对博客文章浏览次数的原子性递增和检索,以优化数据库访问并实时更新文章的浏览统计信息。

代码实现
go">package mainimport ("context""errors""fmt""github.com/go-redis/redis/v8""log""strconv"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// recordArticleView 记录文章浏览次数
func recordArticleView(articleID string) {res, err := rdb.Incr(ctx, articleID).Result()if err != nil {// 如果发生错误,记录错误日志log.Printf("Error incrementing view count for article %s: %v", articleID, err)return}// 可选:记录浏览次数到日志或进行其他业务处理fmt.Printf("Article %s has been viewed %d times\n", articleID, res)
}// getArticleViewCount 从Redis获取文章的浏览次数
func getArticleViewCount(articleID string) (int, error) {// 从Redis获取文章的浏览次数viewCount, err := rdb.Get(ctx, articleID).Result()if err != nil {if errors.Is(err, redis.Nil) {// 如果文章ID在Redis中不存在,可以认为浏览次数为0return 0, nil} else {// 如果发生错误,记录错误日志log.Printf("Error getting view count for article %s: %v", articleID, err)return 0, err}}// 将浏览次数从字符串转换为整数count, err := strconv.Atoi(viewCount)if err != nil {log.Printf("Error converting view count to integer for article %s: %v", articleID, err)return 0, err}return count, nil
}// renderArticlePage 渲染文章页面,并显示浏览次数
func renderArticlePage(articleID string) {// 在渲染文章页面之前,记录浏览次数recordArticleView(articleID)// 获取文章浏览次数viewCount, err := getArticleViewCount(articleID)if err != nil {// 处理错误,例如设置浏览次数为0或跳过错误viewCount = 0}log.Printf("Rendering article %s with view count %d\n", articleID, viewCount)
}

2.3 分布式锁

分布式锁:通过SETNX命令(仅当键不存在时设置值),String类型可以实现分布式锁,保证在分布式系统中的互斥访问 。

案例讲解
背景

在分布式系统中,如电商的秒杀活动或库存管理,需要确保同一时间只有一个进程或线程可以修改共享资源,以避免数据不一致的问题。
在这里插入图片描述

优势
  1. 互斥性:确保同一时间只有一个进程可以访问共享资源,防止数据竞争和冲突。
  2. 高可用性:分布式锁能够在节点故障或网络分区的情况下仍能正常工作,具备自动故障转移和恢复的能力。
  3. 可重入性:支持同一个进程或线程多次获取同一个锁,避免死锁的发生。
  4. 性能开销:相比于其他分布式协调服务,基于Redis的分布式锁实现简单且性能开销较小。
解决方案

使用Redis的SETNX命令实现分布式锁的获取和释放,通过Lua脚本确保释放锁时的原子性,并在执行业务逻辑前尝试获取锁,业务逻辑执行完毕后确保释放锁,从而保证在分布式系统中对共享资源的安全访问。

代码实现
go">package mainimport ("context""github.com/go-redis/redis/v8""log""strconv""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 尝试获取分布式锁
func tryGetDistributedLock(lockKey string, val string, expireTime int) bool {// 使用SET命令结合NX和PX参数尝试获取锁// NX表示如果key不存在则可以设置成功// PX指定锁的超时时间(毫秒)// 这里的val是一个随机值,用于在释放锁时验证锁是否属于当前进程result, err := rdb.SetNX(ctx, lockKey, val, time.Duration(expireTime)*time.Millisecond).Result()if err != nil {// 记录错误,例如:日志记录log.Printf("Error trying to get distributed lock for key %s: %v", lockKey, err)return false}// 如果result为1,则表示获取锁成功,result为0表示锁已被其他进程持有return result
}// 释放分布式锁, 这里的val是一个随机值,用于在释放锁时验证锁是否属于当前进程
func releaseDistributedLock(lockKey string, val string) {// 使用Lua脚本来确保释放锁的操作是原子性的script := `if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])elsereturn 0end`// 执行Lua脚本result, err := rdb.Eval(ctx, script, []string{lockKey}, val).Result()if err != nil {// 记录错误log.Printf("Error releasing distributed lock for key %s: %v", lockKey, err)}// 如果result为1,则表示锁被成功释放,如果为0,则表示锁可能已经释放或不属于当前进程if result == int64(0) {log.Printf("Failed to release the lock, it might have been released by others or expired")}
}// 执行业务逻辑,使用分布式锁来保证业务逻辑的原子性
func executeBusinessLogic(lockKey string) {val := generateRandomValue()                    // 生成一个随机值,作为锁的值if tryGetDistributedLock(lockKey, val, 30000) { // 尝试获取锁,30秒超时defer releaseDistributedLock(lockKey, val) // 无论业务逻辑是否成功执行,都释放锁// 执行具体的业务逻辑// ...} else {// 未能获取锁,处理重试逻辑或返回错误// ...}
}
// generateRandomValue 生成一个随机值作为锁的唯一标识
func generateRandomValue() string {return strconv.FormatInt(time.Now().UnixNano(), 10)
}

2.4 限流

限流:使用EXPIRE命令,结合INCR操作,可以实现API的限流功能,防止系统被过度访问

案例讲解
背景

一个在线视频平台提供了一个API,用于获取视频的元数据。在高流量事件(如新电影发布)期间,这个API可能会收到大量并发请求,这可能导致后端服务压力过大,甚至崩溃。
在这里插入图片描述

优势
  1. 稳定性保障:通过限流,可以防止系统在高负载下崩溃,确保核心服务的稳定性。
  2. 服务公平性:限流可以保证不同用户和客户端在高并发环境下公平地使用服务。
  3. 防止滥用:限制API的调用频率,可以防止恶意用户或爬虫对服务进行滥用。
解决方案
  1. 请求计数:每次API请求时,使用INCR命令对特定的key进行递增操作。
  2. 设置过期时间:使用EXPIRE命令为计数key设置一个过期时间,过期时间取决于限流的时间窗口(例如1秒)。
  3. 检查请求频率:如果请求计数超过设定的阈值(例如每秒100次),则拒绝新的请求或进行排队。
代码实现
go">package mainimport ("context""github.com/go-redis/redis/v8""log""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 伪代码:API限流器
func rateLimiter(apiKey string, threshold int, timeWindow int) bool {currentCount, err := rdb.Incr(ctx, apiKey).Result()if err != nil {log.Printf("Error incrementing API key %s: %v", apiKey, err)return false}// 如果当前计数超过阈值,则拒绝请求if int(currentCount) > threshold {return false}// 重置计数器的过期时间_, err = rdb.Expire(ctx, apiKey, time.Duration(timeWindow)).Result()if err != nil {log.Printf("Error resetting expire time for API key %s: %v", apiKey, err)return false}return true
}// 在API处理函数中调用限流器
func handleAPIRequest(apiKey string) {if rateLimiter(apiKey, 100, 1) { // 限流阈值设为100,时间窗口为1秒// 处理API请求} else {// 限流,返回错误或提示信息}
}

2.5 共享session

在多服务器的Web应用中,用户在不同的服务器上请求时能够保持登录状态,实现会话共享。

案例讲解
背景

考虑一个大型电商平台,它使用多个服务器来处理用户请求以提高可用性和伸缩性。当用户登录后,其会话信息(session)需要在所有服务器间共享,以确保无论用户请求到达哪个服务器,都能识别其登录状态。

优势
  1. 用户体验:用户在任何服务器上都能保持登录状态,无需重复登录。
  2. 系统可靠性:集中管理session减少了因服务器故障导致用户登录状态丢失的风险。
  3. 伸缩性:易于扩展系统以支持更多服务器,session管理不受影响。
解决方案

使用Redis的String类型来集中存储和管理用户session信息。

  • 存储Session:当用户登录成功后,将用户的唯一标识(如session ID)和用户信息序列化后存储在Redis中。
  • 验证Session:每次用户请求时,通过请求中的session ID从Redis获取session信息,验证用户状态。
  • 更新Session:用户活动时,更新Redis中存储的session信息,以保持其活跃状态。
  • 过期策略:设置session信息在Redis中的过期时间,当用户长时间不活动时自动使session失效。
代码实现
go">package mainimport ("context""encoding/json""github.com/go-redis/redis/v8""strconv""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 伪代码:用户登录并存储session
func userLogin(username string, password string) (string, error) {// 验证用户名和密码// 创建session IDsessionID := generateSessionID()// 序列化用户信息userInfo := map[string]string{"username": username}serializedInfo, err := json.Marshal(userInfo)if err != nil {// 处理错误return "", err}// 存储session信息到Redis,设置过期时间err = rdb.Set(ctx, sessionID, string(serializedInfo), time.Duration(30)*time.Minute).Err()if err != nil {// 处理错误return "", err}return sessionID, nil
}// 伪代码:从请求中获取并验证session
func validateSession(sessionID string) (map[string]string, error) {// 从Redis获取session信息serializedInfo, err := rdb.Get(ctx, sessionID).Result()if err != nil {// 处理错误或session不存在return nil, err}// 反序列化用户信息var userInfo map[string]stringerr = json.Unmarshal([]byte(serializedInfo), &userInfo)if err != nil {// 处理错误return nil, err}return userInfo, nil
}// 伪代码:生成新的session ID
func generateSessionID() string {return strconv.FormatInt(time.Now().UnixNano(), 36)
}

http://www.ppmy.cn/ops/108837.html

相关文章

MmAP(论文解读) : Multi-Modal Alignment Prompt for Cross-Domain Multi-Task Learning

MmAP:跨领域多任务学习的多模态提示对齐 AAAI 2024 摘要 多任务学习(Multi-Task Learning,MTL)同时训练多个相关的任务,从而能够提高单个任务的性能。通常,一个多任务网络架构包含共享backbone和任务特定…

oracle锁的机制

文章目录 oracle锁的机制1. 概括2.锁的模式3.锁查看 死锁1. 说明2.死锁产生条件3.解决死锁冲突4. 事务和死锁预防总结 oracle锁的机制 1. 概括 1)说明 锁是一种机制,多个事务同时访问一个数据库对象时,该机制可以实现对并发的控制 2&…

等保2.0测评之Nginx 中间件

前期调研 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器,一般主要功能会有两种,一种作为一个HTTP服务器进行网站的发布处理,另外一种nginx可以作为反向代理进行负载均衡的实现。所以这里填主要功能的时候就要分清。 查看N…

54. Spiral Matrix

Spiral Matrix Given an m x n matrix, return all elements of the matrix in spiral order. 思路:螺旋矩阵的“削水果”法 通过螺旋联想到“削水果”,即将矩阵“削头”(取第一行,并去掉第一行)后旋转90&#xff0…

JAVA并发编程AQS原理剖析

很多小朋友面试时候,面试官考察并发编程部分,都会被问:说一下AQS原理。面对并发编程基础和面试经验,专栏采用通俗简洁无废话无八股文方式,已陆续梳理分享了《一文看懂全部锁机制》、《JUC包之CAS原理》、《volatile核心…

中间件漏洞解析

1.lls漏洞解析 lls6.x 在网站根目录下编写一个一句话木马 然后进行访问 lls7.x 配置环境 将 cgi.fix_pathinfo1 前面的封号取消掉...并重启... 利用姿势 拿蚁剑进行链接 2.Nginx解析漏洞 nginx_parsing 制作图片马并进行上传...获取上传文件地址... 利用Nginx解析漏洞.…

el-table行编辑

需求&#xff1a;单点行编辑并且请求接口更新数据&#xff0c;表格中某几个字段是下拉框取值的&#xff0c;剩下的是文本域&#xff1b;展示的时候 需要区分下拉框编码还是中文 故障模式这个展示的是fault_mode编码,但要显示的文字fault_mode_chn 这点需要注意 <el-tablere…

如何实现过滤器、拦截器和全局异常捕获?

目录 一、实现过滤器 1、创建过滤器 2、注册过滤器 3. 使用注解配置过滤器 4. 在Web.xml中配置过滤器 5. 过滤器的执行顺序 6. 过滤器的链式调用 二、实现拦截器 1、创建拦截器 2. 注册拦截器 3. 使用注解方式注册拦截器 4. 拦截器的执行顺序 5. 拦截器的匹配规则…