Redis实战 | Redis数据5种类型详解

Redis是目前非常主流的KV数据库,它因高性能的读写能力而著称,其实还有另外一个优势,就是Redis提供了更加丰富的数据类型,这使得Redis有着更加广泛的使用场景。那Redis提供给用户的有哪些数据类型呢?主要有:string(字符串)、List(列表)、Set(集合)、Hash(哈希)、Zset(有序集合)、HyperLogLogs(计算基数用的一种数据结构)、Streams(Redis 5.0提供一种建模日志用的全新数据结构)。

需要注意的是这里说的数据类型是指Redis值的数据类型,而Redis键的类型总是string。

本文主要详解一下前5种,也就是最常用的5种数据类型。剩下两种可上Redis官网(redis.io)自行了解下。另外,Redis已经是目前Java程序员面试必问内容,而 “Redis有哪些数据类型?” 更是面试官张口就来的基础问题。如果连这第一问都过不了,那基本上Redis这块已经凉凉了。

string | 字符串类型

redis的字符串类型,可以存储字符串、整数或者浮点数。如果存储的是整数或者浮点数,还能执行自增或者自减操作。

并且redis的string类型是二进制安全的,它可以包含任何数据,比如一个序列化的对象、一个图片字节流等。不过存储大小是有上限的-512M

这里解释下二进制安全的含义:简单地来说,就是字符串不是根据某种特殊的标志位来(C语言的\0)解析的,无论输入的是什么,总能保证输出是处理的原始输入而不是根据某种特殊格式来处理。

redis是怎么实现string类型的二进制安全的呢?

答案是Sds (Simple Dynamic String,简单动态字符串),Redis底层定义了自己的一种数据结构。(简单了解下)

typedefchar*sds;structsdshdr{// buf 已占用长度intlen;// buf 剩余可用长度intfree;// 实际保存字符串数据的地方charbuf[];};

操作字符串的一些命令

基础set、get、del命令及示例

get keyname 获取存储在给定键中的值

set keyname value 设置存储点给定键中的值

del keyname 删除存储在给定键中的值(通用命令,适用于所有类型)

127.0.0.1:6379>sethappytodayOK127.0.0.1:6379>gethappy"today"127.0.0.1:6379>delhappy(integer) 1127.0.0.1:6379>gethappy(nil)127.0.0.1:6379>

自增和自减命令

incr keyname 将键存储的值加1

decr kename 将键存储的是减1

incrby keyname amount 将键存储的值加上整数amount

decrby keyname amount 将键存储的值减去整数amount

incrbyfloat keyname amount 将键存储的值加上浮点数amount

127.0.0.1:6379>setnumber1OK127.0.0.1:6379>getnumber"1"127.0.0.1:6379>incrnumber(integer) 2127.0.0.1:6379>getnumber"2"127.0.0.1:6379>decrnumber(integer) 1127.0.0.1:6379>getnumber"1"127.0.0.1:6379>incrbynumber3(integer) 4127.0.0.1:6379>getnumber"4"127.0.0.1:6379>decrbynumber2(integer) 2127.0.0.1:6379>getnumber"2"127.0.0.1:6379>incrbyfloatnumber1.23"3.23"127.0.0.1:6379>getnumber"3.23"

子串和二进制位命令

append keyname value 追加value值到指定字符串末尾

getrange keyname start end 获取start到end范围的所有字符组成的子串,包括start和end在内

setrange keyname offset value 从偏移量 offset 开始, 用 value 参数覆写(overwrite)键 keyname 储存的字符串值。

getbit keyname offset 对 keyname 所储存的字符串值,获取指定偏移量上的位(bit)。

setbit keyname offset value 对 keyname 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

注意redis的索引以0为开始

127.0.0.1:6379>gethello"world"127.0.0.1:6379>appendhello,java(integer) 10127.0.0.1:6379>gethello"world,java"127.0.0.1:6379>getrangehello2 5"rld,"127.0.0.1:6379>setrangehello6redis(integer) 11127.0.0.1:6379>gethello"world,redis"127.0.0.1:6379> 127.0.0.1:6379>setbitbitstr100 1(integer) 0127.0.0.1:6379>getbitbitstr100(integer) 1127.0.0.1:6379>getbitstr"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b"127.0.0.1:6379>

其他几个重要的命令

setnx key value 只在键 key 不存在的情况下, 将键 key 的值设置为 value;若键 key 已经存在, 则 SETNX 命令不做任何动作。

setex key seconds value 将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。

说明一下: - SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。命令在设置成功时返回 1 , 设置失败时返回 0 。 - SETEX命令相当于SET key value 和 EXPIRE key seconds # 设置生存时间两条命令的效果,但是SETEX是一个原子操作。

127.0.0.1:6379>existsmark(integer) 0127.0.0.1:6379>setnxmarkabcd(integer) 1127.0.0.1:6379>setnxmarkdefg(integer) 0127.0.0.1:6379>getmark"abcd"127.0.0.1:6379>setexcachekey20ak98OK127.0.0.1:6379>getcachekey"ak98"127.0.0.1:6379>ttlcachekey(integer) 2

List | 列表类型

Redis的列表类型和许多程序语言中的列表类型类似,可以有序地存储多个字符串。

支持从列表的左端和右端推入或弹出元素。Redis列表的底层实现是压缩列表(redis内容自己实现的数据结构)和双端链表。看下图


列表操作命令详解

lpush key value [value...]

将一个或者多个value值插入列表的表头。如果 key 不存在,会创建一个空列表并执行 LPUSH 操作。当 key 存在但不是列表类型时,返回一个错误。

执行 LPUSH 命令后,会返回列表的长度。

127.0.0.1:6379>lpushlistkeya(integer)1127.0.0.1:6379>lpushlistkeyabc(integer)4127.0.0.1:6379>lrangelistkey0-11)"c"2)"b"3)"a"4)"a"127.0.0.1:6379>

