Redis学习(一)之“基础知识”

redis 常用操作网址https://www.runoob.com/redis/redis-tutorial.html

1.Redis的诞生

2009年由一个意大利人antirez创建,因为他搭建了一个网站,使用的是mysql,他认为mysql太慢了。所以开发了一个基于内存的数据库

2.为什么叫Redis

它的全称是Remote Dictionary Service,直接翻译过来是远程字典服务

3.SQL和NOSQL的特点

我们知道数据库分为关系型数据库(sql)和非关系型数据库(nosql)
mysql、SQLServer、Oracle属于关系型数据库,Redis则属于非关系型数据库。让我们看看两者的区别

关系型数据库(SQL)

关系型数据库特点
1. 它以表格的形式,基于行存储数据,是一个二维的模式。
2. 它存储的是结构化的数据,数据需要适应表结构(schema)
3. 表与表之间存在关联(Relationship)
4. 大部分关系型数据库都支持SQL(结构化查询语言)的操作,支持复杂的关联查询。
5. 通过支持事务(ACID 酸,A:atomicity 原子性 C:consistency 一致性 I:isolation 隔离性 D:durability 持久性)来提供严格或者实时的数据一致性。

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

非关系型数据库(Not Only SQL)

非关系型数据库的特点

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

非关系型数据库的缺点

  1. 不支持sql标准查询,学习成本就比较高。
  2. nosql只能保证数据相对一致性,尤其是在数据同步的时候,主从服务器的状态是不一致的。

非关系型数据库的种类

  1. KV 存储, 用Key Value 的形式来存储数据。比较常见的有Redis 和 Memcache。

    redis 和 memcache的选择
    对于两者的选择还是要看具体的应用场景,如果需要缓存的数据只是key-value这样简单的结构时,我在项目里还是采用memcache,它也足够的稳定可靠。如果涉及到存储,排序等一系列复杂的操作时,毫无疑问选择redis。
    Redis和Memcache对比及选择

  2. 文档存储,MongoDB。

  3. 列存储,HBase。

  4. 图存储,这个图(Graph)是数据结构,不是文件格式。Neo4j

  5. 对象存储

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

NewSQL 结合了 SQL 和 NoSQL 的特性(例如 PingCAP 的 TiDB)。

2.Redis 特性

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

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

Redis 的特性:

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

3. Redis 的安装与启动

单独写一篇文章介绍

4. Redis的数据类型

4.1 string

string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

4.1.1 存储类型

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

  • string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
  • string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
4.1.2 操作命令
# set一个值
set xhc 你技术很牛

# get一个值
get xhc 

# 设置多个值(批量操作,原子性)
mset xhc 你很棒 xhr 666

# 获取多个值
mget xhc xhr

# 设置值,如果 key 存在,则不成功(not exist)
# 基于此可实现分布式锁。用 del key 释放锁。
setnx xhc

# 但如果释放锁的操作失败了,导致其他节点永远获取不到锁,怎么办?
# 加过期时间。单独用 expire 加过期,也失败了,无法保证原子性,怎么办?多参数
# set key value [expiration EX seconds|PX milliseconds][NX|XX]
# 使用参数的方式
set lock1 1 EX 10 NX


# (整数)值递增
set xhc 1
incr xhc

# Redis Incrby 命令将 key 中储存的数字加上指定的增量值。
# 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
incrby xhc 100

# (整数)值递减
decr xhc 
decrby xhc 100

# 浮点数增量
set f 2.6 
incrbyfloat f 7.3

# 获取值长度
strlen xhc

# 字符串追加内容
append xhc good

# 获取指定范围的字符
getrange xhc 0 2

4.1.3 存储(实现)原理

数据模型
Redis 是 KV 的数据库,它是通过 hashtable 实现的(我们把这个叫做外层的哈希)。所以每个键值对都会有一个 dictEntry(源码位置:dict.h), 里面指向了 key 和 value 的指针。next 指向下一个 dictEntry。

typedef struct dictEntry { 
	void *key; /* key 关键字定义 */ 
	union { 
		void *val; uint64_t u64; /* value 定义 */ 
		int64_t s64; double d; 
	} v; 
struct dictEntry *next; /* 指向下一个键值对节点 */ 
} dictEntry;

