超全,超详细的Redis基础,你掌握了多少?

Redis 入门

Redis 诞生历程

从一个故事开始

08年的时候有一个意大利西西里岛的小伙子,笔名antirez(http://invece.org/),创建
了一个访客信息网站LLOOGG.COM。有的时候我们需要知道网站的访问情况,比如访
客的IP、操作系统、浏览器、使用的搜索关键词、所在地区、访问的网页地址等等。在
国内,有很多网站提供了这个功能,比如 CNZZ,百度统计,国外也有谷歌的 Google
Analytics。我们不用自己写代码去实现这个功能,只需要在全局的footer里面嵌入一段
JS 代码就行了,当页面被访问的时候,就会自动把访客的信息发送到这些网站统计的服
务器,然后我们登录后台就可以查看数据了。

LLOOGG.COM提供的就是这种功能,它可以查看最多10000条的最新浏览记录。这样的话,它需要为每一个网站创建一个列表(List),不同网站的访问记录进入到不同的列表。如果列表的长度超过了用户指定的长度,它需要把最早的记录删除(先进先出) 。
1
当LLOOGG.COM的用户越来越多的时候,它需要维护的列表数量也越来越多,这
种记录最新的请求和删除最早的请求的操作也越来越多。LLOOGG.COM最初使用的数
据库是MySQL,可想而知,因为每一次记录和删除都要读写磁盘,因为数据量和并发量
太大,在这种情况下无论怎么去优化数据库都不管用了。
考虑到最终限制数据库性能的瓶颈在于磁盘,所以antirez打算放弃磁盘,自己去实
现一个具有列表结构的数据库的原型,把数据放在内存而不是磁盘,这样可以大大地提
升列表的push和pop的效率。antirez发现这种思路确实能解决这个问题,所以用C语
言重写了这个内存数据库,并且加上了持久化的功能,09年,Redis横空出世了。从最
开始只支持列表的数据库,到现在支持多种数据类型,并且提供了一系列的高级特性,
Redis已经成为一个在全世界被广泛使用的开源项目。

为什么叫REDIS 呢?它的全称是REmote DIctionary Service,直接翻译过来是远
程字典服务。

从Redis的诞生历史我们看到了,在某些场景中,关系型数据库并不适合用来存储
我们的Web应用的数据。 那么,关系型数据库和非关系型数据库, 或者说SQL和NoSQL,
到底有什么不一样呢?

Redis 定位与特性

SQL 与 NoSQL

在绝大部分时候,我们都会首先考虑用关系型数据库来存储我们的数据,比如
SQLServer,Oracle,MySQL等等。

关系型数据库的特点:

1、它以表格的形式,基于行存储数据,是一个二维的模式。
2、它存储的是结构化的数据,数据存储有固定的模式(schema),数据需要适应
表结构。
3、表与表之间存在关联(Relationship)。
4、大部分关系型数据库都支持SQL(结构化查询语言)的操作,支持复杂的关联查
询。
5、通过支持事务(ACID酸)来提供严格或者实时的数据一致性。

但是使用关系型数据库也存在一些限制,比如:

1、要实现扩容的话,只能向上(垂直)扩展,比如磁盘限制了数据的存储,就要扩
大磁盘容量,通过堆硬件的方式,不支持动态的扩缩容。水平扩容需要复杂的技术来实
现,比如分库分表。
2、表结构修改困难,因此存储的数据格式也受到限制。
3、在高并发和高数据量的情况下,我们的关系型数据库通常会把数据持久化到磁盘,
基于磁盘的读写压力比较大。

为了规避关系型数据库的一系列问题,我们就有了非关系型的数据库,我们一般把
它叫做“non-relational”或者“Not Only SQL”。NoSQL最开始是不提供 SQL的数
据库的意思,但是后来意思慢慢地发生了变化。

非关系型数据库的特点:

1、存储非结构化的数据,比如文本、图片、音频、视频。
2、表与表之间没有关联,可扩展性强。
3、保证数据的最终一致性。遵循BASE(碱)理论。 BasicallyAvailable(基本
可用); Soft-state(软状态); Eventually Consistent(最终一致性)。
4、支持海量数据的存储和高并发的高效读写。
5、支持分布式,能够对数据进行分片存储,扩缩容简单。

对于不同的存储类型,我们又有各种各样的非关系型数据库,比如有几种常见的类
型:
1、KV 存储,用 Key Value 的形式来存储数据。比较常见的有 Redis 和
MemcacheDB。
2、文档存储,MongoDB。
3、列存储,HBase。
4、图存储,这个图(Graph)是数据结构,不是文件格式。Neo4j。
5、对象存储。
6、XML存储等等等等。

这个网页列举了各种各样的NoSQL数据库 http://nosql-database.org/

Redis 特性

官网介绍:https://redis.io/topics/introduction
中文网站:http://www.redis.cn

硬件层面有CPU的缓存;浏览器也有缓存;手机的应用也有缓存。我们把数据缓存
起来的原因就是从原始位置取数据的代价太大了,放在一个临时位置存储起来,取回就
可以快一些。

Redis的特性:
1)更丰富的数据类型
2)进程内与跨进程;单机与分布式
3)功能丰富:持久化机制、过期策略
4)支持多种编程语言
5)高可用,集群

