Redis的String数据结构是一种基础的键值对类型。
key的值。如果key已经存在,这个命令会更新它的值。SET myKey "myValue"
key关联的值。GET myKey
key。DEL myKey
INCR key - 将key中的数值增加1。如果key不存在,它将首先被设置为0。
INCR mycounter
DECR key - 将key中的数值减少1。
DECR mycounter
缓存功能:String类型常用于缓存经常访问的数据,如数据库查询结果、网页内容等,以提高访问速度和降低数据库的压力 。
在商品系统中,商品的详细信息如描述、价格、库存等数据通常不会频繁变动,但会被频繁查询。每次用户访问商品详情时,都直接从数据库查询这些信息会导致不必要的数据库负载。
使用Redis String类型来缓存商品的静态信息。当商品信息更新时,相应的缓存也更新或失效。
// 商品信息缓存键的生成
func generateProductCacheKey(productID string) string {
return "product:" + productID
}
// 将商品信息存储到Redis缓存中
func cacheProductInfo(productID string, productInfo map[string]interface{}) {
cacheKey := generateProductCacheKey(productID)
// 序列化商品信息为JSON格式
productJSON, _ := json.Marshal(productInfo)
// 将序列化后的商品信息存储到Redis
rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不过期,实际使用时可以设置过期时间
}
// 从Redis缓存中获取商品信息
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {
cacheKey := generateProductCacheKey(productID)
// 从Redis获取商品信息
productJSON, err := rdb.Get(ctx, cacheKey).Result()
if err != nil {
return nil, err
}
// 反序列化JSON格式的商品信息
var productInfo map[string]interface{}
json.Unmarshal([]byte(productJSON), &productInfo)
return productInfo, nil
}
// 当商品信息更新时,同步更新Redis缓存
func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {
// 更新数据库中的商品信息
// 更新Redis缓存中的商品信息
cacheProductInfo(productID, newProductInfo)
}
计数器:利用INCR和DECR命令,String类型可以作为计数器使用,适用于统计如网页访问量、商品库存数量等 。
对于文章的浏览量的统计,每篇博客文章都有一个唯一的标识符(例如,文章ID)。每次文章被访问时,文章ID对应的浏览次数在Redis中递增。可以定期将浏览次数同步到数据库,用于历史数据分析。
通过Redis实现对博客文章浏览次数的原子性递增和检索,以优化数据库访问并实时更新文章的浏览统计信息。
// recordArticleView 记录文章的浏览次数
func recordArticleView(articleID string) {
// 使用Redis的INCR命令原子性地递增文章的浏览次数
result, err := redisClient.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, result)
}
// getArticleViewCount 从Redis获取文章的浏览次数
func getArticleViewCount(articleID string) (int, error) {
// 从Redis获取文章的浏览次数
viewCount, err := redisClient.Get(ctx, articleID).Result()
if err != nil {
if err == redis.Nil {
// 如果文章ID在Redis中不存在,可以认为浏览次数为0
return 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
}
}
分布式锁:通过SETNX命令(仅当键不存在时设置值),String类型可以实现分布式锁,保证在分布式系统中的互斥访问 。
在分布式系统中,如电商的秒杀活动或库存管理,需要确保同一时间只有一个进程或线程可以修改共享资源,以避免数据不一致的问题。
使用Redis的SETNX命令实现分布式锁的获取和释放,通过Lua脚本确保释放锁时的原子性,并在执行业务逻辑前尝试获取锁,业务逻辑执行完毕后确保释放锁,从而保证在分布式系统中对共享资源的安全访问。
// 伪代码:在分布式系统中实现分布式锁的功能
// 尝试获取分布式锁
func tryGetDistributedLock(lockKey string, val string, expireTime int) bool {
// 使用SET命令结合NX和PX参数尝试获取锁
// NX表示如果key不存在则可以设置成功
// PX指定锁的超时时间(毫秒)
// 这里的val是一个随机值,用于在释放锁时验证锁是否属于当前进程
result, err := redisClient.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 == 1
}
// 释放分布式锁
func releaseDistributedLock(lockKey string, val string) {
// 使用Lua脚本来确保释放锁的操作是原子性的
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
// 执行Lua脚本
result, err := redisClient.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)
}
限流:使用EXPIRE命令,结合INCR操作,可以实现API的限流功能,防止系统被过度访问 。
一个在线视频平台提供了一个API,用于获取视频的元数据。在高流量事件(如新电影发布)期间,这个API可能会收到大量并发请求,这可能导致后端服务压力过大,甚至崩溃。
INCR命令对特定的key进行递增操作。EXPIRE命令为计数key设置一个过期时间,过期时间取决于限流的时间窗口(例如1秒)。// 伪代码:API限流器
func rateLimiter(apiKey string, threshold int, timeWindow int) bool {
currentCount, err := redisClient.Incr(ctx, apiKey).Result()
if err != nil {
log.Printf("Error incrementing API key %s: %v", apiKey, err)
return false
}
// 如果当前计数超过阈值,则拒绝请求
if currentCount > threshold {
return false
}
// 重置计数器的过期时间
_, err = redisClient.Expire(ctx, apiKey, 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 {
// 限流,返回错误或提示信息
}
}
在多服务器的Web应用中,用户在不同的服务器上请求时能够保持登录状态,实现会话共享。
考虑一个大型电商平台,它使用多个服务器来处理用户请求以提高可用性和伸缩性。当用户登录后,其会话信息(session)需要在所有服务器间共享,以确保无论用户请求到达哪个服务器,都能识别其登录状态。
使用Redis的String类型来集中存储和管理用户session信息。
// 伪代码:用户登录并存储session
func userLogin(username string, password string) (string, error) {
// 验证用户名和密码
// 创建session ID
sessionID := generateSessionID()
// 序列化用户信息
userInfo := map[string]string{"username": username}
serializedInfo, err := json.Marshal(userInfo)
if err != nil {
// 处理错误
return "", err
}
// 存储session信息到Redis,设置过期时间
err = redisClient.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 := redisClient.Get(ctx, sessionID).Result()
if err != nil {
// 处理错误或session不存在
return nil, err
}
// 反序列化用户信息
var userInfo map[string]string
err = 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)
}
Redis的List数据结构是一个双向链表,它支持在头部或尾部添加和删除元素,使其成为实现栈(后进先出)或队列(先进先出)的理想选择。
LPUSH key value - 在列表的头部插入元素。
LPUSH mylist "item1"
RPUSH key value - 在列表的尾部插入元素。
RPUSH mylist "item2"
LPOP key - 移除并获取列表头部的元素。
LPOP mylist
RPOP key - 移除并获取列表尾部的元素。
RPOP mylist
LRANGE key start stop - 获取列表中指定范围内的元素。
LRANGE mylist 0 -1
消息队列:List类型常用于实现消息队列,用于异步处理任务,如邮件发送队列、任务调度等。
在一个电商平台中,用户下单后,系统需要执行多个异步任务,如订单处理、库存更新、发送确认邮件等。
使用Redis List类型存储和管理任务消息队列。
// 将新订单添加到订单处理队列
func addOrderToQueue(order Order) {
redisClient.LPUSH(ctx, "order_queue", order.ToString())
}
// 从订单处理队列中获取待处理的订单
func getNextOrder() (Order, error) {
orderJSON, err := redisClient.RPOP(ctx, "order_queue").Result()
if err != nil {
return Order{}, err
}
var order Order
json.Unmarshal([]byte(orderJSON), &order)
return order, nil
}
// 订单处理完成后,执行后续任务
func processOrder(order Order) {
// 处理订单逻辑
// ...
// 发送确认邮件
// ...
// 更新库存
// ...
}
排行榜:使用List类型,可以存储和管理如游戏得分、文章点赞数等排行榜数据。
在一个社交平台中,用户发表的文章根据点赞数进行排名,需要实时更新和展示排行榜。
LRANGE命令,可以方便地获取指定范围内的排行榜数据。使用Redis List类型存储用户的得分或点赞数,并根据需要对List进行排序。
// 为文章点赞,更新排行榜
func likeArticle(articleID string) {
// 假设每个文章都有一个对应的得分List
redisClient.INCR(ctx, "article:"+articleID+":score")
// 可以进一步使用Sorted Set来维护更复杂的排行榜
}
// 获取文章排行榜
func getArticleRankings() []Article {
articles := []Article{}
// 遍历所有文章的得分List
// 根据得分进行排序
// ...
return articles
}
BRPOP或BLPOP命令在多个列表上进行阻塞式读取,适用于多消费者场景。Redis的Set数据结构是一个无序且元素唯一的集合,它支持集合运算,如添加、删除、取交集、并集、差集等。这使得Set类型非常适合用于实现一些需要进行成员关系测试或集合操作的场景。
SADD key member - 向指定的集合添加元素。
SADD mySet "item1"
SREM key member - 从集合中删除元素。
SREM mySet "item1"
SISMEMBER key member - 检查元素是否是集合的成员。
SISMEMBER mySet "item1"
SINTER key [key ...] - 取一个或多个集合的交集。
SINTER mySet myOtherSet
SUNION key [key ...] - 取一个或多个集合的并集。
SUNION mySet myOtherSet
SDIFF key [key ...] - 取一个集合与另一个集合的差集。
SDIFF mySet myOtherSet
标签系统:Set类型可用于存储和处理具有标签特性的数据,如商品标签、文章分类标签等。
在一个内容平台上,用户可以给文章打上不同的标签,系统需要根据标签过滤和推荐文章。
使用Redis Set类型存储文章的标签集合,实现基于标签的推荐和搜索。
// 给文章添加标签
func addTagToArticle(articleID string, tag string) {
redisClient.SADD(ctx, "article:"+articleID+":tags", tag)
}
// 根据标签获取文章列表
func getArticlesByTag(tag string) []string {
return redisClient.SMEMBERS(ctx, "global:tags:"+tag).Val()
}
// 获取文章的所有标签
func getTagsOfArticle(articleID string) []string {
return redisClient.SMEMBERS(ctx, "article:"+articleID+":tags").Val()
}
社交网络好友关系:Set类型可以表示用户的好友列表,支持快速好友关系测试和好友推荐。
在一个社交网络应用中,用户可以添加和删除好友,系统需要管理用户的好友关系。
使用Redis Set类型存储用户的好友集合,实现好友关系的管理。
// 添加好友
func addFriend(userOneID string, userTwoID string) {
redisClient.SADD(ctx, "user:"+userOneID+":friends", userTwoID)
redisClient.SADD(ctx, "user:"+userTwoID+":friends", userOneID)
}
// 判断是否是好友
func isFriend(userOneID string, userTwoID string) bool {
return redisClient.SISMEMBER(ctx, "user:"+userOneID+":friends", userTwoID).Val() == 1
}
// 获取用户的好友列表
func getFriendsOfUser(userID string) []string {
return redisClient.SMEMBERS(ctx, "user:"+userID+":friends").Val()
}
Redis的Sorted Set数据结构是Set的一个扩展,它不仅能够存储唯一的元素,还能为每个元素关联一个分数(score),并根据这个分数对元素进行排序。
ZADD key score member - 向key对应的Sorted Set中添加元素member,元素的分数为score。如果member已存在,则会更新其分数。
ZADD mySortedSet 5.0 element1
ZRANGE key start stop [WITHSCORES] - 获取key对应的Sorted Set中指定分数范围内的元素,可选地使用WITHSCORES获取分数。
ZRANGE mySortedSet 0 -1 WITHSCORES
ZREM key member - 从key对应的Sorted Set中删除元素member。
ZREM mySortedSet element1
ZINCRBY key increment member - 为key中的member元素的分数增加increment的值。
ZINCRBY mySortedSet 2.5 element1
ZCARD key - 获取key对应的Sorted Set中元素的数量。
ZCARD mySortedSet
排行榜系统:Sorted Set类型非常适合实现排行榜系统,如游戏得分排行榜、文章热度排行榜等。
在一个在线游戏中,玩家的得分需要实时更新并显示在排行榜上。使用Sorted Set可以方便地根据得分高低进行排序。
使用Redis Sorted Set来存储和管理游戏玩家的得分排行榜。
// 更新玩家得分
func updatePlayerScore(playerID string, score float64) {
sortedSetKey := "playerScores"
// 添加或更新玩家得分
rdb.ZAdd(ctx, sortedSetKey, &redis.Z{Score: score, Member: playerID})
}
// 获取排行榜
func getLeaderboard(start int, stop int) []string {
sortedSetKey := "playerScores"
// 获取排行榜数据
leaderboard, _ := rdb.ZRangeWithScores(ctx, sortedSetKey, start, stop).Result()
var result []string
for _, entry := range leaderboard {
result = append(result, fmt.Sprintf("%s: %.2f", entry.Member.(string), entry.Score))
}
return result
}
实时数据统计:Sorted Set可以用于实时数据统计,如网站的访问量统计、商品的销量统计等。
在一个电商平台中,需要统计商品的销量,并根据销量对商品进行排序展示。
使用Redis Sorted Set来实现商品的销量统计和排序。
// 更新商品销量
func updateProductSales(productID string, sales int64) {
sortedSetKey := "productSales"
// 增加商品销量
rdb.ZIncrBy(ctx, sortedSetKey, float64(sales), productID)
}
// 获取商品销量排行
func getProductSalesRanking() []string {
sortedSetKey := "productSales"
// 获取销量排行数据
ranking, _ := rdb.ZRangeWithScores(ctx, sortedSetKey, 0, -1).Result()
var result []string
for _, entry := range ranking {
result = append(result, fmt.Sprintf("%s: %d", entry.Member.(string), int(entry.Score)))
}
return result
}
Redis的Hash数据结构是一种键值对集合,其中每个键(field)对应一个值(value),整个集合与一个主键(key)关联。这种结构非常适合存储对象或键值对集合。
HSET key field value - 为指定的key设置field的值。如果key不存在,会创建一个新的Hash。如果field已经存在,则会更新它的值。
HSET myHash name "John Doe"
HGET key field - 获取与key关联的field的值。
HGET myHash name
HDEL key field - 删除key中的field。
HDEL myHash name
HINCRBY key field increment - 将key中的field的整数值增加increment。
HINCRBY myHash age 1
HGETALL key - 获取key中的所有字段和值。
HGETALL myHash
用户信息存储:Hash类型常用于存储和管理用户信息,如用户ID、姓名、年龄、邮箱等。
在社交网络应用中,每个用户都有一系列属性,如用户名、年龄、兴趣爱好等。使用Hash类型可以方便地存储和查询单个用户的详细信息。
使用Redis Hash类型来存储和管理用户信息。当用户信息更新时,只更新Hash中的对应字段。
// 存储用户信息到Redis Hash
func storeUserInfo(userID string, userInfo map[string]interface{}) {
hashKey := "user:" + userID
// 将用户信息存储到Redis的Hash中
for field, value := range userInfo {
rdb.HSet(ctx, hashKey, field, value)
}
}
// 从Redis Hash获取用户信息
func getUserInfo(userID string) map[string]string {
hashKey := "user:" + userID
// 从Redis获取用户信息
fields, err := rdb.HGetAll(ctx, hashKey).Result()
if err != nil {
// 处理错误
return nil
}
// 将字段转换为字符串映射
var userInfo = make(map[string]string)
for k, v := range fields {
userInfo[k] = v
}
return userInfo
}
购物车管理:Hash类型可以用于实现购物车功能,其中每个用户的购物车是一个Hash,商品ID作为字段,数量作为值。
在电商平台中,用户的购物车需要记录用户选择的商品及其数量。使用Hash类型可以有效地管理每个用户的购物车。
使用Redis Hash类型来实现购物车功能,每个用户的购物车作为一个独立的Hash存储。
// 添加商品到购物车
func addToCart(cartID string, productID string, quantity int) {
cartKey := "cart:" + cartID
// 使用HINCRBY命令增加商品数量
rdb.HIncrBy(ctx, cartKey, productID, int64(quantity))
}
// 获取购物车中的商品和数量
func getCart(cartID string) map[string]int {
cartKey := "cart:" + cartID
// 从Redis获取购物车内容
items, err := rdb.HGetAll(ctx, cartKey).Result()
if err != nil {
// 处理错误
return nil
}
// 将商品ID和数量转换为映射
var cart map[string]int
for productID, quantity := range items {
cart[productID], _ = strconv.Atoi(quantity)
}
return cart
}
Redis的Bitmap是一种基于String类型的特殊数据结构,它使用位(bit)来表示信息,每个位可以是0或1。Bitmap非常适合用于需要快速操作大量独立开关状态的场景,如状态监控、计数器等。
SETBIT key offset value - 对key指定的offset位置设置位值。value可以是0或1。
SETBIT myBitmap 100 1
GETBIT key offset - 获取key在指定offset位置的位值。
GETBIT myBitmap 100
BITCOUNT key [start end] - 计算key中位值为1的数量。可选地,可以指定一个范围[start end]来计算该范围内的位值。
BITCOUNT myBitmap
BITOP operation destkey key [key ...] - 对一个或多个键进行位操作(AND, OR, XOR, NOT)并将结果存储在destkey中。
BITOP AND resultBitmap key1 key2
状态监控:Bitmap类型可以用于监控大量状态,例如用户在线状态、设备状态等。
在一个大型在线游戏平台中,需要实时监控成千上万的玩家是否在线。使用Bitmap可以高效地记录每个玩家的在线状态。
使用Redis Bitmap来存储和查询玩家的在线状态。
// 更新玩家在线状态
func updatePlayerStatus(playerID int, isOnline bool) {
bitmapKey := "playerStatus"
offset := playerID // 假设playerID可以直接用作offset
if isOnline {
rdb.SetBit(ctx, bitmapKey, int64(offset), 1)
} else {
rdb.SetBit(ctx, bitmapKey, int64(offset), 0)
}
}
// 查询玩家在线状态
func checkPlayerStatus(playerID int) bool {
bitmapKey := "playerStatus"
offset := playerID // 假设playerID可以直接用作offset
bitValue, err := rdb.GetBit(ctx, bitmapKey, int64(offset)).Result()
if err != nil {
// 处理错误
return false
}
return bitValue == 1
}
功能开关:Bitmap类型可以用于控制功能开关,例如A/B测试、特性发布等。
在一个SaaS产品中,需要对新功能进行A/B测试,只对部分用户开放。使用Bitmap可以快速地控制哪些用户可以访问新功能。
使用Redis Bitmap来作为功能开关,控制用户对新功能的访问。
// 为用户设置功能访问权限
func setFeatureAccess(userID int, hasAccess bool) {
featureKey := "featureAccess"
offset := userID // 假设userID可以直接用作offset
if hasAccess {
rdb.SetBit(ctx, featureKey, int64(offset), 1)
} else {
rdb.SetBit(ctx, featureKey, int64(offset), 0)
}
}
// 检查用户是否有新功能访问权限
func checkFeatureAccess(userID int) bool {
featureKey := "featureAccess"
offset := userID // 假设userID可以直接用作offset
bitValue, err := rdb.GetBit(ctx, featureKey, int64(offset)).Result()
if err != nil {
// 处理错误
return false
}
return bitValue == 1
}
Redis的HyperLogLog数据结构是一种概率数据结构,用于统计集合中唯一元素的数量,其特点是使用固定量的空间(通常为2KB),并且可以提供非常接近准确值的基数估计。
PFADD key element [element ...] - 向key对应的HyperLogLog中添加元素。如果key不存在,会创建一个新的HyperLogLog。
PFADD myUniqueSet element1 element2
PFCOUNT key - 获取key对应的HyperLogLog中的基数,即唯一元素的数量。
PFCOUNT myUniqueSet
PFMERGE destkey sourcekey [sourcekey ...] - 将多个HyperLogLog集合合并到一个destkey中。
PFMERGE mergedSet myUniqueSet1 myUniqueSet2
唯一用户访问统计:HyperLogLog类型非常适合用来统计一段时间内访问网站或应用的唯一用户数量。
在一个新闻门户网站,需要统计每天访问的唯一用户数量,以评估用户基础和内容的吸引力。
使用Redis HyperLogLog来统计每天访问的唯一用户数量。
// 记录用户访问
func recordUserVisit(userID string) {
uniqueSetKey := "uniqueVisitors"
// 向HyperLogLog中添加用户ID
rdb.PFAdd(ctx, uniqueSetKey, userID)
}
// 获取唯一用户访问量
func getUniqueVisitorCount() int64 {
uniqueSetKey := "uniqueVisitors"
// 获取HyperLogLog中的基数
count, _ := rdb.PFCount(ctx, uniqueSetKey).Result()
return count
}
事件独立性分析:HyperLogLog可以用于分析不同事件的独立性,例如,不同来源的点击事件是否来自相同的用户群体。
在一个广告平台,需要分析不同广告来源的点击事件是否由相同的用户群体产生。
使用Redis HyperLogLog来独立统计不同广告来源的点击事件,并合并集合以分析用户群体的独立性。
// 记录点击事件
func recordClickEvent(clickID string, sourceID string) {
sourceSetKey := "clicks:" + sourceID
// 向对应来源的HyperLogLog中添加点击ID
rdb.PFAdd(ctx, sourceSetKey, clickID)
}
// 合并点击事件集合并分析用户群体独立性
func analyzeAudienceIndependence(sourceIDs []string) int64 {
destKey := "mergedClicks"
// 合并所有来源的HyperLogLog集合
rdb.PFMerge(ctx, destKey, sourceIDs)
// 计算合并后的基数
count, _ := rdb.PFCount(ctx, destKey).Result()
return count
}
Redis的GEO数据结构用于存储地理位置信息,它允许用户进行各种基于地理位置的操作,如查询附近的位置、计算两个地点之间的距离等。
GEOADD key longitude latitude member - 向key对应的GEO集合中添加带有经纬度的成员member。
GEOADD myGeoSet 116.407526 39.904030 "Beijing"
GEOPOS key member [member ...] - 返回一个或多个成员的地理坐标。
GEOPOS myGeoSet "Beijing"
GEODIST key member1 member2 [unit] - 计算两个成员之间的距离。
GEODIST myGeoSet "Beijing" "Shanghai"
GEOHASH key member [member ...] - 返回一个或多个成员的Geohash表示。
GEOHASH myGeoSet "Beijing"
GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] - 查询给定位置周围指定半径内的所有成员。
GEORADIUS myGeoSet 116.407526 39.904030 500 km WITHCOORD WITHDIST WITHHASH
附近地点搜索:GEO类型可以用于实现基于地理位置的搜索功能,如查找附近的餐馆、影院等。
在一个旅游应用中,用户需要查找当前位置附近的旅游景点。
使用Redis GEO类型来实现基于用户当前位置的附近地点搜索。
// 搜索附近地点
func searchNearbyPlaces(longitude float64, latitude float64, radius float64) []string {
geoSetKey := "touristSpots"
// 执行GEORADIUS命令搜索附近地点
places, _ := rdb.GeoRadius(
ctx, geoSetKey, longitude, latitude, radius, "km",
&redis.GeoRadiusQuery{WithCoord: true, WithDist: true, WithHash: true}).Result()
var nearbyPlaces []string
for _, place := range places {
nearbyPlaces = append(nearbyPlaces, fmt.Sprintf("Name: %s, Distance: %.2f km", place.Member.(string), place.Dist))
}
return nearbyPlaces
}
用户定位与导航:GEO类型可以用于记录用户的地理位置,并提供导航服务。
在一个打车应用中,需要根据司机和乘客的位置进行匹配,并提供导航服务。
使用Redis GEO类型来记录司机和乘客的位置,并计算他们之间的距离。
// 记录司机位置
func recordDriverPosition(driverID string, longitude float64, latitude float64) {
geoSetKey := "drivers"
rdb.GeoAdd(ctx, geoSetKey, &redis.GeoLocation{Longitude: longitude, Latitude: latitude, Member: driverID})
}
// 计算司机与乘客之间的距离并匹配
func matchDriverToPassenger(passengerLongitude float64, passengerLatitude float64) (string, float64) {
driversGeoSetKey := "drivers"
passengerGeoSetKey := "passengers"
// 记录乘客位置
rdb.GeoAdd(ctx, passengerGeoSetKey, &redis.GeoLocation{Longitude: passengerLongitude, Latitude: latitude, Member: "PassengerID"})
// 搜索最近的司机
nearestDriver, _ := rdb.GeoRadius(
ctx, driversGeoSetKey, passengerLongitude, passengerLatitude, 5, "km",
&redis.GeoRadiusQuery{Count: 1, Any: true}).Result()
if len(nearestDriver) > 0 {
// 计算距离
distance := nearestDriver[0].Dist
return nearestDriver[0].Member.(string), distance
}
return "", 0
}
微信扫一扫
关注该公众号