set xhc 你真棒为例
图示:
在这里插入图片描述
key 是字符串,但是 Redis 没有直接使用 C 的字符数组,而是存储在自定义的 SDS 中。

value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在 redisObject 中。实际上五种常用的数据类型的任何一种,都是通过 redisObject 来存储的。

redisObject
redisObject 定义在 src/server.h 文件中。

typedef struct redisObject {
	unsigned type:4; /* 对象的类型,包括:OBJ_STRING、OBJ_LIST、OBJ_HASH、OBJ_SET、OBJ_ZSET */
	unsigned encoding:4; /* 具体的数据结构 */
	unsigned lru:LRU_BITS; /* 24 位,对象最后一次被命令程序访问的时间,与内存回收有关 */
	int refcount; /* 引用计数。当 refcount 为 0 的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了 */
	void *ptr; /* 指向对象实际的数据结构 */
} robj;

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

type xhc 
string 

内部编码

字符串类型的内部编码有三种:

  1. int:存储 8 个字节的长整型(long,2^63-1)。
  2. embstr:代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串), 存储小于 44 个字节的字符串。
/* object.c */ 
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
  1. raw:存储大于 44 个字节的字符串(3.2 版本之前是 39 字节)。

示例:

127.0.0.1:6379> set number 1 
OK 
127.0.0.1:6379> set xhc "you very good,you very good ,you very good ,you very good ,you very good " 
OK 
127.0.0.1:6379> set jack sexboy 
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 xhc 
"raw"

常见面试题

  • 问题 1:什么是 SDS?
    Redis 中字符串的实现。
/* sds.h */
struct __attribute__ ((__packed__)) sdshdr8 {
	uint8_t len; /* 当前字符数组的长度 */
	uint8_t alloc; /*当前字符数组总共分配的内存大小 */
	unsigned char flags; /* 当前字符数组的属性、用来标识到底是 sdshdr8 还是 sdshdr16 等 */
	char buf[]; /* 字符串真正的值 */
};
  • 问题 2:为什么 Redis 要用 SDS 实现字符串?
    C 语言本身没有字符串类型(只能用字符数组 char[]实现)。

    • 使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。
    • 如果要获取字符长度,必须遍历字符数组,时间复杂度是 O(n)。
    • C 字符串长度的变更会对字符数组做内存重分配。
    • 通过从字符串开始到结尾碰到的第一个’\0’来标记字符串的结束,因此不能保存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。

    SDS 的特点:

    • 不用担心内存溢出问题,如果需要会对 SDS 进行扩容。
    • 获取字符串长度时间复杂度为 O(1),因为定义了 len 属性。
    • 通过“空间预分配”( sdsMakeRoomFor)和“惰性空间释放”,防止多次重分配内存。
    • 判断是否结束的标志是 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 了?
    对于 embstr,由于其实现是只读的,因此在对 embstr 对象进行修改时,都会先转化为 raw 再进行修改。 因此,只要是修改 embstr 对象,修改后的对象一定是 raw 的,无论是否达到了 44 个字节。

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

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

4.1.4 应用场景
  • 缓存
    例如:热点数据缓存(例如报表,微博热搜榜),对象缓存,全页缓存。 可以提升热点数据的访问速度。
  • 数据共享分布式
    STRING 类型,因为 Redis 是分布式的独立服务,可以在多个应用之间共享
    例如:分布式 Session
<dependency> 
	<groupId>org.springframework.session</groupId> 
	<artifactId>spring-session-data-redis</artifactId> 
</dependency>
public Boolean getLock(Object lockObject) {
    jedisUtil = getJedisConnetion();
    boolean flag = jedisUtil.setNX(lockObj, 1);
    if (flag) {
        expire(locakObj, 10);
    }
    return flag;
}

public void releaseLock(Object lockObject) {
    del(lockObj);
}
  • 全局ID
    INT 类型,incrby,利用原子性
