redis实战——go-redis的使用与redis基础数据类型的使用场景(二)

一.go-redis操作hash

常用命令:

redisClient.HSet("map", "name", "jack")
// 批量设置
redisClient.HMSet("map", map[string]interface{}{"a": "b", "c": "d", "e": "f"})
// 单个访问
redisClient.HGet("map", "a").Val()
// 批量访问
redisClient.HMGet("map", "a", "b").Val()
// 获取整个map
redisClient.HGetAll("map").Val()
// 删除map的一个字段
redisClient.HDel("map", "a")
// 判断字段是否存在
redisClient.HExists("map", "a")
// 获取所有的map的键
redisClient.HKeys("map")
// 获取map长度
redisClient.HLen("map")
// 遍历map中的键值对
redisClient.HScan("map", 0, "", 1)

Hash的常用场景主要有两种:

  • 缓存对象
  • 做购物车

首先是缓存对象,hash命令中的key,valuefiled很好的能够对应对象的结构我们可以利用Hash来缓存结构,比如像下面我写了一个json文件,我们来看如何将它缓存起来:

[{"message":{"name": "张三","age": 30,"email": "zhangsan@example.com","isStudent": false,"subjects": ["数学", "英语", "物理"]}},{"message": {"name": "李四","age": 25,"email": "lisi@example.com","isStudent": true,"subjects": ["化学", "生物"]}}
]

缓存的代码如下:

package mainimport ("context""encoding/json""fmt""github.com/redis/go-redis/v9"snoyflake "go-redis/sonyflake""io""os""strconv"
)type Student struct {id      uint64Message map[string]any `json:"message"`
}var rdb *redis.Client
var ctx context.Contextfunc Init() {rdb = redis.NewClient(&redis.Options{Addr:     "127.0.0.1:6379",Password: "",DB:       0,})err := snoyflake.Init()if err != nil {fmt.Println("sonyflake init failed,err:", err)return}ctx = context.Background()
}func main() {//初始化Init()var students []Student//读取json文件file, err := os.Open("student.json")if err != nil {fmt.Println("file Open failed,err:", err)}defer file.Close()str, _ := io.ReadAll(file)err = json.Unmarshal(str, &students)if err != nil {fmt.Println("json Unmarshal failed,err:", err)return}//生成idfor _, student := range students {student.id, _ = snoyflake.GetID()}//写入redisfiled := "message"for _, student := range students {value, _ := json.Marshal(student.Message)err = rdb.HSet(ctx, strconv.FormatUint(student.id, 10), filed, value).Err()if err != nil {fmt.Println("redis HSet failed,err:", err)return}}//读取redisfor _, student := range students {value, err := rdb.HGet(ctx, strconv.FormatUint(student.id, 10), filed).Result()if err != nil {fmt.Println("redis HGet failed,err:", err)return}fmt.Println(value)}
}

运行结果:

{"age":25,"email":"lisi@example.com","isStudent":true,"name":"李四","subjects":["化学","生物"]}
{"age":25,"email":"lisi@example.com","isStudent":true,"name":"李四","subjects":["化学","生物"]}

那购物车我们应该怎么做呢?假设我们现在要清空我们的购物车,购物车其实就三个属性:

  • 谁买
  • 买什么
  • 买多少个

知道了这个我们就可以尝试实现一个简单的购物车了,这里我们选择将RedisMysql联合使用,将商品的具体信息储存在Mysql中,Redis中只实现购物车的相关功能:

首先我们生成一个表来存储商品信息:
在这里插入图片描述
然后我们现在模拟一个场景:

  • 我们将商品将入购物车
  • 我们添加购物车中商品的数量
  • 计算总价格

这个基本上就是一整个大致流程了,我们来看一下怎么实现:

