tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 各种MQ

  • Nginx

  • IdentityServer

  • Redis

    • 初始Redis与安装
    • Redis常用的一些场景
      • 数据类型
        • 基本数据-5种
        • 扩展
      • 应用场景
      • 应用场景-实现
        • 点赞实现
        • 排行榜
        • 分布式锁
        • 阅读量
        • 标签
      • Redis的局限
    • Redis中的缓存失效
    • 关于Redis的一些问题
    • Redis的持久化策略
    • Redis从单机到集群等模式
    • Redis和MySql实现数据一致性
    • 通过Redis实现分布式锁
    • Redis的客户端介绍
  • Linux

  • 中间件
  • Redis
EffectTang
2023-10-30
目录

Redis常用的一些场景

# Redis常用的一些场景

说某个东西擅长或者常用于某个领域,一定要从它自身的特性出发。因为它有某种特性,因此它才擅长做某件事。那要说Redis常用于哪些场景,那我们就要先了解下它的特性,或者说它支持的数据结构有哪些。

# 数据类型

# 基本数据-5种

Redis的基本数据类型有5种,

String(字符串):字符串是最基本的数据类型,可以存储简单的字符串或者是二进制数据。字符串类型的值可以是最大为 512MB 的二进制数据。

List(列表):列表是按照插入顺序排列的字符串列表,适合用来存储消息队列或者是用户的浏览历史等有序数据。列表可以方便地从头部(push)或尾部(pop)添加或移除元素。

# 从右边添加三个元素,并判断该键listkey的 底层数据结构
127.0.0.1:6379> rpush listkey e1 e2 e3
(integer) 3
127.0.0.1:6379> object encoding listkey
"ziplist”
1
2
3
4
5

Hash(哈希表):哈希表允许用户将一个键值对映射存储在一个哈希中,类似于 Python 中的字典。适合存储对象,因为可以将一个对象的不同属性映射到同一个键上。

基本语法:HSET key field value HDEL KEY_NAME FIELD1.. FIELDN

# 创建一个名为 user 的哈希表,并添加两个字段
HSET user name "zhangsan"
HSET user age 25
1
2
3

Set(集合):集合存储的是唯一的字符串元素。集合中的元素不会重复,适用于存储唯一元素的集合,比如用户好友列表或者是用户ID的集合。

Redis Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略,假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。当集合 key 不是集合类型时,返回一个错误。

基本语法如下:sadd key_name value1...valueN

redis 127.0.0.1:6379> SADD myset "hello'!(integer)1
redis 127.0.0.1:6379> SADD myset "foo"
(integer)1
redis 127.0.0.1:6379> SADD myset "hello"!
(integer)8
redis 127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) “foo”
1
2
3
4
5
6
7
8

针对于点赞,获取总点赞数对应的指令,可以使用它

scard key

获取集合的成员数。

Sorted Set(有序集合/zset):有序集合与集合相似,但是每个成员都关联了一个分数,这个分数决定了成员在集合中的顺序。有序集合可以按照分数来排序集合中的成员。

注意:有序集合中的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同。

以下是其中新增指令的基本语法:

redis 127.0.0.1:6379> ZADD w3ckey 1 redis(integer)1
redis 127.0.0.1:6379> ZADD w3ckey 2 mongodb
(integer)1
redis 127.0.0.1:6379> ZADD w3ckey 3 mysql
(integer)1
redis 127.0.0.1:6379> ZADD w3ckey 3 mysql
(integer)0
redis 127.0.0.1:6379> ZADD w3ckey 4 mysql
(integer)0
redis 127.0.0.1:6379> ZRANGE w3ckey 0 10 WITHSCORES
1)"redis"
2)“1”
3)"mongodb"
4)“2"
5)"mysql".
6)"4"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

它之所适合做排行榜是因为,它自身独特的数据结构,以及对应的指令,比如

ZINCRBY key increment member
1

有序集合中对指定成员的分数加上增量 increment

# 扩展

除了上述五种基本的数据结构之外,Redis 还提供了扩展的数据结构以支持更多的使用场景:

在字符串的基础之上演变出了位图(Bitmaps)和HyperLogLog(计数器)两种神奇的“数据结构”,

