redis深度学习(一):基础数据类型

redis有5中基本数据类型:String(字符串)、list(集合)、set(集合)、hash(哈希)和zset(有序集合)

1.String字符串

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

键值对操作:

127.0.0.1:6379> set name zhangsan

OK

127.0.0.1:6379> get name

"zhangsan"

127.0.0.1:6379> exists name

(integer) 1

127.0.0.1:6379> del name

(integer) 1

127.0.0.1:6379> get name

(nil)

批量键值对操作:

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

127.0.0.1:6379> set name1 zhangsan

OK

127.0.0.1:6379> set name2 list

OK

127.0.0.1:6379> mget name1 name2

1) "zhangsan"

2) "list"

127.0.0.1:6379> mget name1 name2 name3

1) "zhangsan"

2) "list"

3) (nil)

127.0.0.1:6379> mset name1 boy name2 girl name3 unknown

OK

127.0.0.1:6379> mget name1 name2 name3

1) "boy"

2) "girl"

3) "unknown"

过期和set命令扩展:

可以对key设置过期时间,到点自动删除,类似缓存失效时间。

127.0.0.1:6379> set name1 zhangsan

OK

127.0.0.1:6379> get name1

"zhangsan"

127.0.0.1:6379> expire name1 5 # 5s后过期

(integer) 1

127.0.0.1:6379> get name1

“zhangsan"

# wait for 5s

127.0.0.1:6379> get name1

(nil)



127.0.0.1:6379> setex name1 5 zhangsan # 5s后过期,等价于set+expire

OK

127.0.0.1:6379> get name1

"zhangsan"

127.0.0.1:6379> get name1

(nil)



127.0.0.1:6379> setnx name zhangsan # 如果name不存在就执行 set 创建

(integer) 1

127.0.0.1:6379> get name

"zhangsan"

127.0.0.1:6379> setnx name lisi # 因为name已经存在,所以 set 创建不成功

(integer) 0

127.0.0.1:6379> get name # 没有改变

"zhangsan"

计数

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

127.0.0.1:6379> set age 30

OK

127.0.0.1:6379> incr age

(integer) 31

127.0.0.1:6379> incrby age 5

(integer) 36

127.0.0.1:6379> incrby age -5

(integer) 31

127.0.0.1:6379> set size1 9223372036854775807 # Long.Max

OK

127.0.0.1:6379> incr size

(error) ERR value is not an integer or out of range

2.list(列表)

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

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

右边进左边出:队列(先进先出)

127.0.0.1:6379> rpush books python java golang

(integer) 3

127.0.0.1:6379> llen books

(integer) 3

127.0.0.1:6379> lpop books

"python"

127.0.0.1:6379> lpop books

"java"

127.0.0.1:6379> lpop books

"golang"

127.0.0.1:6379> lpop books

(nil)

右边进右边出:栈(先进后出)

127.0.0.1:6379> rpush books python java golang

(integer) 3

127.0.0.1:6379> rpop books

"golang"

127.0.0.1:6379> rpop books

"java"

127.0.0.1:6379> rpop books

"python"

127.0.0.1:6379> rpop books

(nil)

慢操作

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

ltrim 它有两个参数,start_indexend_index 定义了一个区间,在这个区间的值保留,区间外的统统砍掉。我们可以通过ltrim来实现一个定长的链表,这一点非常有用。

index可以为负数,index=-1 表示倒数第一个元素,同样 index=-2 表示倒数第二个元素。

127.0.0.1:6379> rpush books python java golang

(integer) 3

127.0.0.1:6379> lindex books 1 # O(n) 慎用

"java"

127.0.0.1:6379> lrange books 0 -1 # 获取所有元素,O(n) 慎用

1) "python"

2) "java"

3) "golang"

127.0.0.1:6379> ltrim books 1 -1 # O(n) 慎用

OK

127.0.0.1:6379> lrange books 0 -1

1) "java"

2) "golang"

127.0.0.1:6379> ltrim books 1 0 # 这其实是清空了整个列表,因为区间范围长度为负

OK

127.0.0.1:6379> llen books

(integer) 0

快速列表

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

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

3.hash(字典)

Reids的字典相当于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还是字符串,需要根据实际情况权衡。

127.0.0.1:6379> hset books java "think in java"    # 命令行的字符串包含空格时,要用引号括起来

(integer) 1

127.0.0.1:6379> hset books golang "concurrency in go"

(integer) 1

127.0.0.1:6379> hset books python "python cookbook"

(integer) 1

127.0.0.1:6379> hgetall books # key value # 间隔出现

1) "java"

2) "think in java"

3) "golang"

4) "concurrency in go"

5) "python"

6) "python cookbook"

127.0.0.1:6379> entries books

(error) ERR unknown command 'entries'

127.0.0.1:6379> hlen books

(integer) 3

127.0.0.1:6379> hget books java

"think in java"

127.0.0.1:6379> hset books golang "learning go programming" # 因为是更新操作,所以返回 0

(integer) 0

127.0.0.1:6379> hget books golang

"learning go programming"

127.0.0.1:6379> hmset books java "effective java" python "learning python" golang "modern golang programming"

OK

127.0.0.1:6379> hgetall books

1) "java"

2) "effective java"

3) "golang"

4) "modern golang programming"

5) "python"

6) "learning python"

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

127.0.0.1:6379> hincrby user age 1

(integer) 1

127.0.0.1:6379> hget user age

"1"

127.0.0.1:6379> hincrby user age 5

(integer) 6

127.0.0.1:6379> hget user age

"6"

4.set(集合)

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

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

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

127.0.0.1:6379> hgetall books

(empty list or set)

127.0.0.1:6379> sadd books pythone

(integer) 1

127.0.0.1:6379> sadd books java

(integer) 1

127.0.0.1:6379> sadd books golang

(integer) 1

127.0.0.1:6379> sadd books java # 重复

(integer) 0

127.0.0.1:6379> smembers books # 这个顺序和插入的时候并不一致,因为set是无序的

1) "phthon"

2) "golang"

3) "java"

127.0.0.1:6379> sismember books java # 查询某个 value是否存在,相当于 contains(o)

(integer) 1

127.0.0.1:6379> sismember books php

(integer) 0

127.0.0.1:6379> scard books # 获取长度相当于 count()

(integer) 3

127.0.0.1:6379> spop books # 弹出一个

"phthon"

127.0.0.1:6379> spop books

"java"

127.0.0.1:6379> spop books

"golang"

127.0.0.1:6379> spop books

(nil)

5.zset(有序列表)

zset类似于java中的sortedSet和HashMap结合体,一方面它是一个set,保证了内部value的唯一性,另一方面它给每个value赋予一个score,代表这个value的排序权重。它的内部实现的是一种叫做跳跃列表的数据结构。

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

zset可以用来存放粉丝的列表,value值是粉丝的用户ID,score是关注时间。我们可以对粉丝列表按关注时间进行排序。

zset还可以用来存储学生的成绩,value值是学生的ID,score是他的成绩,我们可以按成绩对学生进行排序就可以得到名次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值