Redis 安装启动

Linux安装

参考:
CentOS7 安装 Redis 单实例 https://gper.club/articles/7e7e7f7ff7g5egc4g6b
Docker 安装 RabbitMQ 集群 https://gper.club/articles/7e7e7f7ff7g5egc5g6c
主要是注意配置文件几处关键内容(后台启动、绑定 IP、密码)的修改,配置别名

Windows服务端安装

微软自行编写了一个Redis服务端,可用于基本的测试和学习。
https://github.com/MicrosoftArchive/redis/tags

服务启动

src目录下,直接启动

./redis-server

后台启动(指定配置文件)

  1. redis.conf修改两行配置
daemonize yes 这里改成yes,后台启动
bind 0.0.0.0 改成本机的ip ,127.0.0.1
  1. redis.conf修改两行配置
redis-server/usr/local/soft/redis-5.0.5/redis.conf

总结:redis的参数可以通过三种方式配置,一种是redis.conf,一种是启动时–携
带的参数,一种是config set。

基本操作

默认有16个库(0-15),可以在配置文件中修改,默认使用第一个db0。

databases 16

因为没有完全隔离,不像数据库的database,不适合把不同的库分配给不同的业务使用。

切换数据库

select 0

清空当前数据库

flushdb

清空所有数据库

flushall

Redis是字典结构的存储方式,采用key-value存储。key和value的最大长度限制
是512M(来自官网 https://redis.io/topics/data-types-intro/)。

键的基本操作。

命令参考:http://redisdoc.com/index.html

存值

set xianyi 2673

取值

get xianyi 

查看所有键

keys *

获取键总数

dbsize

查看键是否存在

exists xianyi 

删除键

del xianyi 

重命名键

rename xianyi xianyi2 

查看类型

type xianyi

Redis一共有几种数据类型?(注意是数据类型不是数据结构)

官网:https://redis.io/topics/data-types-intro
String、Hash、Set、List、Zset、Hyperloglog、Geo、Streams

Redis 基本数据类型

String 字符串

存储类型

可以用来存储字符串、整数、浮点数。

操作命令

设置多个值(批量操作,原子性)

mset xianyi1 123 xianyi2 456

设置值,如果 key 存在,则不成功

setnx  xianyi

基于此可实现分布式锁。用 delkey 释放锁。 但如果释放锁的操作失败了,导致其他节点永远获取不到锁,怎么办? 加过期时间。单独用 expire 加过期,也失败了,无法保证原子性,怎么办?多参数

set key value [expiration EX seconds|PX milliseconds][NX|XX]

使用参数的方式

set lock 1 EX 10 NX

(整数)值递增

incr xianyi 
incrby xianyi 100

(整数)值递减

decr xianyi 
decrby xianyi 100

浮点数增量

set f 2.6 
incrbyfloat f 7.3

获取多个值

mget jack1 jack2

获取值长度

strlen jack

字符串追加内容

append jack good

获取指定范围的字符

getrange redisofxianyi 0 8
扩展:

其实我们上面说到分布式锁使用del释放锁,这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,即使这把锁不是它的。
所以,通常正确的加锁姿势,我们会将value值存入一个requestid,来标识锁的拥有者,可以使用UUID。在解锁时,传入requestid,判断如果get(key)==requestid,才进行del(key)释放锁。否则解锁失败。而且这两个操作要保证原子性,那么我们可以使用eval命令执行Lua脚本来实现,

Redis分布式锁的正确姿势(Java版)

加锁:

public class RedisTool {
    
    
 
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    
    
 
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
    
    
            return true;
        }
        return false;
 
    }
 
}

