牛牛的Redis的学习笔记--第一章
前言
最近闲来无事,入手了一本钱文品老师的《Redis深度历险-核心原理与应用实践》一书,于是便偶尔翻几页学习一下Redis,毕竟用了好几年了,但是一些底层的核心内容一直没有系统的了解过,借此机会也让自己对Redis有个更深得了解吧。现在把第一张的内容进行总结并且记录一下,自己也在下面使用笔记本记录了一些要点,但是笔记本总是放着放着不知道拿哪里去了,搞了在不同的笔记本上东记一些西记一些,这里写个学习笔记,算是归纳整理吧,放在网上,也能跟朋友们交流下。
1、Redis的基础结构:
Redis常用的数据结构分为:1、string的相关知识:
1.1、string的基本概念及实现
Redis的字符串时动态字符串,是可以修改的,Redis每次申请字符串空间的时候,是采用预分配冗余空间的方式进行分配的。
其内部字符串分配的空间内存,一定是高于储存的字符串的。如果说字符串小于1M那么每次申请到的内存是储存内存的一倍,
如果字符串大于1M时,那么每次申请都是以1M 进行扩容的。注意:字符串的最大空间(字符串的长度)是512M。
而字符串的底部实现则是以位图实现的,字符串是由多个字符组成的,而一个字符则是由八个位(bit)组成的,
所以一串字符串则是由好多个8位数组组成
1.2、string的相关命令
string相当于键值对,支持简单的增删改查。命令一般有:
单个操作:
-
set key
-
get key
批量操作:
> set test 牛牛
OK
> get test
"牛牛"
> mset test0 牛牛 test1 壮壮 test2 瞳瞳
OK
> mget test test0 test1 test2
1) "牛牛"
2) "牛牛"
3) "壮壮"
4) "瞳瞳"
> mget test0 test1 test2
1) "牛牛"
2) "壮壮"
3) "瞳瞳"
设置过期时间:
> expire test 1
(integer) 1
> get test
(nil)
> get test1
"壮壮"
> expire test1 1
(integer) 1
> get test1
(nil)
不存在就添加,但是存在就不改变(返回0/1来区分是否添加成功)
> setnx test2 111
(integer) 0
> setnx test3 111
(integer) 1
> get test2
"瞳瞳"
> get test4
(nil)
2、List(列表)的相关知识
Redis的列表相当于java中的LinkedList
(链表还是双向链表 //TODO:根据之前的Demo写一篇链表的博客---20220300),
这意味着Redis中List的插入和删除操作是非常快的,
其时间复杂度位O(1),但是索引的速度确实很慢,时间负责都为O(n)。
Redis的列表有多种用途,
当他从右边进并从左边出的时候,可以看做是队列。
然当他左边进左边出(或者右进右出)的时候可以就可以看成是栈,
而list也可以当作延时队列进行来使用。在一侧添加消息,并在另一侧对消息进行轮询弹出进行处理。
2.1、list的相关命令
推进和弹出
> rpush listtest C#
(integer) 1
> rpush listtest java
(integer) 2
> rpush listtest php
(integer) 3
> rpush listtest js
(integer) 4
> rpop listtest
"js"
> lpop listtest
"C#"
> rpush tt C# java php
(integer) 3
> rpush tt android node go python
(integer) 7
//push 可添加多个元素
遍历定位
lindex listtest 0
"java"
> lindex listtest 5
(nil)
> lindex listtest -1
"php"
> lindex listtest 1
"php"
//之前的命令已将 C# 和js 弹出,所以此时列表里只存在两个元素,
在坐标中,-1代表着最后一个元素。也就是最右边的元素(列表是从左到右排列)
截取保留(修剪):该命令会保留区间内的所有元素,并且删除区间外的所有元素
返回列表元素:返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素 如果start是0 而end是-1 则返回正向所有元素
//返回列表元素
//截取保留
> lrange tt 0 -1
1) "C#"
2) "java"
3) "php"
4) "android"
5) "node"
6) "go"
7) "python"
> ltrim tt 2 5
OK
> lrange tt 0 -1
1) "php"
2) "android"
3) "node"
4) "go"
//可以发现从第三个元素到第六个元素还存在于列表中,其他的已经被裁剪掉了
2.2、注意:
Redis的列表并不是普通的链表,而是快速链表(quicklist),首先在元素较少的情况下,列表是使用一块连续的内存进行储存的,这个结构是ziplist(压缩列表)。它将所有的元素都彼此紧挨着一起储存。当数据量比较多的时候,则会改成quicklist进行储存,因为普通的链表需要的附加指针空间太大,会浪费时间,还会加重内存的碎片化,。所以Redis将链表和ziplist结合组成了quicklist,就是将多个ziplist使用双向指针进行串联,既能满足快速的插入删除,又不会出现太大的冗余。
3、hash(哈希表--字典)的相关知识
Redis的hash和C#的Dictionary<,>的结构是一毛一样的。
3.1、hash的命令:
设置和获取
获取key下的全部数据
获取key下的二级键值对数量
设置二级键值对数值计数
4、set(集合)的相关知识
Redis的集合其实就是相当于C#中的List<T>具体泛型是什么类型的,需要看具体插入的值(这里泛型仅作比如,具体的类型没啥用)。当集合的最后一个元素被移除后,结构就会被自动删除。内存则会被回收,该结构具有去重的功能,所以一边可用于储存中奖结果名单之类的作用,可以确保用户不会被抽中两次。
4.1、set(集合)的命令
添加和获取元素
<li>sadd key values </li>
<li>smembers key</li>
<br>
判断一个元素是否存在
<li>sismember key value</li>
<br>
查询集合中的元素数量
<li>scard key</li>
<br>
随机弹出一个值
<li>spop key</li>
5、zset(有序集合)的相关知识
Redis的有序集合其实和集合的区别就是它比集合多了个score(权重概念),而集合中的元素,可以根据score的变化,进行排序,
其内部实现用的是一种叫做“跳跃列表”的数据结构。同样的有序集合中最后一个元素被移除的时候数据结构就会被删除,内存就会被回收,有序集合通常用于储存 粉丝列表,用value来保存用户Id,用score来记录关注时间(时间戳),也可储存学生的成绩,value储存学生的id,score储存成绩,个人猜测:微信的步数列表、附近的人,应该是用这个进行储存的。
5.1、zset(有序集合)的命令
添加和获取元素
<li>zadd key score values </li>
按照权重正向排出区间内的元素
<li>zrange key start_index end_index</li>
<br>
按照权重逆向排出区间内的元素
<li>zrevrange key start_index end_index</li>
<br>
查询集合中的元素数量
<li>zcard key</li>
<br>
获取指定value的权重
<li>zscore key value</li>
<br>
获取指定value的排名
<li>zrank key value</li>
<br>
获取权重区间内的元素
<li>zrangebyscore key start_score end_score</li>
<br>
删除一个值
<li>zrem key</li>
Redis的数据结构类型及其通用规则
1、容器类型数据结构
在Redis中 list、set、hash、zset 这四种数据结构属于容器行数据结构,他们均有一下两条通用规则<br>
<li>一、如果容器不存在就创建一个,再进行操作。</li>
<li>二、如果最后一个元素被取出或者删除了,那么容器就会消失,内存回收释放。
Redis的过期时间
Redis的所有数据结构都可以设置过期时间,时间到了,Redis会自动删除相应的对象,但是过期时间是以对象为单位的,所以hash并不能对某个二级key设置过期时间,还有就是如果一个字符串设置了过期时间,那么如果再次调用set命令的话过期时间会消失。
位图的应用场景及使用方式
刚听说位图的时候,觉得这是一块很难懂的玩意,又是bit又是字符串的,但是当潜下心来了解的时候发现其实一点都不难,Redids的字符串,每个英文字母是一个字节,而一个字节是八个bit,而一个字符串就是一堆以八个bit为一组的数组,每个bit不是1就是0,也可以看作非真即假,这个时候,我们可以把这个玩意儿用于储存比如内日签到记录,或者每日登记之类的场景,因为是否签到,就是非真即假,签到了我们就存个1,没签到我们就存个0。这样一年的签到记录也就是个45个字的字符串。。。。
位图的命令也很简单
- setbit key offset value
- getbit key offset
> set h h
OK
> getbit h 0
0
> getbit h 1
1
> getbit h 2
1
> getbit h 3
0
> getbit h 4
1
> getbit h 5
0
> getbit h 6
0
> getbit h 7
0
// 01101000
/*
使用py解析下h的二进制编码
>>> bin(ord('h'))
'01101000'
>>>
*/
可以发现一毛一样,不过!!
正常的二进制是从高位到地位的, 也就是说py解析出来的八个数字。其实是
76543210以这样排列,但是Redis中位图则是从低到高进行排列的。
所以跟正常的二进制刚好是相反的01234567
接下来我们可以通过位图添加出一个字符
setbit t 0 0
0
> setbit t 1 1
0
> setbit t 2 1
0
> setbit t 3 0
0
> setbit t 4 1
0
> setbit t 5 0
0
> setbit t 6 0
0
> setbit t 7 0
0
> get t
"h"
> bitcount h
3
//上面这个命令则是统计这个Key的位图里有几个1,意味着位图如果使用到签到场景的话,可以统计整个记录里面签到多少次。如果指定了参数范围,则可以查看这个范围内的签到次数,但是注意,指定的范围,只能是8的倍数,而不能任意指定。
思考:
常用的数据存储中,我们针对一个完整的结构,是直接存储json还是分开存储属性?比如用户信息。我们是把整个用户信息进行存储还是以用户id为命名空间,下面存储各个信息的单独属性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构