• 极客文库-知识库上线!
  • 极客文库小编@勤劳的小蚂蚁,为您推荐每日资讯,欢迎关注!
  • 每日更新优质编程文章!
  • 更多功能模块开发中。。。

分布式缓存技术redis系列(二)详细讲解redis数据结构(内存模型)以及常用命令

Redis 数据类型

与 Memcached 仅支持简单的 key-value 结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String、List、Hash、Set 和 Sorted Set。
Redis 数据类型内存结构分析
Redis内部使用一个 redisObject 对象来表示所有的 key 和 value。redisObject 主要的信息包括数据类型(type)、编码方式(encoding)、数据指针(ptr)、虚拟内存(vm)等。type 代表一个 value 对象具体是何种数据类型,encoding 是不同数据类型在 redis 内部式。
redisObject 对象示意图

下面分别介绍 5 种数据类型的用法。

String 类型
字符串是 Redis 值的最基础的类型。Redis中使用的字符串是通过包装的,基于 c 语言字符数组实现的简单动态字符串(simple dynamic string, SDS)一个抽象数据结构。其源码定义如下:
struct sdshdr {

    int len; //len 表示 buf 中存储的字符串的长度。

    int free//free 表示 buf 中空闲空间的长度。

    char buf[]; //buf 用于存储字符串内容。

};

C 语言字符串内存结构示意图 1
假设上图是”hello”字符串的内存结构,这个时候 len=5,free=2 那么 redis 包装后(sds)其长度为:
sizeof(struct sdshdr) + len + free + 1
其中 buf 的大小为:
len + free + 1
1 表示 1 个字节是用来存储结束符’’的。Redis字符串是二进制安全的,因为二进制数据通常会有中间某个字节存储’’的这种情况,这意味着一个Redis字符串可以包含任何种类的数据,例如一个 JPEG 图像或者一个序列化的 Ruby 对象。二进制是否安全,简单的理解就是能不能在字符串中间有‘’,如下图:
C 语言字符串内存结构示意图 2
对于上图,sds 认为这个字符串是“hello world”,而 C 语言的字符处理函数认为这个字符串是“hello”。
应用场景
String 是最常用的一种数据类型,普通的 key/value 存储都可以归为此类。
常用命令
(1)set——设置 key 对应的值为 String 类型的 value
(2)get——获取 key 对应的值
192.168.2.129:6379setnx name lisi

(integer) 0

192.168.2.129:6379setnx name1 wangwu

(integer) 1

192.168.2.129:6379get name

zhangsan

192.168.2.129:6379get name1

wangwu

192.168.2.129:6379>
(3)mget——批量获取多个 key 的值,如果可以不存在则返回 nil
192.168.2.129:6379mget name name1

1) “zhangsan

2) “wangwu

192.168.2.129:6379mget name name1 name2

1) “zhangsan

2) “wangwu

3) (nil)

192.168.2.129:6379>
(4)incr && incrby——incr 对 key 对应的值进行加加操作,并返回新的值;incrby 加指定值
192.168.2.129:6379get age

“20”

192.168.2.129:6379incr age

(integer) 21

192.168.2.129:6379set age1 “20”

OK

192.168.2.129:6379get age1

“20”

192.168.2.129:6379incr age1

(integer) 21

192.168.2.129:6379incrby age 3

(integer) 24
从上面的结果可以看出,我们对 int 型的 age 和 string 型的 age1 都能进行 incr 操作时,
实际上 type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int,如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如”20″这样的字符串,当遇到 incr、decr 等操作时会转成数值型进行计算,此时 redisObject 的 encoding 字段为 int。如果你试图对 name 进行 incr 操作则报错。
192.168.2.129:6379> incr name

(error) ERR value is not an integer or out of range
(5)decr && decrby——decr 对 key 对应的值进行减减操作,并返回新的值;decrby 减指定值
192.168.2.129:6379decr age

(integer) 23

192.168.2.129:6379decrby age 3

(integer) 20

192.168.2.129:6379>
(6)其他命令
Hash 类型
Hash 是一个 String 类型的 field 和 value 之间的映射表,即 redis 的 Hash 数据类型的 key(hash 表名称)对应的 value 实际的内部存储结构为一个 HashMap,因此 Hash 特别适合存储对象。相对于把一个对象的每个属性存储为 String 类型,将整个对象存储在 Hash 类型中会占用更少内存。
Hash 数据类型内部结构示意图
当前 HashMap 的实现有两种方式:当 HashMap 的成员比较少时 Redis 为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的 HashMap 结构,这时对应的 value 的 redisObject 的 encoding 为 zipmap,当成员数量增大时会自动转成真正的 HashMap,此时 encoding 为 ht。
应用场景
用一个对象来存储用户信息,商品信息,订单信息等等。
常用命令
(1)hset——设置 key 对应的 HashMap 中的 field 的 value
(2)hget——获取 key 对应的 HashMap 中的 field 的 value
192.168.2.129:6379hset myhash name zhangsan

(integer) 1

192.168.2.129:6379hset myhash age 20

(integer) 1

192.168.2.129:6379hget myhash name

zhangsan

192.168.2.129:6379hget myhash age

“20”

192.168.2.129:6379>
(3)hgetall——获取 key 对应的 HashMap 中的所有 field 的 value
192.168.2.129:6379hgetall myhash

1) “name

2) “zhangsan

3) “age

4) “20”

