Go新项目-调研关于go项目中redis的使用场景,lua实战(7)

Go新项目-调研关于go项目中redis的使用场景,lua实战(7)_第1张图片

文章目录

      • 参考地址
      • redis的使用场景的解释
        • 1、缓存
        • 2、排行榜
        • 3、计数器
        • 4、分布式会话
        • 5、分布式锁
        • 6、社交网络
        • 7、最新列表
        • 8、消息系统
        • 9、地理位置
        • 10、搜索引擎
      • 常用命令
      • 实际场景
        • 1、缓存
        • 2、数据共享分布式
        • 3、分布式锁
        • 4、全局ID
        • 5、计数器
        • 6、限流
        • 7、位统计
        • 8、购物车
        • 9、用户消息时间线timeline
        • 10、消息队列
        • 11、抽奖
        • 12、点赞、签到、打卡
        • 13、商品标签
        • 14、商品筛选
        • 15、用户关注、推荐模型
        • 16、排行榜
      • go的incr+expire原子性操作之调用lua脚本

参考地址

  • https://juejin.cn/post/7079756129433370638
  • https://blog.csdn.net/gaogaoshan/article/details/41039581
  • https://redis.io/docs/clients/go/

redis的使用场景的解释

下面一一来分析下Redis的应用场景都有哪些。

1、缓存

缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非常多。

2、排行榜

很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。

3、计数器

什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,
并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。

4、分布式会话

集群模式下,在应用不多的情况下一般使用容器自带的session复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以Redis等内存数据库为中心
的session服务,session不再由容器管理,而是由session服务及内存数据库管理。

5、分布式锁

在很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局ID、减库存、秒杀等场景,
并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,
大大影响了数据库的性能。可以利用Redis的setnx功能来编写分布式的锁,如果设置返回1说明获取锁成功,否则获取锁失败,实际应用中要考虑的细节要更多。

6、社交网络

点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据,
Redis提供的哈希、集合等数据结构能很方便的的实现这些功能。

7、最新列表

Redis列表结构,LPUSH可以在列表头部插入一个内容ID作为关键字,LTRIM可用来限制列表的数量,这样列表永远为N个ID,无需查询最新的列表,
直接根据ID去到对应的内容页即可。

8、消息系统

消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。
Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。

9、地理位置

使用Redis的Geo数据类型可以存储地理位置信息,实现地理位置相关的查询和分析。

10、搜索引擎

Redis的Sorted Set可以用来存储有序数据,例如排行榜、搜索结果等。
在Go语言中,可以使用ZADD、ZRANGE等命令来实现对Sorted Set的操作,实现搜索引擎的功能。

常用命令

  • SET 和 GET:用于设置和获取字符串类型的值;
  • HSET、HGET、HMSET、HMGET:用于设置和获取哈希类型的值;
  • LPUSH、RPUSH、LPOP、RPOP:用于操作列表类型的值;
  • SADD、SREM、SMEMBERS:用于操作集合类型的值;
  • ZADD、ZREM、ZRANGE:用于操作有序集合类型的值;
  • INCR、INCRBY、DECR、DECRBY:用于对整数类型的值进行原子性的加减操作;
  • EXPIRE、TTL:用于设置和查询键的过期时间;
  • PUBLISH、SUBSCRIBE:用于实现 Redis 的发布订阅功能;
  • MULTI、EXEC、WATCH、UNWATCH:用于实现 Redis 的事务功能;
  • SCAN:用于迭代大量的键,避免阻塞 Redis 服务器。

实际场景

  • 缓存
  • 数据共享分布式
  • 分布式锁
  • 全局ID
  • 计数器
  • 限流
  • 位统计
  • 购物车
  • 用户消息时间线timeline
  • 消息队列
  • 抽奖
  • 点赞、签到、打卡
  • 商品标签
  • 商品筛选
  • 用户关注、推荐模型
  • 排行榜
1、缓存

String类型

例如:热点数据缓存(例如报表、明星出轨),对象缓存、全页缓存、可以提升热点数据的访问数据。

2、数据共享分布式

String 类型,因为 Redis 是分布式的独立服务,可以在多个应用之间共享

例如:分布式Session

 
 org.springframework.session 
 spring-session-data-redis 

3、分布式锁

String 类型setnx方法,只有不存在时才能添加成功,返回true

public static boolean getLock(String key) {
Long flag = jedis.setnx(key, "1");
if (flag == 1) {
jedis.expire(key, 10);
}
return flag == 1;
}

public static void releaseLock(String key) {
jedis.del(key);
}
4、全局ID

int类型,incrby,利用原子性

incrby userid 1000

分库分表的场景,一次性拿一段

5、计数器
int类型,incr方法

例如:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库

6、限流

int类型,incr方法

以访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false

7、位统计

String类型的bitcount(1.6.6的bitmap数据结构介绍)