可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:

  1. 第一个为key,我们使用key来当锁,因为key是唯一的。
  2. 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
  3. 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
  4. 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
  5. 第五个为time,与第四个参数相呼应,代表key的过期时间。

总的来说,执行上面的set()方法就只会导致两种结果:

  1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。
  2. 已有锁存在,不做任何操作。

加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。

解锁:

public class RedisTool {
    
    
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
    
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
        if (RELEASE_SUCCESS.equals(result)) {
    
    
            return true;
        }
        return false;
    }

eval()方法是将Lua代码交给Redis服务端执行

那么这段Lua代码的功能是什么呢?其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述操作是原子性的。那么为什么执行eval()方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:

简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。

存储(实现)原理

数据模型

sethelloword为例,因为Redis是KV的数据库,它是通过hashtable实现的(我们把这个叫做外层的哈希)。所以每个键值对都会有一个dictEntry(源码位置:dict.h),里面指向了key和value的指针。next指向下一个dictEntry。
redis dict
在这里插入图片描述
key是字符串,但是Redis没有直接使用C的字符数组,而是存储在自定义的SDS中。
value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在redisObject中。实际上五种常用的数据类型的任何一种,都是通过redisObject来存储的。
redisObject
redisObject定义在src/server.h文件中。

在这里插入图片描述
可以使用type命令来查看对外的类型。
127.0.0.1:6379> type qs
string

内部编码
在这里插入图片描述
127.0.0.1:6379>set number 1
OK
127.0.0.1:6379>set qs “isagoodteacheringupao,havecrossedmountainsandsea”
OK
127.0.0.1:6379>set jack bighead
OK
127.0.0.1:6379>object encoding number
“int”
127.0.0.1:6379>object encoding jack
“embstr”
127.0.0.1:6379>object encoding qs
“raw”
字符串类型的内部编码有三种:

1、int,存储8个字节的长整型(long,2^63-1)。
2、embstr, 代表embstr格式的SDS(SimpleDynamicString简单动态字符串),存储小于44个字节的字符串。
3、raw,存储大于44个字节的字符串(3.2版本之前是39字节)。为什么是39?

/*object.c*/ 
#defineOBJ_ENCODING_EMBSTR_SIZE_LIMIT44

问题1、什么是SDS?
Redis中字符串的实现。
在3.2以后的版本中,SDS又有多种结构(sds.h):
sdshdr5、 sdshdr8、 sdshdr16、sdshdr32、sdshdr64,用于存储不同的长度的字符串,分别代表
2^5=32byte,
2^8=256byte,
2^16=65536byte=64KB,
2^32byte=4GB。
在这里插入图片描述
问题2、为什么Redis要用SDS实现字符串?
我们知道,C语言本身没有字符串类型(只能用字符数组char[]实现)。
1、使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。
2、如果要获取字符长度,必须遍历字符数组,时间复杂度是O(n)。
3、C字符串长度的变更会对字符数组做内存重分配。
4、通过从字符串开始到结尾碰到的第一个’\0’来标记字符串的结束,因此不能保
存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。