list类型可以加入重复的元素,这个和后面要说的set(集合类型)不同。

lrange listkey 0 -1 是获取整个列表的内容

类似的rpush命令是从列表右端加入元素

LPOP key

从列表的左端弹出一个值,并返回被弹出的值

127.0.0.1:6379>lrangelistkey0-11)"c"2)"b"3)"a"4)"a"127.0.0.1:6379>lpoplistkey"c"127.0.0.1:6379>lrangelistkey0-11)"b"2)"a"3)"a"127.0.0.1:6379>

lrange key start end

获取列表key在给定start到end范围上的所有元素值。

0表示第一个元素,-1表示最后一个元素。

127.0.0.1:6379>lrangelistkey0-11)"b"2)"a"3)"a"127.0.0.1:6379>lrangelistkey011)"b"2)"a"127.0.0.1:6379>

lindex key index

获取列表在给定index位置上的单个元素值。

可以是-1,代表最后一个元素,-2表示倒数第二个元素,以此类推。

127.0.0.1:6379>lrangelistkey0-11)"b"2)"a"3)"a"127.0.0.1:6379>lindexlistkey0"b"127.0.0.1:6379>lindexlistkey-1"a"127.0.0.1:6379>lindexlistkey3(nil)127.0.0.1:6379>

blpop key [key …] timeout

blpop 是阻塞式的弹出命令,它是lpop key 命令的阻塞版本。当给定列表内没有任何元素可供弹出的时候,连接将被 blpop 命令阻塞,直到等待超时或发现可弹出元素为止。

当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。

因此可以分两种情况讨论,一种是至少有一个key存在且是非空列表,则blpop命令不会阻塞,另外是blpop命令中的列表是空列表,此时会在超时时间内阻塞。

先看下非阻塞的场景,返回值是第一个非空列表名和被弹出元素。