incrby userid 1000

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

  • 计数器
    INT 类型,incr 方法
    例如:文章的阅读量,微博点赞数,允许一定的延迟,先写入 Redis 再定时同步到数据库。
  • 限流
    INT 类型,incr 方法
    以访问者的 IP 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false。
  • 位统计
    bitmap位图
    因为 bit 非常节省空间(1 MB=8388608 bit),可以用来做大数据量的统计。
    例如:在线用户统计,留存用户统计

4.2 Hash

类似map的一种结构,可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对象)缓存在redis里,然后每次读写缓存的时候,可以操作hash里的某个字段。

例:key=150
value={
“id”: 150,
“name”: “zhangsan”,
“age”: 20
}

hash类的数据结构,主要是用来存放一些对象,把一些简单的对象给缓存起来,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值

4.2.1 存储类型

包含键值对的无序散列表。value 只能是字符串,不能嵌套其他类型。

同样是存储字符串,Hash 与 String 的主要区别?

  1. 把所有相关的值聚集到一个 key 中,节省内存空间
  2. 只使用一个 key,减少 key 冲突
  3. 当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗

Hash 不适合的场景:

  1. Field 不能单独设置过期时间
  2. 没有 bit 操作
  3. 需要考虑数据量分布的问题(value 值非常大的时候,无法分布到多个节点)
4.2.2 操作命令
# 存储
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 
# 获取所有key
hkeys h1 
# 获取所有value
hvals h1 
# 获取所有
hgetall h1

# key操作
# h1 是否存在
hget exists h1 
# 删除h1
hdel h1 
# 返回h1的长度
hlen h1
4.2.3 存储(实现)原理

Redis 的 Hash 本身也是一个 KV 的结构,类似于 Java 中的 HashMap。
外层的哈希(Redis KV 的实现)只用到了 hashtable。当存储 hash 数据类型时, 我们把它叫做内层的哈希。

内层的哈希底层可以使用两种数据结构实现:

  • ziplist:OBJ_ENCODING_ZIPLIST(压缩列表)
  • hashtable:OBJ_ENCODING_HT(哈希表)

一个哈希对象超过配置的阈值(键和值的长度有>64byte 键值对个数>512 个)时, 会转换成哈希表(hashtable)。

问题1:什么时候扩容?
已使用节点数和字典大小之间的比率超过 1:5,触发扩容

4.2.1 应用场景
  • String
    String可以做的事情,Hash 都可以做。
  • 存储对象类型的数据
    比如对象或者一张表的数据,比 String 节省了更多 key 的空间,也更加便于集中管理。
  • 购物车
    key:用户 id;field:商品 id;value:商品数量。
    +1:hincr。-1:hdecr。删除:hdel。全选:hgetall。商品数:hlen。

4.3 List

4.3.1 存储类型

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色。
在这里插入图片描述

4.3.2 操作命令
# 从左边增加元素
lpush queue a 
lpush queue b c
# 从右边增加元素
rpush queue d e
# 删除链表左边的第一个元素
lpop queue 
# 删除链表右边的第一个元素
rpop queue

# 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
blpop queue 
brpop queue

# 用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
lindex queue 0 
lrange queue 0 -1
4.3.3 存储(实现)原理

在早期的版本中,数据量较小时用 ziplist 存储,达到临界值时转换为 linkedlist 进行存储,分别对应 OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_LINKEDLIST 。

3.2 版本之后,统一用 quicklist 来存储。quicklist 存储了一个双向链表,每个节点都是一个 ziplist

quicklist
quicklist(快速列表)是 ziplist 和 linkedlist 的结合体。

typedef struct quicklist { 
	quicklistNode *head; /* 指向双向列表的表头 */ 
	quicklistNode *tail; /* 指向双向列表的表尾 */ 
	unsigned long count; /* 所有的 ziplist 中一共存了多少个元素 */ 
	unsigned long len; /* 双向链表的长度,node 的数量 */ 
	int fill : 16; /* fill factor for individual nodes */ 
	unsigned int compress : 16; /* 压缩深度,0:不压缩; */ 
} quicklist;

quicklistNode 中的*zl 指向一个 ziplist,一个 ziplist 可以存放多个元素。