package mainimport ("context""database/sql""encoding/json""fmt""github.com/fsnotify/fsnotify""github.com/redis/go-redis/v9""github.com/spf13/viper"snoyflake "go-redis/sonyflake""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/schema""io""os""strconv""time"
)type MySQL struct {DataName     stringDataUser     stringDataPassword stringDataHost     stringDataPort     string
}type Redis struct {Addr     stringPassword stringDB       int
}type Setting struct {MySQL MySQLRedis Redis
}// 用户
type User struct {UserID int     `gorm:"primaryKey;autoIncrement"`Price  float64 `gorm:"type:float;not null"`
}// 商品
type Goods struct {GoodsID       int     `gorm:"primaryKey;autoIncrement"`ProductName   string  `gorm:"type:varchar(255);not null"`Category      string  `gorm:"type:varchar(50);not null"`Brand         string  `gorm:"type:varchar(50);not null"`Price         float64 `gorm:"type:decimal(12,2);not null"`StockQuantity int     `gorm:"not null"`Description   string  `gorm:"type:text"`ListingDate   string  `gorm:"type:date"`
}type Message struct {Userid  int `json:"用户ID"`Goodsid int `json:"商品ID"`Number  int `json:"数量"`
}var ConfMessage = new(Setting)
var db *gorm.DB
var rdb *redis.Client
var ctx context.Context
var messages []Message
var userlist []string
var costlist map[string]float64 = make(map[string]float64)func main() {//初始化Init()//读取json文件file, err := os.Open("shopping.json")if err != nil {fmt.Println("file Open failed,err:", err)}defer file.Close()str, _ := io.ReadAll(file)err = json.Unmarshal(str, &messages)if err != nil {fmt.Println("json Unmarshal failed,err:", err)return}//写入redisfor _, message := range messages {userlist = append(userlist, strconv.Itoa(message.Userid))filed := strconv.Itoa(message.Goodsid)err = rdb.HSet(ctx, strconv.Itoa(message.Userid), filed, message.Number).Err()if err != nil {fmt.Println("redis HSet failed,err:", err)return}}//选择其中某种商品添加一定数量rdb.HIncrBy(ctx, "1001", "1001", 3)//打印一下查看是否操作成功fmt.Println(rdb.HGetAll(ctx, "1001").Val())//计算总价格for _, userid := range userlist {cost := TotalPrice(userid)costlist[userid] = cost}for k, v := range costlist {fmt.Println(k, v)}
}func TotalPrice(userid string) float64 {var user Userres, _ := rdb.HGetAll(ctx, userid).Result()for k, v := range res {id, _ := strconv.Atoi(k)number, _ := strconv.Atoi(v)goods := Goods{}db.Where("goods_id = ?", id).First(&goods)db.Where("user_id = ?", userid).First(&user)user.Price += goods.Price * float64(number)}return user.Price
}func InitConfig() error {viper.AddConfigPath(".")viper.SetConfigName("config")viper.SetConfigType("ini")err := viper.ReadInConfig()if err != nil {return err}if err = viper.Unmarshal(&ConfMessage); err != nil {return err}viper.WatchConfig()viper.OnConfigChange(func(e fsnotify.Event) {if err = viper.Unmarshal(&ConfMessage); err != nil {return}})return nil
}// 初始化redis
func InitRedis() error {rdb = redis.NewClient(&redis.Options{Addr:     "127.0.0.1:6379",Password: "",DB:       0,})ctx = context.Background()_, err := rdb.Ping(ctx).Result()if err != nil {return err}return nil
}// 初始化mysql
func InitMysql() error {dns := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",ConfMessage.MySQL.DataUser,ConfMessage.MySQL.DataPassword,ConfMessage.MySQL.DataHost,ConfMessage.MySQL.DataPort,ConfMessage.MySQL.DataName)var err errordb, err = gorm.Open(mysql.Open(dns), &gorm.Config{//跳过默认事务,提高性能SkipDefaultTransaction: true,//禁用外键约束DisableForeignKeyConstraintWhenMigrating: true,NamingStrategy: schema.NamingStrategy{//禁用默认表名复数SingularTable: true,},})if err != nil {fmt.Println("连接数据库失败", err)os.Exit(1)}var sqlDB *sql.DBsqlDB, err = db.DB()if err != nil {return err}_ = db.AutoMigrate(&User{}, &Goods{})//设置连接池最大连接数量sqlDB.SetMaxOpenConns(100)//设置连接池最大空闲连接数sqlDB.SetMaxIdleConns(10)//设置连接连接可重用的最大时长sqlDB.SetConnMaxLifetime(10 * time.Second)return nil
}func Init() {err := snoyflake.Init()if err != nil {fmt.Println("sonyflake init failed,err:", err)return}err = InitConfig()if err != nil {fmt.Println("config init failed,err:", err)return}err = InitMysql()if err != nil {fmt.Println("mysql init failed,err:", err)return}err = InitRedis()if err != nil {fmt.Println("redis init failed,err:", err)return}
}

