竹笋

首页 » 问答 » 问答 » 基础万丈高楼平地起Redis基础数据
TUhjnbcbe - 2023/1/1 19:12:00
北京白癜风医院哪家最好 https://jbk.familydoctor.com.cn/bjbdfyy/

千里之行,始于足下。本节我们的学习目标是:快速理解并掌握Redis的基础知识。

由于本节内容是Redis最简单最容易掌握的知识,如果读者已经很熟悉Redis的基础数据结构,从珍惜生命的角度出发,你可以略过本节内容,跳到下一节继续阅读。如果你觉得本节的动画有点晃眼,阅读起来不那么舒服,可以看看作者的另一篇文章《Redis数据结构基础教程》。

要体验Redis,我们先从Redis安装说起。

Redis安装

体验Redis需要使用Linux或者Mac环境,如果是Windows可以考虑使用虚拟机。主要方式有四种:

使用Docker安装。通过Github源码编译。直接安装apt-getinstall(Ubuntu)、yuminstall(RedHat)或者brewinstall(Mac)。如果读者懒于安装操作,也可以使用网页版的WebRedis直接体验。

具体操作如下:

Docker方式

Github源码编译方式

直接安装方式

Redis基础数据结构

Redis有5种基础数据结构,分别为:string(字符串)、list(列表)、set(集合)、hash(哈希)和zset(有序集合)。熟练掌握这5种基本数据结构的使用是Redis知识最基础也最重要的部分,它也是在Redis面试题中问到最多的内容。

本节将带领Redis初学者快速通关这5种基本数据结构。考虑到Redis的命令非常多,这里只选取那些最常见的指令进行讲解,如果有遗漏常见指令,读者可以在评论去留言。

string(字符串)

字符串string是Redis最简单的数据结构。Redis所有的数据结构都是以唯一的key字符串作为名称,然后通过这个唯一key值来获取相应的value数据。不同类型的数据结构的差异就在于value的结构不一样。

字符串结构使用非常广泛,一个常见的用途就是缓存用户信息。我们将用户信息结构体使用JSON序列化成字符串,然后将序列化后的字符串塞进Redis来缓存。同样,取用户信息会经过一次反序列化的过程。

Redis的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为M。

键值对

批量键值对

可以批量对多个字符串进行读写,节省网络耗时开销。

过期和set命令扩展

可以对key设置过期时间,到点自动删除,这个功能常用来控制缓存的失效时间。不过这个「自动删除」的机制是比较复杂的,如果你感兴趣,可以继续深入阅读第26节《朝生暮死——过期策略》。

计数

如果value值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是signedlong的最大最小值,超过了这个值,Redis会报错。

字符串是由多个字节组成,每个字节又是由8个bit组成,如此便可以将一个字符串看成很多bit的组合,这便是bitmap「位图」数据结构,位图的具体使用会放到后面的章节来讲。

list(列表)

Redis的列表相当于Java语言里面的LinkedList,注意它是链表而不是数组。这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n),这点让人非常意外。当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。

Redis的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理。

右边进左边出:队列

右边进右边出:栈

慢操作

lindex相当于Java链表的get(intindex)方法,它需要对链表进行遍历,性能随着参数index增大而变差。

ltrim和字面上的含义不太一样,个人觉得它叫lretain(保留)更合适一些,因为ltrim跟的两个参数start_index和end_index定义了一个区间,在这个区间内的值,ltrim要保留,区间之外统统砍掉。

我们可以通过ltrim来实现一个定长的链表,这一点非常有用。index可以为负数,index=-1表示倒数第一个元素,同样index=-2表示倒数第二个元素。

快速列表

如果再深入一点,你会发现Redis底层存储的还不是一个简单的linkedlist,而是称之为快速链表quicklist的一个结构。

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间,而且会加重内存的碎片化。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。所以Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

hash(字典)

Redis的字典相当于Java语言里面的HashMap,它是无序字典。内部实现结构上同Java的HashMap也是一致的,同样的数组+链表二维结构。第一维hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。

不同的是,Redis的字典的值只能是字符串,另外它们rehash的方式不一样,因为Java的HashMap在字典很大时,rehash是个耗时的操作,需要一次性全部rehash。Redis为了高性能,不能堵塞服务,所以采用了渐进式rehash策略。

渐进式rehash会在rehash的同时,保留新旧两个hash结构,查询时会同时查询两个hash结构,然后在后续的定时任务中以及hash操作指令中,循序渐进地将旧hash的内容一点点迁移到新的hash结构中。当搬迁完成了,就会使用新的hash结构取而代之。当hash移除了最后一个元素之后,该数据结构自动被删除,内存被回收。

hash结构也可以用来存储用户信息,不同于字符串一次性需要全部序列化整个对象,hash可以对用户结构中的每个字段单独存储。这样当我们需要获取用户信息时可以进行部分获取。而以整个字符串的形式去保存用户信息的话就只能一次性全部读取,这样就会比较浪费网络流量。

hash也有缺点,hash结构的存储消耗要高于单个字符串,到底该使用hash还是字符串,需要根据实际情况再三权衡。

同字符串对象一样,hash结构中的单个子key也可以进行计数,它对应的指令是hincrby,和incr使用基本一样。

set(集合)

Redis的集合相当于Java语言里面的HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。

当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。

set结构可以用来存储活动中奖的用户ID,因为有去重功能,可以保证同一个用户不会中奖两次。

zset(有序集合)

zset可能是Redis提供的最为特色的数据结构,它也是在面试中面试官最爱问的数据结构。它类似于Java的SortedSet和HashMap的结合体,一方面它是一个set,保证了内部value的唯一性,另一方面它可以给每个value赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫做「跳跃列表」的数据结构。

zset中最后一个value被移除后,数据结构自动删除,内存被回收。

zset可以用来存粉丝列表,value值是粉丝的用户ID,score是

1
查看完整版本: 基础万丈高楼平地起Redis基础数据