127.0.0.1:6379>lpushlist1hellojava(integer)2127.0.0.1:6379>lpushlist2helloredis(integer)2127.0.0.1:6379>blpoplist2list1list301)"list2"2)"redis"127.0.0.1:6379>

阻塞的场景,在执行了blpop book1 book2 300 命令后会一直阻塞住。

127.0.0.1:6379>existsbook1(integer) 0127.0.0.1:6379>existsbook2(integer) 0127.0.0.1:6379>blpopbook1book2300

这个时候,我们如果在开另外一个redis客户端,执行如下lpush命令往book1列表中推入一个元素。

127.0.0.1:6379>lpushbook1springboot(integer) 1127.0.0.1:6379>

此时,再回到原来阻塞的客户端,已经弹出了元素。

127.0.0.1:6379>existsbook1(integer) 0127.0.0.1:6379>existsbook2(integer) 0127.0.0.1:6379>blpopbook1book23001) "book1"2) "springboot"(237.45s)127.0.0.1:6379>

通过利用Redis列表类型的阻塞式命令的特性,我们最容易想到的就是可以用它实现一个简易版的消息队列。

set | 集合类型

Redis的集合以无序的方式存储多个不同的元素。这里要注意的是无序和不同。

除了对集合能快速执行添加、删除、检查一个元素是否在集合中之外,还可以对多个集合执行交集、并集和差集运算。

底层实现概述

Redis的集合类型底层实现主要是通过一种叫做字典的数据结构。不过Redis为了追求极致的性能,会根据存储的值是否是整数,选择一种intset的数据结构。当满足一定条件后,会切换成字典的实现。

这里大概解释下字典: 其实是由一集键值对(key-value pairs)组成, 各个键值对的键各不相同, 程序可以添加新的键值对到字典中, 或者基于键进行查找、更新或删除等操作。

Redis的set(集合)在使用字典数据结构保存数据时,将元素保存到字典的键里面, 而字典的值则统一设为 NULL 。

集合类型操作命令详解

sadd key member [member...]

将一个或者多个元素添加到集合key中,已存在于集合中的元素将被忽略。返回新添加的元素数量,不包括忽略的元素。

srem key member [member...]

移除集合中的一个或多个元素,不存在的元素将被忽略。返回被成功移除的元素数量。

sismember key meber

检查元素member是否存在于集合key中。如果是返回1,不是或者key不存在,返回0。

scard key 返回集合包含的元素数量

spop key 随机移除集合中的一个元素,并返回被移除元素。

smembers key 返回集合中包含的所有元素

127.0.0.1:6379>saddset1javaspringredis(integer)3127.0.0.1:6379>smembersset11)"redis"2)"spring"3)"java"127.0.0.1:6379>scardset1(integer)3127.0.0.1:6379>sremset1spring(integer)1127.0.0.1:6379>sismemberset1spring(integer)0127.0.0.1:6379>smembersset11)"redis"2)"java"127.0.0.1:6379>saddset1mysqlspring(integer)2127.0.0.1:6379>spopset1"redis"127.0.0.1:6379>smembersset11)"mysql"2)"spring"3)"java"127.0.0.1:6379>

下面是一些用于处理多个集合的一些命令

sdiff key [key...] 返回存在于第一个集合,但不存在于其他集合中的元素(数学上的差集运算)

sinter key [key...] 返回同时存在于所有集合中的元素(数学上的交集运算) sunion key [key...] 返回至少存在于一个集合中的元素(数学上的并集运算)

127.0.0.1:6379>smembersset11)"mysql"2)"spring"3)"java"127.0.0.1:6379>smembersset21)"mysql"2)"springboot"3)"redis"127.0.0.1:6379>sdiffset1set21)"java"2)"spring"127.0.0.1:6379>sinterset1set21)"mysql"127.0.0.1:6379>sunionset1set21)"mysql"2)"springboot"3)"java"4)"spring"5)"redis"127.0.0.1:6379>