随着LBS(Location Based Service,基于位置服务)的不断发展,Redis3.2版本中加入有关Geospatial Indexes(地理空间索引)。

Bitmaps:Bitmaps本身不是一种数据结构,实际上它就是字符串),但是它可以对字符串的位进行操作。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。

因为 bitmaps 是基于字符串类型实现的,而 Redis 中的字符串(String)类型实际上可以存储的最大长度是 512 MB,所以bitmaps 的最大长度是 512 MB,即 232232 个比特位(bits)。

setbit key offset value
1

注意:虽然理论上 bitmaps 的长度可以达到 512 MB,但在实际应用中,你可能需要考虑服务器内存的限制以及单个键的大小限制。此外,对于非常大的 bitmaps,某些操作可能会变得较为耗时,因此在设计应用时应考虑到这一点。

HyperLogLog (HLL):它也不是新的数据类型,实际类型仍为字符串类型,但通过一种基数算法——能够以极小的空间消耗来近似计算一个集合中不重复元素的数量。用于估算大量数据中的不同元素的数量(即基数)。

HyperLogLog 算法是由 Philippe Flajolet 等人提出的一种概率算法,其特点是能够以极小的空间消耗来近似计算一个集合中不重复元素的数量。

特点

  • 内存高效:相对于精确计算基数所需的空间,HyperLogLog 只需极少的内存即可实现近似的计算。
  • 近似计算:HyperLogLog 提供的是一个估计值,而不是精确值。但是,随着数据量的增长,误差率会逐渐稳定在一个较低的水平。
  • 无状态:多个 HyperLogLog 结构可以合并成一个,这使得它可以很方便地用于分布式的环境。

# 应用场景

  • 缓存:在绝⼤多数的互联⽹项⽬中,为了提供数据的访问速度、降低数据库的访问压⼒,我们可以使⽤redis 作为缓存来实现,此外,Redis还提供了键值过期时间设置,提供了灵活控制最大内存和内存溢出后的淘汰策略。极大的提高了软件的稳定性。
  • 点赞、计数器等功能:对数据实时读写要求⽐较⾼(更新频繁),但是对数据库的⼀致性要求并不是太⾼的功能、场景 ,我们使用Redis提供的列表和有序集合数据结构,可以很方便地构建各种排行榜系统,或者通过计数器功能,实现点赞、播放量功能等,或者还可以通过redis的过期功能,进行限速功能:如一分钟内只允许发送一次短信等功能。
  • 历史记录、投票系统、评论系统:存储用户的操作历史或者其他需要保留的历史数据,可以将最新的记录推入列表的一端,当列表长度超过预设的最大长度时,可以移除多余的旧记录、投票系统:可以使用列表来记录用户的投票情况,每个用户投票后将其记录到列表中,可以使用列表的长度来统计投票总数。以上场景都是 list 数据类型非常适合的
  • 标签:set集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人。但实际上一个标签系统远比你想得要复杂得多,这里只是提供一个简要的思路
  • 排行榜:zset有序集合比较典型的使用场景就是排行榜系统
  • 分布式锁:基于redis的操作特性可以实现分布式锁功能
  • 分布式会话:在分布式系统中可以使⽤redis实现 session (共享缓存)
  • 消息队列系统:可以使⽤redis实现应⽤之间的通信。或者直接使用Redis提供的发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。

除了以上场景,Redis还能用在其他地方,这里就不一一列举了。但需要注意的是,因为Redis的特性,为了尽可能的保证一致性,对于使用的数据,要尽量选取不经常修改的数据。

# 应用场景-实现

# 点赞实现

以下是使用SpringBoot跟redis实现的一个简要版本,代码demo。

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Set;

@Service
public class LikeService {

    private final RedisTemplate<String, String> redisTemplate;

    public LikeService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 点赞
    public void like(String userId, String contentId) {
        String key = "like:content:" + contentId;
        redisTemplate.opsForSet().add(key, userId);
    }

    // 取消点赞
    public void unlike(String userId, String contentId) {
        String key = "like:content:" + contentId;
        redisTemplate.opsForSet().remove(key, userId);
    }