typedef struct quicklistNode { 
struct quicklistNode *prev; /* 前一个节点 */ 
struct quicklistNode *next; /* 后一个节点 */ 
unsigned char *zl; /* 指向实际的 ziplist */ 
unsigned int sz; /* 当前 ziplist 占用多少字节 */ 
unsigned int count : 16; /* 当前 ziplist 中存储了多少个元素,占 16bit(下同),最大 65536 个 */ 
unsigned int encoding : 2; /* 是否采用了 LZF 压缩算法压缩节点,1:RAW 2:LZF */ unsigned int container : 2; /* 2:ziplist,未来可能支持其他结构存储 */ 
unsigned int recompress : 1; /* 当前 ziplist 是不是已经被解压出来作临时使用 */ 
unsigned int attempted_compress : 1; /* 测试用 */ 
unsigned int extra : 10; /* 预留给未来使用 */ 
} quicklistNode;
4.3.4 应用场景
  • 用户消息时间线 timeline
    因为 List 是有序的,可以用来做用户时间线
  • 消息队列
    List 提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。
    队列:先进先出:rpush blpop,左头右尾,右边进入队列,左边出队列。
    :先进后出:rpush brpop

4.4 set

无序集合,自动去重

Redis 的 Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。示例:sadd key member

4.4.1 存储类型

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

4.4.2 操作命令
# 添加一个或者多个元素
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

4.4.3 存储(实现)原理

Redis 用 intsethashtable 存储 set。
如果元素都是整数类型,就用 inset 存储。
如果不是整数类型,就用 hashtable(数组+链表的存来储结构)。

4.4.4 应用场景
  • 抽奖
    随机获取元素 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
    比关系型数据库简单许多。
  • 商品筛选
# 获取差集 
sdiff set1 set2 
# 获取交集(intersection ) 
sinter set1 set2 
# 获取并集 
sunion set1 set2

4.5 sorted set(zset)

排序的set,去重但是可以排序,写进去的时候给一个分数,自动根据分数排序,最大的特点是有个分数可以自定义排序规则

4.5.1 存储类型

sorted set,有序的 set,每个元素有个 score。 score 相同时,按照 key 的 ASCII 码排序。

数据结构对比:

数据结构是否允许重复元素是否有序有序实现方式
列表 list索引下标
集合Set
有序集合zset分值score
4.5.2 操作命令
# 添加元素
zadd myzset 10 java 20 php 30 ruby 40 cpp 50 python

# 获取全部元素
# zrange 返回有序集中,指定区间内的成员。其中成员的位置按分数值递增(从小到大)来排序。
zrange myzset 0 -1 withscores 
zrevrange myzset 0 -1 withscores

# 根据分值区间获取元素
zrangebyscore myzset 20 30

# 移除元素 也可以根据 score rank 删除
zrem myzset php cpp

# 统计元素个数
zcard myzset

# 分值递增
zincrby myzset 5 python

# 根据分值统计个数
zcount myzset 20 60

# 获取元素 rank
# Redis Zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。
zrank myzset java

# 获取元素 score
zsocre myzset java
# 也有倒序的 rev 操作(reverse)

4.5.3 存储(实现)原理

同时满足以下条件时使用 ziplist 编码:

  • 元素数量小于 128 个
  • 所有 member 的长度都小于 64 字节

在 ziplist 的内部,按照 score 排序递增来存储。插入的时候要移动之后的数据。

超过阈值之后,使用 skiplist(跳表)+dict 存储。

问题:什么是 skiplist?
我们先来看一下有序链表:
在这里插入图片描述
在这样一个链表中,如果我们要查找某个数据,那么需要从头开始逐个进行比较, 直到找到包含数据的那个节点,或者找到第一个比给定数据大的节点为止(没找到)。 也就是说,时间复杂度为 O(n)。同样,当我们要插入新数据的时候,也要经历同样的查找过程,从而确定插入位置。

二分查找法只适用于有序数组,不适用于链表。

假如我们每相邻两个节点增加一个指针(或者理解为有三个元素进入了第二层), 让指针指向下下个节点。
在这里插入图片描述
这样所有新增加的指针连成了一个新的链表,但它包含的节点个数只有原来的一半 (上图中是 7, 19, 26)。在插入一个数据的时候,决定要放到那一层,取决于一个算法(在 redis 中 t_zset.c 有一个 zslRandomLevel 这个方法)

