redis的作用和应用实践|青训营

125 阅读4分钟

为什么需要 Redis?

Redis(Remote Dictionary Server)是一种开源的内存数据存储系统,它提供了高效的键值对存储和数据结构操作。Redis 的设计目标是提供快速、可扩展、稳定的数据存储和访问,适合用于缓存、计数器、会话管理、队列等应用场景。

以下是一些使用 Redis 的主要原因:

  1. 高性能:Redis 将数据存储在内存中,并使用异步写入磁盘的方式来保证持久化。这样可以获得快速的读写性能,并且适合处理高并发的请求。
  2. 丰富的数据结构:除了简单的键值对存储外,Redis 还支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。这些数据结构的操作都是原子性的,可以进行高效的数据处理和计算。
  3. 持久化支持:Redis 提供了两种持久化方式,分别是快照(snapshotting)和追加日志(append-only file,AOF)。通过持久化,可以保证数据在服务器重启后的恢复,并且可以灵活地选择持久化的级别和方式。
  4. 分布式支持:Redis 提供了集群和主从复制功能,可以水平扩展存储容量和提高系统的可用性。通过数据分片和数据复制,可以实现负载均衡和故障恢复。

Redis 的基本工作原理

Redis 的基本工作原理如下:

  1. 内存存储:Redis 将数据存储在内存中,这使得它能够提供极快的读写性能。内存存储也是 Redis 的主要特点之一,但它也限制了可以存储的数据量,因此需要根据内存容量进行适当的规划和管理。
  2. 单线程模型:Redis 使用单线程模型来处理客户端的请求。这是因为在绝大多数情况下,Redis 的性能瓶颈主要是在 CPU 密集型操作上,而不是在多线程并发上。使用单线程可以简化内部的数据结构和操作,并减少线程切换的开销。
  3. 事件驱动模型:Redis 使用事件驱动模型,通过事件循环和非阻塞 I/O 进行异步处理。当有客户端请求到达或数据可用时,Redis 会触发相应的事件,并在事件循环中进行处理。这样可以充分利用系统资源,并支持高并发的请求。
  4. 持久化机制:Redis 提供了持久化机制来保证数据在服务器重启后的恢复。通过快照和追加日志两种方式,可以将内存中的数据存储到磁盘中,并在重启时加载恢复。持久化可以根据需求进行配置,以便在性能和数据安全性之间做出权衡。

Redis 应用案例

这次的青训营项目要求我们实现极简版抖音的几个功能接口,其实在测试过程中就能发现,从前端发送请求到后端调用数据库进行状态更新然后相应,其实是个很慢的过程。比如以前我们小组写的dao层中的状态更新方法:

// FavoriteVideo 给视频点赞
func (u *UserInfo) ToFavoriteVideo(video *Video) error {

    tx := DB.Begin()

    if err := tx.Model(video).UpdateColumn("favorite_count", gorm.Expr("favorite_count + 1")).Error; err != nil {
       tx.Rollback()
       return err
    }

    if err := tx.Model(u).Association("FavorVideos").Append(video); err != nil {
       tx.Rollback()
       return err
    }
    return tx.Commit().Error
}

在用户点赞后进行的一系列操作传输到数据库更新是有延迟的,此时,如果我点完赞马上退出登录,再重新登录,因为延迟原因,各种状态初始化时还没有同步到更新以后的数据,此时点赞又会是没有点赞的状态,然后就会出现脏库的情况。因此,我们使用了redis来实现持久化功能:

// FavoriteVideo 给视频点赞
func (u *UserInfo) ToFavoriteVideo(video *Video) error {

    tx := DB.Begin()

    if err := tx.Model(video).UpdateColumn("favorite_count", gorm.Expr("favorite_count + 1")).Error; err != nil {
       tx.Rollback()
       return err
    }

    if err := tx.Model(u).Association("FavorVideos").Append(video); err != nil {
       tx.Rollback()
       return err
    }
    if redis.IsInit {
       redis.New().UpdateFavoriteState(u.ID, video.ID, true)
       redis.New().UpdateUserReceivedLikeCount(video.UserInfoID, true)
    }
    return tx.Commit().Error
}


func (r *Redis) UpdateFavoriteState(uid, vid int64, state bool) {
    key := fmt.Sprintf("%s:%d", "favorite", uid)
    if state {
       rdb.SAdd(ctx, key, vid)
       return
    }
    rdb.SRem(ctx, key, vid)
}
func (r *Redis) UpdateUserReceivedLikeCount(uid int64, state bool) {
    key := fmt.Sprintf("liked:%d", uid)
    if state {
       rdb.IncrBy(ctx, key, 1)
    } else {
       rdb.DecrBy(ctx, key, 1)
    }
}

这样用户在进行操作时,更新数据就会存获到内存快照中,此时再退出重新登录就会把之前的状态重新恢复而不是重新初始化。