深圳做网站建设和维护专员管理层,网站建设 国外,做矿产公司的网站,网站首页制作实验报告Go语言操作Redis
在项目开发中redis的使用也比较频繁#xff0c;本文介绍了Go语言中go-redis库的基本使用。
Redis介绍
Redis是一个开源的内存数据库#xff0c;Redis提供了多种不同类型的数据结构#xff0c;很多业务场景下的问题都可以很自然地映射到这些数据结构上。除…Go语言操作Redis在项目开发中redis的使用也比较频繁本文介绍了Go语言中go-redis库的基本使用。Redis介绍Redis是一个开源的内存数据库Redis提供了多种不同类型的数据结构很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外通过复制、持久化和客户端分片等特性我们可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。Redis使用一种简单而高效的文本协议与客户端进行通信该协议被称为Redis网络协议或RESPRedis Serialization Protocol。RESP是一种直观的协议它以行为单位进行通信并且通过不同的数据类型和前缀来表示不同的指令和数据。RESP协议的主要特点如下简单性RESP协议的设计非常简单易于实现和理解。它使用纯文本格式进行通信每个指令和数据以换行符\r\n分隔。支持不同的数据类型RESP协议支持多种数据类型包括字符串Simple String、错误信息Error、整数Integer、批量字符串Bulk String和数组Array。基于请求-响应模型客户端向Redis发送请求Redis返回相应的响应。请求和响应之间使用简单的消息结构进行交互。RESP协议的基本规则如下简单字符串Simple String以“字符开头后面跟着字符串内容。例如”OK\r\n表示一个简单字符串响应内容为OK。错误信息Error以-“字符开头后面跟着错误消息。例如”-Error occurred\r\n表示一个错误响应内容为Error occurred。整数Integer以:“字符开头后面跟着整数值。例如”:42\r\n表示一个整数响应值为42。批量字符串Bulk String以$“字符开头后面跟着字符串长度和换行符然后是字符串内容和换行符。例如”$5\r\nHello\r\n表示一个长度为5的批量字符串内容为Hello。数组Array以*“字符开头后面跟着数组元素的数量和换行符然后是数组中的每个元素。例如*3\r\n$5\r\nHello\r\n:42\r\n-Error\r\n表示一个包含3个元素的数组分别是批量字符串Hello”、整数42和错误信息Error。RESP协议以文本形式提供了一种与Redis进行通信的简单方式。它使得客户端可以通过发送相应的指令并解析Redis的响应来与Redis进行交互。需要注意的是RESP协议是一种底层协议对于常规的Redis操作通常建议使用Redis客户端库这些库提供了高级的抽象和功能使得与Redis的交互更加方便和易于使用Redis支持的数据结构Redis支持诸如字符串strings、哈希hashes、列表lists、集合sets、带范围查询的排序集合sorted sets、位图bitmaps、hyperloglogs、带半径查询和流的地理空间索引等数据结构geospatial indexes。Redis应用场景缓存系统减轻主数据库MySQL的压力。计数场景比如微博、抖音中的关注数和粉丝数。热门排行榜需要排序的场景特别适合使用ZSET。利用LIST可以实现队列的功能。准备Redis环境这里直接使用Docker启动一个redis环境方便学习使用。docker启动一个名为redis507的5.0.7版本的redis server示例docker run --name redis507 -p 6379:6379 -d redis:5.0.7注意此处的版本、容器名和端口号请根据自己需要设置。启动一个redis-cli连接上面的redis server:docker run -it --network host --rm redis:5.0.7 redis-cligo-redis库安装区别于另一个比较常用的Go语言redis client库redigo我们这里采用https://github.com/go-redis/redis连接Redis数据库并进行操作因为go-redis支持连接哨兵及集群模式的Redis。使用以下命令下载并安装:goget-u github.com/go-redis/redis连接普通连接// 声明一个全局的rdb变量varrdb*redis.Client// 初始化连接funcinitClient()(errerror){rdbredis.NewClient(redis.Options{Addr:localhost:6379,Password:,// no password setDB:0,// use default DB})_,errrdb.Ping().Result()iferr!nil{returnerr}returnnil}注意 最新版本下Ping()可能需要传递context.Context参数例如rdb.Ping(context.TODO())连接Redis哨兵模式funcinitClient()(errerror){rdb:redis.NewFailoverClient(redis.FailoverOptions{MasterName:master,SentinelAddrs:[]string{x.x.x.x:26379,xx.xx.xx.xx:26379,xxx.xxx.xxx.xxx:26379},})_,errrdb.Ping().Result()iferr!nil{returnerr}returnnil}连接Redis集群funcinitClient()(errerror){rdb:redis.NewClusterClient(redis.ClusterOptions{Addrs:[]string{:7000,:7001,:7002,:7003,:7004,:7005},})_,errrdb.Ping().Result()iferr!nil{returnerr}returnnil}基本使用set/get示例funcredisExample(){err:rdb.Set(score,100,0).Err()iferr!nil{fmt.Printf(set score failed, err:%v\n,err)return}val,err:rdb.Get(score).Result()iferr!nil{fmt.Printf(get score failed, err:%v\n,err)return}fmt.Println(score,val)val2,err:rdb.Get(name).Result()iferrredis.Nil{fmt.Println(name does not exist)}elseiferr!nil{fmt.Printf(get name failed, err:%v\n,err)return}else{fmt.Println(name,val2)}}zset示例funcredisExample2(){zsetKey:language_ranklanguages:[]redis.Z{redis.Z{Score:90.0,Member:Golang},redis.Z{Score:98.0,Member:Java},redis.Z{Score:95.0,Member:Python},redis.Z{Score:97.0,Member:JavaScript},redis.Z{Score:99.0,Member:C/C},}// ZADDnum,err:rdb.ZAdd(zsetKey,languages...).Result()iferr!nil{fmt.Printf(zadd failed, err:%v\n,err)return}fmt.Printf(zadd %d succ.\n,num)// 把Golang的分数加10newScore,err:rdb.ZIncrBy(zsetKey,10.0,Golang).Result()iferr!nil{fmt.Printf(zincrby failed, err:%v\n,err)return}fmt.Printf(Golangs score is %f now.\n,newScore)// 取分数最高的3个ret,err:rdb.ZRevRangeWithScores(zsetKey,0,2).Result()iferr!nil{fmt.Printf(zrevrange failed, err:%v\n,err)return}for_,z:rangeret{fmt.Println(z.Member,z.Score)}// 取95~100分的op:redis.ZRangeBy{Min:95,Max:100,}ret,errrdb.ZRangeByScoreWithScores(zsetKey,op).Result()iferr!nil{fmt.Printf(zrangebyscore failed, err:%v\n,err)return}for_,z:rangeret{fmt.Println(z.Member,z.Score)}}输出结果如下$ ./06redis_demo zadd 0 succ. Golangs score is 100.000000 now. Golang 100 C/C 99 Java 98 JavaScript 97 Java 98 C/C 99 Golang 100PipelinePipeline主要是一种网络优化。它本质上意味着客户端缓冲一堆命令并一次性将它们发送到服务器。这些命令不能保证在事务中执行。这样做的好处是节省了每个命令的网络往返时间RTT。Pipeline基本示例如下pipe:rdb.Pipeline()incr:pipe.Incr(pipeline_counter)pipe.Expire(pipeline_counter,time.Hour)_,err:pipe.Exec()fmt.Println(incr.Val(),err)上面的代码相当于将以下两个命令一次发给redis server端执行与不使用Pipeline相比能减少一次RTT。INCR pipeline_counter EXPIRE pipeline_counts 3600也可以使用Pipelinedvarincr*redis.IntCmd_,err:rdb.Pipelined(func(pipe redis.Pipeliner)error{incrpipe.Incr(pipelined_counter)pipe.Expire(pipelined_counter,time.Hour)returnnil})fmt.Println(incr.Val(),err)在某些场景下当我们有多条命令要执行时就可以考虑使用pipeline来优化。事务Redis是单线程的因此单个命令始终是原子的但是来自不同客户端的两个给定命令可以依次执行例如在它们之间交替执行。但是Multi/exec能够确保在multi/exec两个语句之间的命令之间没有其他客户端正在执行命令。在这种场景我们需要使用TxPipeline。TxPipeline总体上类似于上面的Pipeline但是它内部会使用MULTI/EXEC包裹排队的命令。例如pipe:rdb.TxPipeline()incr:pipe.Incr(tx_pipeline_counter)pipe.Expire(tx_pipeline_counter,time.Hour)_,err:pipe.Exec()fmt.Println(incr.Val(),err)上面代码相当于在一个RTT下执行了下面的redis命令MULTI INCR pipeline_counter EXPIRE pipeline_counts 3600 EXEC还有一个与上文类似的TxPipelined方法使用方法如下varincr*redis.IntCmd_,err:rdb.TxPipelined(func(pipe redis.Pipeliner)error{incrpipe.Incr(tx_pipelined_counter)pipe.Expire(tx_pipelined_counter,time.Hour)returnnil})fmt.Println(incr.Val(),err)Watch在某些场景下我们除了要使用MULTI/EXEC命令外还需要配合使用WATCH命令。在用户使用WATCH命令监视某个键之后直到该用户执行EXEC命令的这段时间里如果有其他用户抢先对被监视的键进行了替换、更新、删除等操作那么当用户尝试执行EXEC的时候事务将失败并返回一个错误用户可以根据这个错误选择重试事务或者放弃事务。Watch(fnfunc(*Tx)error,keys...string)errorWatch方法接收一个函数和一个或多个key作为参数。基本使用示例如下// 监视watch_count的值并在值不变的前提下将其值1key:watch_counterrclient.Watch(func(tx*redis.Tx)error{n,err:tx.Get(key).Int()iferr!nilerr!redis.Nil{returnerr}_,errtx.Pipelined(func(pipe redis.Pipeliner)error{pipe.Set(key,n1,0)returnnil})returnerr},key)最后看一个官方文档中使用GET和SET命令以事务方式递增Key的值的示例constroutineCount100increment:func(keystring)error{txf:func(tx*redis.Tx)error{// 获得当前值或零值n,err:tx.Get(key).Int()iferr!nilerr!redis.Nil{returnerr}// 实际操作乐观锁定中的本地操作n// 仅在监视的Key保持不变的情况下运行_,errtx.Pipelined(func(pipe redis.Pipeliner)error{// pipe 处理错误情况pipe.Set(key,n,0)returnnil})returnerr}forretries:routineCount;retries0;retries--{err:rdb.Watch(txf,key)iferr!redis.TxFailedErr{returnerr}// 乐观锁丢失}returnerrors.New(increment reached maximum number of retries)}varwg sync.WaitGroup wg.Add(routineCount)fori:0;iroutineCount;i{gofunc(){deferwg.Done()iferr:increment(counter3);err!nil{fmt.Println(increment error:,err)}}()}wg.Wait()n,err:rdb.Get(counter3).Int()fmt.Println(ended with,n,err)更多详情请查阅文档。原文地址也是我自己写的Go语言操作Redis