字符是以8位二进制存储的

set k1 a
setbit k1 6 1
setbit k1 7 0
get k1
/* 6 7 代表的a的二进制位的修改
a 对应的ASCII码是97,转换为二进制数据是01100001
b 对应的ASCII码是98,转换为二进制数据是01100010

因为bit非常节省空间(1 MB=8388608 bit),可以用来做大数据量的统计。
*/

例如:在线用户统计,留存用户统计

setbit onlineusers 01
setbit onlineusers 11
setbit onlineusers 20

支持按位与、按位或等等操作

BITOPANDdestkeykey[key...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。       
BITOPORdestkeykey[key...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOPXORdestkeykey[key...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
BITOPNOTdestkeykey ,对给定 key 求逻辑非,并将结果保存到 destkey 。

计算出7天都在线的用户

BITOP "AND" "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ...  "day_7_online_users"
8、购物车

String 或hash。所有String可以做的hash都可以做

key:用户id;field:商品id;value:商品数量。
+1:hincr。-1:hdecr。删除:hdel。全选:hgetall。商品数:hlen。
9、用户消息时间线timeline

list,双向链表,直接作为timeline就好了。插入有序

10、消息队列

List提供了两个阻塞的弹出操作:blpop/brpop,可以设置超时时间

blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作。其实就是java的阻塞队列。学习的东西越多。学习成本越低
  • 队列:先进先除:rpush blpop,左头右尾,右边进入队列,左边出队列
  • 栈:先进后出:rpush brpop
11、抽奖

自带一个随机获得值

spop myset
12、点赞、签到、打卡

假如上面的微博ID是t1001,用户ID是u3001

用 like:t1001 来维护 t1001 这条微博的所有点赞用户

  • 点赞了这条微博:sadd like:t1001 u3001
  • 取消点赞:srem like:t1001 u3001
  • 是否点赞:sismember like:t1001 u3001
  • 点赞的所有用户:smembers like:t1001
  • 点赞数:scard like:t1001
13、商品标签

老规矩,用 tags:i5001 来维护商品所有的标签。

sadd tags:i5001 画面清晰细腻
sadd tags:i5001 真彩清晰显示屏
sadd tags:i5001 流程至极
14、商品筛选
// 获取差集
sdiff set1 set2
// 获取交集(intersection)
sinter set1 set2
// 获取并集
sunion set1 set2

假如:iPhone15 上市了

sadd brand:apple iPhone11
sadd brand:ios iPhone11
sad screensize:6.0-6.24 iPhone11
sad screentype:lcd iPhone 11

筛选商品,苹果的、ios的、屏幕在6.0-6.24之间的,屏幕材质是LCD屏幕

sinter brand:apple brand:ios screensize:6.0-6.24 screentype:lcd
15、用户关注、推荐模型

follow 关注 fans 粉丝

相互关注:

sadd 1:follow 2
sadd 2:fans 1
sadd 1:fans 2
sadd 2:follow 1

我关注的人也关注了他(取交集):

sinter 1:follow 2:fans

可能认识的人:

用户1可能认识的人(差集):sdiff 2:follow 1:follow
用户2可能认识的人:sdiff 1:follow 2:follow
16、排行榜

id 为6001 的新闻点击数加1:

zincrby hotNews:20190926 1 n6001

获取今天点击最多的15条:

zrevrange hotNews:20190926 0 15 withscores

go的incr+expire原子性操作之调用lua脚本

  • 它实现了 incr 和 expire 的原子化操作,并且在 key 不存在和 key 已经过期的情况下也能正常工作。
package main

import (
   "context"
   "fmt"
   "github.com/go-redis/redis/v8"
   "time"
)

// Lua脚本
// 通过执行一个 Lua 脚本来实现原子化的 incr 和 expire 操作。如果该 key 的过期时间已经设置为 -1,
// 则表示该 key 没有过期时间,此时通过 expire 命令为该 key 设置过期时间。
func main(){
   client := redis.NewClient(&redis.Options{
       Addr:     "localhost:6379",
       Password: "123456",
       DB:       10,
   })
   defer client.Close()

   result1, err1 := IncrWithTTL(client, "counter", 3600)
   if err1 != nil {
       fmt.Println(err1)
   } else {
       fmt.Println(result1)
   }
}

func IncrWithTTL(client *redis.Client, key string, ttl time.Duration) (int64, error) {
    script := redis.NewScript(`
    local value = redis.call("incr", KEYS[1])
                if redis.call("ttl", KEYS[1]) == -1 then
                redis.call("expire", KEYS[1], ARGV[1])
                end
                return value
    `)
    result, err := script.Run(context.Background(), client, []string{key}, ttl.String()).Result()
    if err != nil {
         return 0, err
    }
    return result.(int64), nil
}

你可能感兴趣的:(Go,golang,redis,lua)