SDS的特点:
1、不用担心内存溢出问题,如果需要会对SDS进行扩容。
2、获取字符串长度时间复杂度为O(1),因为定义了len属性。
3、通过“空间预分配”( sdsMakeRoomFor)和“惰性空间释放”,防止多
次重分配内存。
4、判断是否结束的标志是len属性(它同样以’\0’结尾是因为这样就可以使用C语言中函数库操作字符串的函数了),可以包含’\0’。
在这里插入图片描述
问题3、embstr和raw的区别?
embstr 的使用只分配一次内存空间(因为RedisObject 和SDS是连续的), 而 raw需要分配两次内存空间(分别为RedisObject和SDS分配空间)。

因此与 raw 相比,embstr 的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。

而 embstr 的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个RedisObject和SDS都需要重新分配空间,因此 Redis 中的 embstr 实现为只读。

问题4:int和embstr什么时候转化为raw?
当 int 数 据 不 再 是 整 数 , 或 大 小 超 过 了 long 的 范 围(2^63-1=9223372036854775807)时,自动转化为 embstr。

127.0.0.1:6379>set k1 1
OK
127.0.0.1:6379>append k1 a
(integer) 2
127.0.0.1:6379>object encoding k1
“raw”

问题5:明明没有超过阈值,为什么变成raw了?
127.0.0.1:6379>set k2 a
OK
127.0.0.1:6379>object encoding k2
“embstr”
127.0.0.1:6379>append k2 b
(integer) 2
127.0.0.1:6379>object encoding k2
“raw”
对于 embstr,由于其实现是只读的,因此在对 embstr 对象进行修改时,都会先
转化为 raw 再进行修改。
因此,只要是修改 embstr 对象,修改后的对象一定是 raw 的,无论是否达到了 44个字节。

问题6:当长度小于阈值时,会还原吗?
关于 Redis 内部编码的转换,都符合以下规律:编码转换在 Redis 写入数据时完
成,且转换过程不可逆,只能从小内存编码向大内存编码转换(但是不包括重新set)。

问题7:为什么要对底层的数据结构进行一层包装呢?
通过封装,可以根据对象的类型动态地选择存储结构和可以使用的命令,实现节省空间和优化查询速度。

应用场景

缓存
String类型
例如:热点数据缓存(例如报表,明星出轨),对象缓存,全页缓存。
可以提升热点数据的访问速度。
数据共享分布式
STRING类型,因为Redis是分布式的独立服务,可以在多个应用之间共享
例如:分布式Session

<dependency> 
	<groupId>org.springframework.session</groupId> 
	<artifactId>spring-session-data-redis</artifactId> 
</dependency>

分布式锁
STRING类型setnx方法,只有不存在时才能添加成功,返回true。 http://redisdoc.com/string/set.html 建议用参数的形式

全局ID
INT类型,INCRBY,利用原子性

incrby userid 1000

分库分表的场景,一次性拿一段)
计数器
INT类型,INCR方法
例如:文章的阅读量,微博点赞数,允许一定的延迟,先写入Redis再定时同步到
数据库。
限流
INT类型,INCR方法
以访问者的IP和其他信息作为key,访问一次增加一次计数,超过次数则返回false
位统计
String类型的BITCOUNT(1.6.6的bitmap数据结构介绍)。
字符是以8位二进制存储的
set k1 a
setbit k1 6 1
setbit k1 7 0
get k1
a对应的ASCII码是97,转换为二进制数据是01100001
b对应的ASCII码是98,转换为二进制数据是01100010
因为bit非常节省空间(1 MB=8388608 bit),可以用来做大数据量的统计。
例如:在线用户统计,留存用户统计
setbit onlineusers 0 1
setbit onlineusers 1 1
setbit onlineusers 2 0