现在当我们想查找数据的时候,可以先沿着这个新链表进行查找。当碰到比待查数据大的节点时,再回到原来的链表中的下一层进行查找。比如,我们想查找 23,查找的路径是沿着下图中标红的指针所指向的方向进行的:
在这里插入图片描述

  1. 23 首先和 7 比较,再和 19 比较,比它们都大,继续向后比较。
  2. 但 23 和 26 比较的时候,比 26 要小,因此回到下面的链表(原链表),与 22 比较。
  3. 23 比 22 要大,沿下面的指针继续向后和 26 比较。23 比 26 小,说明待查数 据 23 在原链表中不存在

在这个查找过程中,由于新增加的指针,我们不再需要与链表中每个节点逐个进行 比较了。需要比较的节点数大概只有原来的一半。这就是跳跃表。

为什么不用 AVL 树或者红黑树?因为 skiplist 更加简洁。

跳表源码:server.h

typedef struct zskiplistNode { 
	sds ele; /* zset 的元素 */ 
	double score; /* 分值 */ 
	struct zskiplistNode *backward; /* 后退指针 */ 
		struct zskiplistLevel { 
			struct zskiplistNode *forward; /* 前进指针,对应 level 的下一个节点 */ 
			unsigned long span; /* 从当前节点到下一个节点的跨度(跨越的节点数) */ 
		} level[]; /* 层 */ 
} zskiplistNode;

typedef struct zskiplist { 
	struct zskiplistNode *header, *tail; /* 指向跳跃表的头结点和尾节点 */ 
	unsigned long length; /* 跳跃表的节点数 */ 
	int level; /* 最大的层数 */ 
} zskiplist;

typedef struct zset {
	dict *dict; 
	zskiplist *zsl; 
} zset;
4.5.4 应用场景
  • 排行榜
# id 为 6001 的新闻点击数加 1:
zincrby hotNews20190926 1 n6001 
# 获取今天点击最多的 15 条:
zrevrange hotNews20190926 0 15 withscores

5. 其他数据结构简介

5.1 BitMaps

Bitmaps 是在字符串类型上面定义的位操作。一个字节由 8 个二进制位组成。
在这里插入图片描述
应用场景:

  • 用户访问统计
  • 在线用户统计
5.2 Hyperloglogs

Hyperloglogs:提供了一种不太准确的基数统计方法,比如统计网站的 UV,存在一定的误差

5.3 Hyperloglogs

5.0 推出的数据类型。支持多播的可持久化的消息队列,用于实现发布订阅功能,借鉴了 kafka 的设计。

6.总结

数据结构总结

对象对象 type 属性值type 命令输出底层可能的存储结构object encoding
字符串对象OBJ_STRING“string”OBJ_ENCODING_INT OBJ_ENCODING_EMBSTR OBJ_ENCODING_RAWint embstr raw
列表对象OBJ_LIST“list”OBJ_ENCODING_QUICKLISTquicklist
哈希对象OBJ_HASH“hash”OBJ_ENCODING_ZIPLIST OBJ_ENCODING_HTziplist hashtable
集合对象OBJ_SET“set”OBJ_ENCODING_INTSET OBJ_ENCODING_HTintset hashtable
有序集合对象OBJ_ZSET“zset”OBJ_ENCODING_ZIPLIST OBJ_ENCODING_SKIPLISTziplist skiplist(包含 ht)

编码转换总结:
在这里插入图片描述

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

Redis 的基本操作

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

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

select 15

清空当前数据库

flushdb

清空所有数据库

flushall

Redis 是字典结构的存储方式,采用 key-value 存储。key 和 value 的最大长度限制
512M

存值

set xhc 1234

存值

get xhc

查看所有键

keys *

获取键总数

dbsize

查看键是否存在

exists xhc

删除键

del xhc

重命名键

rename xhc xhr

查看类型

type xhc

下一篇
Redis学习(二)之“高级特性”

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值