这个代码有点长我们根据main函数中的逻辑来顺一下:

  • Init:首先在这个函数我们完成了对相关工具的初始化,主要有以下几步:
    1.初始化雪花算法
    2.初始化相关配置,这里我们选择的是viper来读取配置文件
    3.初始化redis
    4.利用gorm连接mysql数据库,完成对`mysql的初始化

  • 读取json文件:这里我们将相关的信息存储在json文件中来模拟购物车初始消息:json文件内容如下:

[{"用户ID": 1001,"商品ID": 1001,"数量": 1},{"用户ID": 1001,"商品ID": 1003,"数量": 1},{"用户ID": 1002,"商品ID": 1002,"数量": 1},{"用户ID": 1002,"商品ID": 1004,"数量": 1},{"用户ID": 1002,"商品ID": 1005,"数量": 1}
]

我们将读取后存储到redis来作为对相关信息的缓存(如果大家想更贴合实际环境,可以添加一个过期时间)

  • 相关操作:最后我们模拟了我们平时增加/减少购买数量的操作并且通过解析redis中的相关信息并在mysql中查询实现了结账操作,完成了一个购物车的基本功能。

二.go-redis操作Set

常用命令:

 // 往一个集合里面添加元素
redisClient.SAdd("set", "a", "b", "c")
// 获取集合中的所有成员
redisClient.SMembers("set")
// 判断一个元素是否属于这个集合
redisClient.SIsMember("set", "a")
// 随机返回count个元素
redisClient.SRandMemberN("set", 1)
// 获取一个集合的元素个数
redisClient.SCard("set")
// 获取集合中的所有成员
redisClient.SMembers("set")
// 判断一个元素是否属于这个集合
redisClient.SIsMember("set", "a")
// 随机返回count个元素
redisClient.SRandMemberN("set", 1)
// 获取一个集合的元素个数
redisClient.SCard("set")
// 弹出并删除该元素
redisClient.SPop("set")
// 弹出并删除N给元素
redisClient.SPopN("set", 2)
// 从源集合移动指定元素刀目标集合
redisClient.SMove("set", "set2", "a")
// 删除指定元素
redisClient.SRem("set", "a", "b")
// 遍历集合
redisClient.SScan("set", 0, "", 2)

集合主要有以下的特性:

  • 无序
  • 无重复的元素
  • 支持并交差等操作

比较适合用来数据去重和保障数据的唯一性,还可以用来统计多个集合的交集、错集和并集等,当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。

注意: Set进行聚合计算(交集、差集、并集)时复杂度较大(>=N),在数据量比较大的时候,任意造成Redis实例阻塞,为了解决这种情况我们一般会选择一个从库完成聚合统计,或者把数据返回给客户端,由客户端来完成聚合统计。

让我们在使用Set中一般会在以下场景中使用:

  • 点赞
  • 共同关注
  • 抽奖

这里我们以点赞功能为例,我们来看一下我们可以如何实现一个点赞功能:

package mainimport ("context""fmt""github.com/redis/go-redis/v9"
)var (rdb *redis.Clientctx context.Context
)func main() {err := InitRedis()if err != nil {fmt.Println("redis init failed,err:", err)}Upvote("1", "1")Upvote("2", "1")Upvote("3", "1")str, _ := GetUpvote("1")fmt.Println(str)count, _ := GetUpvoteCount("1")fmt.Println(count)CancelUpvote("1", "1")str, _ = GetUpvote("1")fmt.Println(str)count, _ = GetUpvoteCount("1")fmt.Println(count)
}// Upvote 点赞
func Upvote(userid, articleid string) error {return rdb.SAdd(ctx, articleid, userid).Err()
}// CancelUpvote 取消点赞
func CancelUpvote(userid, articleid string) error {return rdb.SRem(ctx, articleid, userid).Err()
}// GetUpvoteCount 获取点赞数
func GetUpvoteCount(articleid string) (int64, error) {return rdb.SCard(ctx, articleid).Result()
}// GetUpvote 获取点赞列表
func GetUpvote(articleid string) ([]string, error) {return rdb.SMembers(ctx, articleid).Result()
}// GetVoteStatus 获取点赞状态
func GetVoteStatus(userid, articleid string) (bool, error) {return rdb.SIsMember(ctx, articleid, userid).Result()
}func InitRedis() error {rdb = redis.NewClient(&redis.Options{Addr:     "127.0.0.1:6379",Password: "",DB:       0,})ctx = context.Background()_, err := rdb.Ping(ctx).Result()if err != nil {return err}return nil
}

go-redis操作zset

常用命令如下:

// 往有序集合中加入元素
redisClient.ZAdd("ss", redis.Z{Score:  1,Member: "a",
}, redis.Z{Score:  2,Member: "b",
})
// 返回有序集合中该元素的排名,从低到高排列
redisClient.ZRank("ss", "1")
// 返回有序集合中该元素的排名,从高到低排列
redisClient.ZRevRank("ss", "1")
// 返回介于min和max之间的成员数量
redisClient.ZCount("ss", "1", "2")// 返回对元素的权值
redisClient.ZScore("ss", "a")// 返回指定区间的元素
redisClient.ZRange("ss", 1, 2)
// 返回介于min和max之间的所有成员列表
redisClient.ZRangeByScore("ss", redis.ZRangeBy{Min:    "1",Max:    "2",Offset: 0,Count:  1,
})
// 给一个对应的元素增加相应的权值
redisClient.ZIncr("ss", redis.Z{Score:  2,Member: "b",
})
// 删除指定元素
redisClient.ZRem("ss", "a")
// 删除指定排名区间的元素
redisClient.ZRemRangeByRank("ss", 1, 2)
// 删除权值在min和max区间的元素
redisClient.ZRemRangeByScore("ss", "1", "2")

Zset 类型(Sorted Set,有序集合) 可以根据元素的权重来排序,我们可以自己来决定每个元素的权重值。在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,可以优先考虑使用 Sorted Set。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/407839.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计选题推荐-游戏比赛网上售票系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

棚子影院CMS程序PHP源码

01, 棚子影视是我现在最常用的一个看视频的网站,支持观看电影、国漫!动漫,电视剧、综艺、记录片、香港剧等等。同时棚子影视支持手机,PC端在线观看,不用下载任何播放器,直接电脑或者手机打开网址就可以在线…

vue3 RouterLink路由跳转后RouterView组件未加载,页面未显示,且控制台无任何报错

在使用 vue3 开发项目过程中,组件之间使用 router-link 跳转,但是当我开发的组件跳转到其他组件时,其他组件的页面未加载,再跳转回自己的组件时,自己的组件也加载不出来了,浏览器刷新后页面可以加载出来。但…

结合 curl 与住宅代理实现高效数据抓取

引言 什么是 curl?有哪些功能? 基本 curl 命令有哪些? 为什么要使用 curl 处理 HTTP 请求? 如何使用 curl 和住宅代理进行网络抓取? 总结 引言 在当今数据驱动的商业环境中,数据的获取和分析能力是企…

Redis | 非关系型数据库Redis的初步认识

本节内容相对理论,着重看基础通用命令这一节 Redis 非关 kv型{字典} 概念应用ubuntu安装配置 windows添加密码 可能问题【ubuntu】远程连接 基础通用命令 ⭐ 概念 特点: 1、开源的,使用C编写,基于内存且支持持久化 2、没有表 支持…

Linux服务器不能使用su切换到其他用户

1. 现象 使用非root用户登录系统后,想使用su - 切换到root时,输入正确的密码后出现: 一开始以为自己密码记错了,或者是系统被入侵后篡改了root用户密码,服务器上的数据非常重要。后面不通过SSH连接,直接去…

图文详解ThreadLocal:原理、结构与内存泄漏解析

目录 一.什么是ThreadLocal 二.ThreadLocal的内部结构 三.ThreadLocal带来的内存泄露问题 ▐ key强引用 ▐ key弱引用 总结 一.什么是ThreadLocal 在Java中,ThreadLocal 类提供了一种方式,使得每个线程可以独立地持有自己的变量副本,而…

AI副业:别只顾刷黑神话悟空!AI做神话账号,商单月入过万(附教程)

前言 国产3A大作《黑神话:悟空》一经发布,瞬间登顶steam榜首,打破单机游戏在线记录,全球几十万国外玩家听着中文配音,看着翻译过来的英文字幕,玩的不亦乐乎! 甚至国外论坛上兴起了一股西游风&a…

[已解决]mac远程连接windows桌面:parallels client连接遇到的问题

[已解决]mac远程连接windows桌面:parallels client连接遇到的问题 问题一:网络不通问题二:远程windows防火墙导致无法连接问题三:远程桌面服务未启动问题四:家庭版(非专业版)windows导致的无法使…

快排补充(挖坑法,lomuto前后指针,非递归法)

挖坑法 挖坑法动态示意图 挖坑法方法分析 创建左右指针。⾸先从右向左找出⽐基准⼩的数据,找到后⽴即放⼊左边坑中,当前位置变为新 的"坑",然后从左向右找出⽐基准⼤的数据,找到后⽴即放⼊右边坑中,当前位置…

P2P 文件共享:现代网络中的高效文件传输

在互联网的世界中,不同应用程序的数据传输方法各异。P2P文件共享(Peer-to-Peer File Sharing) 作为一种高效的文件传输方式,使得用户可以在没有中央服务器的情况下直接进行文件交换。本文将详细介绍P2P文件共享的基本原理、优势及…

vue3实现系统tab标签页面切换

功能: 支持刷新当前、关闭其他、关闭全部、关闭当前支持打开多个相同path不同路由参数的页面,将fullPath作为路由页面唯一值 UI组件: 使用的是element-plus中的el-tab组件,结构目录如下 代码实现: 下面是 TagsView…

缺失ffmpeg.dll要用什么修复方法?快速恢复丢失的ffmpeg.dll文件

多媒体软件用户常常会遭遇一个提示:系统无法找到ffmpeg.dll文件。这类情况经常在启动视频编辑软件、流媒体播放应用或其他音视频处理工具时出现,导致相关程序无法正确加载和执行。ffmpeg.dll是一种关键的动态链接库文件,负责处理复杂的视频和…

【实战教程】一键升级CentOS 7.9.2009至OpenSSL 1.0.2u:加固你的Linux服务器安全防线!

文章目录 【实战教程】一键升级CentOS 7.9.2009至OpenSSL 1.0.2u:加固你的Linux服务器安全防线!一、 背景二、 升级步骤2.1 检查 OpenSSL 版本2.2 安装 OpenSSL 依赖包2.3 下载 OpenSSL 的新版本2.4 解压缩下载的文件2.5 编译并安装 OpenSSL2.5.1 切换到…

linux系统编程:网络通信

1.网络 1.粘包 tcp特点 面向连接 字节流(TCP 将数据视为连续的字节流,没有明确的消息边界。会发生粘包问题。 避免粘包 特殊分隔符:在消息间加入特殊的分隔符(如换行符或其他特殊字符),接收方根据分…

大模型时代的AI应用开发,可以不用,但必须会

成熟的“格子衫”和年轻的“脸庞”,与开发者有关的大会总是少不了这两种元素,Create 2024百度AI开发者大会也不例外。 过去几十年,层出不穷的编程语言、框架等新技术,把一代又一代年轻的脸庞,塑造为成熟的格子衫&…

技术前沿:WebRTC与H.265编码的兼容性挑战与应对策略

WebRTC(Web Real-Time Communication)是一种支持网页浏览器进行实时语音通话、视频聊天以及P2P文件共享的技术。然而,标准的WebRTC API在大多数浏览器中默认并不支持H.265(也称为HEVC,高效视频编码)编码。这…

3D打印的模具镶件性能究竟如何?

随着模具制造业的快速发展,3D打印技术凭借其独特优势,在模具随形水路设计、异形模具制造及模具排气结构优化等方面大放异彩,赢得了注塑、压铸等行业的广泛关注。然而,新技术带来的材料变革让不少人对3D打印模具的性能持观望态度—…

超全大模型训练流程,教你如何训练自己的大模型

“大模型的核心主要有两部分,一是训练数据,二是机器学习模型。” 现在大模型发展得如火如荼,但是没有学过人工智能技术的开发者,只会调用其接口,但不清楚怎么训练一个大模型。 今天就简单介绍一下自己的理解&#xf…

算法日记day 46(单调栈之下一个更大元素|柱状图中最大图形)

一、下一个更大元素1 题目: nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。 给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。 对于每个 0 …