支持按位与、按位或等等操作。
BITOP AND destkey key[key…] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey。
BITOP OR destkey key[key…] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOP XOR destkey key[key…] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey。
BITOP NOT destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey 。
#计算出 7 天都在线的用户

BITOP"AND"“7_days_both_online_users”“day_1_online_users”“day_2_online_users”…“day_7_online_users”

如果一个对象的value有多个值的时候,怎么存储?
例如用一个key存储一张表的数据。
在这里插入图片描述
序列化?例如 JSON/Protobuf/XML,会增加序列化和反序列化的开销,并且不能
单独获取、修改一个值。
可以通过key分层的方式来实现,例如:

mset student:1:sno GP16666 student:1:sname 一个人的精彩 student:1:company 腾讯

获取值的时候一次获取多个值:

mget student:1:sno student:1:sname student:1:company

缺点:key太长,占用的空间太多。有没有更好的方式?

Hash 哈希

在这里插入图片描述
存储类型
包含键值对的无序散列表。value只能是字符串,不能嵌套其他类型。

同样是存储字符串,Hash与String的主要区别?
1、把所有相关的值聚集到一个key中,节省内存空间
2、只使用一个key,减少key冲突
3、当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU的消耗

Hash不适合的场景:
1、Field不能单独设置过期时间
2、没有bit操作
3、需要考虑数据量分布的问题(value值非常大的时候,无法分布到多个节点)

操作命令

hset h1 f 6
hset h1 e 5 
hmset h1 a 1 b 2 c 3 d 4
hget h1 a 
hmget h1 a b c d 
hkeys h1
hvals h1 
hgetall h1

key操作

hget exists h1 
hdel h1
hlen h1

存储(实现)原理

Redis的Hash本身也是一个KV的结构,类似于Java中的HashMap。
外层的哈希(Redis KV 的实现)只用到了 hashtable。当存储hash 数据类型时,
我们把它叫做内层的哈希。内层的哈希底层可以使用两种数据结构实现:
ziplist:OBJ_ENCODING_ZIPLIST(压缩列表)
hashtable:OBJ_ENCODING_HT(哈希表)

在这里插入图片描述
ziplist压缩列表
ziplist压缩列表是什么?
ziplist是一个经过特殊编码的双向链表,它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度,通过牺牲部分读写性能,来换取高效的内存空间利用率,是一种时间换空间的思想。只用在字段个数少,字段值小的场景里面。
问题:什么时候使用ziplist存储?

当hash对象同时满足以下两个条件的时候,使用ziplist编码:
1)所有的键值对的健和值的字符串长度都小于等于64byte(一个英文字母
一个字节);
2)哈希对象保存的键值对数量小于512个。

应用场景
String
String可以做的事情,Hash都可以做。

存储对象类型的数据
比如对象或者一张表的数据,比String节省了更多key的空间,也更加便于集中管理。

List 列表

存储类型
在这里插入图片描述
操作命令

lpush queue a
lpush queue b c

rpush queue d e
lpop queue
rpop queue

取值

lindex queue 0
lrange queue 0 -1
在这里插入图片描述
存储(实现)原理

在早期的版本中,数据量较小时用ziplist存储,达到临界值时转换为linkedlist进行存储,分别对应OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_LINKEDLIST 。
3.2版本之后,统一用quicklist来存储。quicklist存储了一个双向链表,每个节点都是一个ziplist
应用场景
因为List是有序的,可以用来做用户时间线
消息队列
List提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。
BLPOP:BLPOPkey1timeout移出并获取列表的第一个元素, 如果列表没有元素
会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOP:BRPOPkey1timeout移出并获取列表的最后一个元素, 如果列表没有元
素会阻塞列表直到等待超时或发现可弹出元素为止。
队列:先进先出:rpush blpop,左头右尾,右边进入队列,左边出队列。
栈:先进后出:rpush brpop