192.168.2.129:6379>
(4)其它命令
List 类型
Redis 的 List 类型其实就是每一个元素都是 String 类型的双向链表。我们可以从链表的头部和尾部添加或者删除元素。这样的 List 既可以作为栈,也可以作为队列使用。
List 数据结构内部示意图
应用场景
如好友列表,粉丝列表,消息队列,最新消息排行等。
常用命令
(1)lpush——在 key 对应的 list 的头部添加一个元素。
(2)lrange——获取 key 对应的 list 的指定下标范围的元素,-1 表示获取所有元素。
(3)lpop——从 key 对应的 list 的尾部删除一个元素,并返回该元素。
192.168.2.129:6379lpush newlist news1 news2 news3

(integer) 3

192.168.2.129:6379lrange newlist 0 -1

1) “news3

2) “news2

3) “news1

192.168.2.129:6379lpop newlist

news3

192.168.2.129:6379lrange newlist 0 -1

1) “news2

2) “news1

192.168.2.129:6379>
从上面的操作可以看出,lpush、lpop 从表头操作。
(4)rpush——在 key 对应的 list 的尾部添加一个元素。
(5)rpop——从 key 对应的 list 的尾部删除一个元素,并返回该元素。
192.168.2.129:6379rpush newlist2 news1 news2 news3

(integer) 3

192.168.2.129:6379lrange newlist2 0 -1

1) “news1

2) “news2

3) “news3

192.168.2.129:6379rpop newlist2

news3

192.168.2.129:6379>
从上面的操作可以看出,rpush、rpop 从表尾操作
(6)其他命令
Set 类型
Redis 集合(Set 类型)是一个无序的 String 类型数据的集合,类似 List 的一个列表,与 List 不同的是 Set 不能有重复的数据。实际上,Set 的内部是用 HashMap 实现的,Set 只用了 HashMap 的 key 列来存储对象。我们来看看 java 中 HashSet 的源码:
public class HashSet<E>
    extends AbstractSet<E>

    implements Set<E>, Cloneablejava.io.Serializable

{
    static final long serialVersionUID = –5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map

    private static final Object PRESENT = new Object();


    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
   public HashSet() {
        map = new HashMap<>();
}

……



/**
     * Returns an iterator over the elements in this set.  The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set
     * @see ConcurrentModificationException
     */

    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
可见创建一个 HashSet 的时候实际上创建了一个 HashMap;Set 中的元素,只是存放在了底层 HashMap 的 key 上,底层 HashMap 的 value 列为空,遍历 HashSet 的时候从 HashMap 中取出 keySet 来遍历。
Set 底层结构示意图
应用场景
集合有取交集、并集、差集等操作,因此可以求共同好友、共同兴趣、分类标签等。
常用命令
(1)sadd——在 key 对应的 set 中添加一个元素。
(2)smembers——获取 key 对应的 set 的所有元素。
(3)spop——随机返回并删除 key 对应的 set 中的一个元素。
192.168.2.129:6379sadd myset news1 news2 news3

(integer) 3

192.168.2.129:6379smembers myset

1) “news3

2) “news2

3) “news1

192.168.2.129:6379spop myset

news3

192.168.2.129:6379>
(4)sdiff——求给定 key 对应的 set 与第一个 key 对应的 set 的差集
192.168.2.129:6379smembers myset

1) “news3

2) “news2

3) “news1

192.168.2.129:6379sadd myset2 news3 news4 news5

(integer) 3

192.168.2.129:6379smembers myset2

1) “news4

2) “news3

3) “news5

192.168.2.129:6379sdiff myset myset2

1) “news1

2) “news2

192.168.2.129:6379>
(5)suion——求给定 key 对应的 set 并集
192.168.2.129:6379> sunion myset myset2

1“news3”

2“news1”

3“news2”

4“news4”

5“news5”

192.168.2.129:6379>
(6)sinter——求给定 key 对应的 set 交集
192.168.2.129:6379sinter myset myset2

1) “news3

192.168.2.129:6379>
(7)其他命令
SortSet
SortSet 顾名思义,是一个排好序的 Set,它在 Set 的基础上增加了一个顺序属性 score,这个属性在添加修改元素时可以指定,每次指定后,SortSet 会自动重新按新的值排序。
sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score。
应用场景
如按时间排序的时间轴。
常用命令
(1)zadd ——在 key 对应的 zset 中添加一个元素
(2)zrange——获取 key 对应的 zset 中指定范围的元素,-1 表示获取所有元素
192.168.2.129:6379> zadd myzset 1 “one” 2 “two” 3 “three”

(integer) 3

192.168.2.129:6379> zrange myzset 0 –1

1“one”

2“two”

3“three”

192.168.2.129:6379> zrange myzset 0 –1 withscores

1“one”

2“1”

3“two”

4“2”

5“three”

6“3”

192.168.2.129:6379>
(3)zrem——删除 key 对应的 zset 中的一个元素
192.168.2.129:6379zrem myzset one

(integer) 1

192.168.2.129:6379zrange myzset 0 -1 withscores

1) “two

2) “2”

3) “three

4) “3”

192.168.2.129:6379>
(4)其它命令 

Redis 常用命令

键值常用命令
keys/exits/del/expire/ttl/move/persist/randomkey/rename/type
服务器常用命令
ping/echo/select/quit/dbsize/info/config get/flushdb/flushall
这些命令都很容易使用,就不举例说明了。到此,redis 的数据类型以及常用命令已经介绍完毕,下一篇我们将学习 redis 的一些高级特性。
参考文档

喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

欢迎 注册账号 登录 发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ

247507792

工作时间:09:00-23:00

新浪微博:点我访问

个人博客:点我访问