hash | 散列表(哈希表)

Redis的hash类型其实就是一个缩减版的redis。它存储的是键值对,将多个键值对存储到一个redis键里面。

底层实现概述

hash类型的底层主要也是基于字典这种数据结构来实现的。

redis内部在实现hash数据类型的时候是使用了两种数据结构。在创建一个空的hash表时,默认使用的是ziplist的数据结构,满足一定条件后会转成字典的形式。

散列表操作命令详解

hmget hash-key key [key...] 从散列表里面获取一个或多个键的值

hmset hash-key key value [key value...] 为散列表里面的一个或多个键设置值 hdel hash-key key [key...] 删除散列表里面的一个或多个键值对,返回删除成功的键值对的数量

hlen hash-key 返回散列表包含的键值对的数量

hexists hash-key key 检查给定的键是否存在于散列表中

hkeys hash-key 获取散列包含的所有键

hvals hash-key 获取散列包含的所有值

hgetall hash-key 获取散列包含的所有键值对

127.0.0.1:6379>hmsethash1usernametomemail123@123year12OK127.0.0.1:6379>hmgethash1email1)"123@123"127.0.0.1:6379>hlenhash1(integer)3127.0.0.1:6379>hdelhash1year(integer)1127.0.0.1:6379>hexistshash1year(integer)0127.0.0.1:6379>hkeyshash11)"username"2)"email"127.0.0.1:6379>hvalshash11)"tom"2)"123@123"127.0.0.1:6379>hgetallhash11)"username"2)"tom"3)"email"4)"123@123"127.0.0.1:6379>

zset | 有序集合

有序集合相比较于集合,多个有序两个字,我们知道set集合类型存储的元素是无序的,那Redis有序集合是怎么保证有序的?使用分值,有序集合里存储着成员与分值之间的映射,并提供了分值处理命令,以及根据分值的大小有序地获取成员或分值的命令。

底层实现概述

Redis有序集合的实现使用了一种叫跳跃表的数据结构(简称跳表,可自行查阅),同时也使用到了前面提到的压缩列表。也是满足一定条件的话,会自行转换。

有序集合操作命令详解

zadd z-key score memer [score member...] 将带有给定分值的成员添加到有序集合里面

zrem z-key member [member...] 从有序集合里面移除给定的成员,并返回被移除成员的数量

zcard z-key 返回有序集合包含的成员数量

zincrby z-key increment member 将member成员的分值加上increment

zcount z-key min max 返回分值介于min和max之间的成员数量

zrank z-key member 返回成员member在有序集合中的排名

zscore z-key member 返回成员member的分值

zrange z-key start stop [withscores] 返回有序集合中排名介于start和stop之间的成员,如果给定了可选的withscores选项,name命令会将成员的分值也一并返回。

zrevrank z-key member 返回有序集合里成员member的排名,成员按照分值从大到小排列。

zrevrange z-key start stop 返回有序集合给定排名范围内的成员,成员按照分值从大到小排列。

zrangebyscore z-key min max 返回有序集合中分值介于min和max之间的所有成员

127.0.0.1:6379>zaddzset110a12b1c3d20e(integer)5127.0.0.1:6379>zcardzset1(integer)5127.0.0.1:6379>zcountzset1210(integer)2127.0.0.1:6379>zrankzset1d(integer)1127.0.0.1:6379>zscorezset1e"20"127.0.0.1:6379>zrangezset1351)"b"2)"e"127.0.0.1:6379>zrevrankzset1d(integer)3127.0.0.1:6379>zrevrangezset1351)"d"2)"c"127.0.0.1:6379>zrangebyscorezset15101)"a"127.0.0.1:6379>

原文链接:https://www.toutiao.com/a6996609709968982536/?log_from=1421a94b2cf08_1641554260183

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容