    // 获取点赞数
    public Long getLikeCount(String contentId) {
        String key = "like:content:" + contentId;
        return redisTemplate.opsForSet().size(key);
    }

    // 检查用户是否已点赞
    public Boolean isLiked(String userId, String contentId) {
        String key = "like:content:" + contentId;
        return redisTemplate.opsForSet().isMember(key, userId);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

以下是关于点赞表的设计参考,因为用户可能会出现重复点击的情况,因为需要实现接口的幂等。

create table likelog(
    like_id bigint auto_increment primary key comment '点赞表主键id',
    obj_id bigint not null comment '稿件或者 文章id',
    user_id bigint not null comment '点赞人id',
    like_action int not null default 0 comment '是否点赞 ,0 未点赞,1 点赞',
    create_time timestamp default current_timestamp comment '记录创建时间'
)engine = innodb default charset = utf8mb4 comment ='点赞流水表';

create table likelist(
    id bigint auto_increment primary key ,
    like_total bigint default 0 comment '点赞总数',
    obj_id bigint not null comment '稿件或者 文章id'
)engine = innodb default charset = utf8mb4 comment= '点赞总数表';
1
2
3
4
5
6
7
8
9
10
11
12
13

除了主键索引以外,没有建立其他索引。以上sql设计仅供参考。

# 排行榜

可以使用Redis中的zset数据结构。它在存储元素时,还可以为每个元素设置分数。

public void likeWithRanking(String userId, String contentId) {
    String key = "like:ranking";
    // 每次点赞增加分数
    redisTemplate.opsForZSet().incrementScore(key, contentId, 1);
}
1
2
3
4
5

方案优势

  1. 高性能:Redis 的 Set 结构天然支持去重和快速统计(SADD、SCARD)。
  2. 高并发:通过 Redis 的原子操作避免竞态条件。
  3. 灵活性:可扩展为点赞排行榜、用户点赞历史等功能。

# 分布式锁

# 阅读量

如果你仅需要记录文章的总阅读次数,并不需要知道具体的阅读者是谁,那么使用Redis的String类型是最简单直接的方式。

  • 操作方法:
    • 使用INCR命令增加阅读次数。
    • 可以结合GET获取当前的阅读次数。
  • 优点:
    • 实现简单,性能高。
    • 非常适合于只需统计总数的场景。
  • 缺点:无法提供关于谁进行了阅读的信息。

如果除了统计总的阅读次数外,你还想追踪哪些用户已经阅读了某篇文章(例如防止重复计数),可以使用Set来存储每个用户的唯一标识符(如用户ID)。

  • 操作方法:
    • 使用SADD添加用户ID到集合中。
    • 使用SCARD获取集合中的元素数量作为阅读人数。
  • 优点:
    • 自动去重,确保每个用户只被计数一次。
    • 可以方便地查询所有已阅读的用户。
  • 缺点:相较于String,增加了内存消耗和管理复杂度。

注意点:

但同一个用户,跟一段时间去阅读,还可以给文章增加一次浏览量,也就是说同一个用户,允许对同一篇文章多次增加阅读量。

# 标签

# Redis的局限

新的技术总是会解决一些以前无法解决的问题,或者提高以前解决某些问题的效率,但同样的,技术上没有唯一的银弹,新技术也会有一些局限。下面就说下Redis的局限,或者说不建议它使用的场景。

我们知道Redis的数据是存放在内存中的,如果海量的数据都存放在此,那我们需要多大的内存呢?对于技术实现是一个巨大的挑战,更不要说内存的价格比硬盘的价格要贵,过高的经济成本也会让我们无法负担。

因此,一些大规模的数据则不建议存放Redis。

同时,如果某些数据不频繁变动,那存Redis的意义也不大,因为频繁的读操作本身相对于写操作来说,通常对数据库的负担较小。因此,对于一些频繁变动的数据才建议放在Redis中,其他的则还是放在传统数据库,如MySql等中。

上次更新: 2025/04/23, 16:23:16
初始Redis与安装
Redis中的缓存失效

← 初始Redis与安装 Redis中的缓存失效→

最近更新
01
面向切面跟自定义注解的结合
05-22
02
时间跟其他数据的序列化
05-19
03
数据加密与安全
05-17
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式