Set 集合

存储类型

String类型的无序集合,最大存储数量2^32-1(40亿左右)。

在这里插入图片描述
操作命令

添加一个或者多个元素
sadd myset a b c d e f g

获取所有元素
smembers myset

统计元素个数
scard myset

随机获取一个元素
srandmember key

随机弹出一个元素
spop myset

移除一个或者多个元素
srem myset d e f

查看元素是否存在
sismember myset a
存储(实现)原理
Redis用intset或hashtable存储set。如果元素都是整数类型,就用inset存储。
如果不是整数类型,就用hashtable(数组+链表的存来储结构)。
问题:KV怎么存储set的元素?key就是元素的值,value为null。
如果元素个数超过512个,也会用hashtable存储。
应用场景
抽奖
随机获取元素
spop myset
点赞、签到、打卡
在这里插入图片描述
这条微博的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
比关系型数据库简单许多。

商品标签
用 tags:i5001来维护商品所有的标签。
在这里插入图片描述
sadd tags:i5001 画面清晰细腻
sadd tags:i5001 真彩清晰显示屏
sadd tags:i5001 流畅至极
商品筛选
获取差集
sdiff set1 set2
获取交集(intersection )
sinter set1 set2
获取并集
sunion set1 set2

iPhone11上市了。
sadd brand:apple iPhone11
sadd brand:ios iPhone11
sad screensize:6.0-6.24 iPhone11
sad screentype:lcd iPhone11
筛选商品,苹果的,iOS的,屏幕在6.0-6.24之间的,屏幕材质是LCD屏幕
sinter brand:apple brand:ios screensize:6.0-6.24 screentype:lcd
用户关注、推荐模型

ZSet 有序集合

存储类型
在这里插入图片描述
sorted set,有序的set,每个元素有个score。
score相同时,按照key的ASCII码排序。
数据结构对比:
在这里插入图片描述
操作命令
添加元素
zadd myzset 10 java 20 php 30 ruby 40 cpp 50 python
获取全部元素
zrange myzset 0 -1 withscores
zrevrange myzset 0 -1 withscores
根据分值区间获取元素
zrangebyscore myzset 2030
移除元素
也可以根据 scorerank 删除
zrem myzset php cpp
统计元素个数
zcard myzset
分值递增
zincrby myzset 5 python
根据分值统计个数
zcount myzset 20 60
获取元素 rank
zrank myzset java
获取元素 score
zsocre myzset java
也有倒序的 rev 操作(reverse)

存储(实现)原理

同时满足以下条件时使用ziplist编码:
 元素数量小于128个
 所有member的长度都小于64字节
在ziplist的内部,按照score排序递增来存储。插入的时候要移动之后的数据。
应用场景
排行榜
id为6001的新闻点击数加1:zincrby hotNews:20190926 1 n6001
获取今天点击最多的15条:zrevrange hotNews:20190926 0 15 withscores
在这里插入图片描述

总结

数据结构总结
在这里插入图片描述
编码转换总结
在这里插入图片描述
应用场景总结
缓存——提升热点数据的访问速度
共享数据——数据的存储和共享的问题
全局 ID —— 分布式全局 ID 的生成方案(分库分表)
分布式锁——进程间共享数据的原子操作保证
在线用户统计和计数
队列、栈——跨进程的队列/栈
消息队列——异步解耦的消息机制
服务注册与发现 —— RPC 通信机制的服务协调中心(Dubbo 支持 Redis)
购物车
新浪/Twitter 用户消息时间线
抽奖逻辑(礼物、转发)
点赞、签到、打卡
商品标签
用户(商品)关注(推荐)模型
电商产品筛选
排行榜

猜你喜欢

转载自blog.csdn.net/weixin_38087538/article/details/108249644