> For the complete documentation index, see [llms.txt](https://jun-wang.gitbook.io/learnjava/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/web-zhong-jian-jian-xue-xi/redis/redis-de-shu-ju-lei-xing.md).

# Redis的数据类型

Redis支持的数据类型一般有5种：

* 字符串String
* 字符串链表linked-list
* 哈希hash
* 字符串集合set
* 有序字符串集合sorted set（zset）

比较常用的是字符串和哈希类型。

## 1. 字符串String

特点：

* 二进制保存的，存入和获取的数据相同；
* Value最多可以容纳的数据长度是512M；

```
# 操作实例
127.0.0.1:6379> set name wangjun
OK
127.0.0.1:6379> get name
"wangjun"
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set name wangjun2
OK
127.0.0.1:6379> get name
"wangjun2"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> flushdb（清空所有的key）
OK
```

## 2. 哈希Hash

特点：

* key，value的map容器；
* 每一个hash可以存储2^32-1（将近43亿）个键值对

```
# 操作实例
127.0.0.1:6379> hmset person name wangjun age 25 email scuwangjun@hotmail.com
OK
127.0.0.1:6379> hgetall person
1) "name"
2) "wangjun"
3) "age"
4) "25"
5) "email"
6) "scuwangjun@hotmail.com"
127.0.0.1:6379> hget person email
"scuwangjun@hotmail.com"
```

在hash类型中，person作为key，key指向的哈希结构如同java的map一样，有许多键值对。在redis中，hash是一个String类型的filed和value的映射表，因此我们存储的数据实际在redis的内存中都是一个一个字符串而已。我们上面的例子中的hash结构如下：

key: person指向 ->

| filed | value                    |
| ----- | ------------------------ |
| name  | wangjun                  |
| age   | 25                       |
| email | <scuwangjun@hotmail.com> |

## 3. 字符串链表linked-list

有序的集合，允许重复，并且是双向链表，因此即可以从左到右也可以从右到左遍历它存储的节点。使用链表的结构就意味着读性能的丧失，因为需要在链表中一个一个的查找，但是插入和删除比较便利。

list存储的最大长度为2^32-1（将近43亿）

内部存储结构：

* ArrayList数组结构
* LinkedList双向链表

因为是双向链表，所以redis提供了左操作和右操作，示例如下：

```
# 操作实例
127.0.0.1:6379> lpush list node3 node2 node1
(integer) 3
127.0.0.1:6379> rpush list node4
(integer) 4
127.0.0.1:6379> lindex list 0
"node1"
127.0.0.1:6379> lindex list 3
"node4"
127.0.0.1:6379> llen list
(integer) 4
127.0.0.1:6379> lpop list
"node1"
127.0.0.1:6379> lindex list 0
"node2"
```

需要指出的是这些命令都不是进程安全的，因为我们操作这些命令的时候，其他客户端可能也在操作，这就有可能造成并发数据安全和一致性问题，幸运的是redis为了克服这些问题提供了链表阻塞命令，他们在运行的时候会给链表加锁，以保证操作链表的命令安全性，比如`blpop`、`brpop`、`rpoplpush`、`brpoplpush`等，当使用这些命令的时候会对对应的链表加锁，加锁的结果就是其他进程不能再读取或者写入该链表，只能等待命令结束。

对链表加锁虽然保证了数据一致性，但也要付出性能的代价，在实际的项目中更多时候我们并不希望阻塞的处理请求，所以这些命令在实际中使用的并不多。后面还会继续探讨高并发锁的问&#x9898;**(loading......)**

## 4. 字符串集合set

redis的集合不是一个线性结构而是一个哈希表结构，它的内部会根据hash分子来存储和查找数据，因此插入、删除和查找的复杂度都是O(1)。

* 没有顺序的集合，不允许重复
* 最大包含的元素数量：2^32-1（将近43亿）
* 集合的每一个元素都是String类型的数据

集合支持并集，交集和差集的运算。

```
# 操作实例
127.0.0.1:6379> sadd set1 s1 s2 s3 s4 s5 s6
(integer) 6
127.0.0.1:6379> sadd set2 s0 s1 s2 s7 s8
(integer) 5
127.0.0.1:6379> scard set1（长度）
(integer) 6
127.0.0.1:6379> sdiff set1 set2（差集）
1) "s3"
2) "s6"
3) "s4"
4) "s5"
127.0.0.1:6379> sinter set1 set2（交集）
1) "s1"
2) "s2"
127.0.0.1:6379> smembers set1
1) "s4"
2) "s6"
3) "s3"
4) "s1"
5) "s2"
6) "s5"
127.0.0.1:6379> spop set1（随机弹出一个元素）
"s1"
127.0.0.1:6379> smembers set1
1) "s4"
2) "s6"
3) "s3"
4) "s2"
5) "s5"
127.0.0.1:6379> sunion set1 set2（并集）
1) "s0"
2) "s8"
3) "s7"
4) "s6"
5) "s3"
6) "s2"
7) "s5"
8) "s4"
9) "s1"
```

## 5. 有序字符串集合sorted set

有序集合和集合类似，只是它是有序的，和无序集合的主要区别在于每一个元素除了值之外，它还会多一个浮点型的分数，根据这个分数，redis就可以支持对分数进行排序，和无序集合一样对于**每一个元素都是唯一的**，但是对于不同元素而言，它的分数可以一样。元素的值也是String数据类型，在满足一定的条件下，也可以对值进行排序。

**有序集合的数据结构：**

有序集合的底层存储数据结构有两种：ziplist、哈希表+跳表结构，根据配置选择。

* 当一个key存储的items小于64的时候使用了ziplist结构，查询的时间复杂度是O(n);
* 当大于64的时候采用了hash+skiplist的存储结构，添加和删除的时间复杂度为O(log(n));
* 如果仅仅是查询元素的话可以直接使用hash，其复杂度是O(1)。

有序集合的命令和无序集合的命令是接近的，只是在这些命令的基础上，会增加对于排序的操作。

```
# 操作实例
27.0.0.1:6379> zadd zset1 1 s1 2 s2 3 s3
(integer) 3
127.0.0.1:6379> zcard zset1 # 获取成员数量
(integer) 3
127.0.0.1:6379> zcount zset1 1 2  # 返回分数范围内的成员数量
(integer) 2
127.0.0.1:6379> zlexcount zset1 [s1 [s2 # 返回成员之间的成员数量
(integer) 2
```

**有序集合的使用场景：**

* 微博的热门话题
* 游戏的排名

## 6. 基数HyperLogLog

基数严格来说是一种算法，类似基数排序。如果要存储一本英文字典，内存可能不够，但是字母的数量是有限的，我们可以计算存储这些字母需要的空间。它存储的值是String类型的。

基数存储的优点在于：在输入元素的数量或者体积很大时，计算基数所需的空间总是固定的，并且是很小的。缺点是：它是估算基数的算法，会有0.81%的误差，而且只会根据输入元素来计算基数，而不会存储输入元素本身，因此无法像其他存储结构那样返回输入的各个元素，因此应用在对准确性不是很重要的场景，比如QQ同时在线人数、网站IP访问数量等。

```
# 操作实例
127.0.0.1:6379> pfadd h1 a
(integer) 1
127.0.0.1:6379> pfadd h1 b
(integer) 1
127.0.0.1:6379> pfadd h1 c
(integer) 1
127.0.0.1:6379> pfadd h1 a
(integer) 0
127.0.0.1:6379> pfcount h1
(integer) 3
127.0.0.1:6379> pfadd h2 d
(integer) 1
127.0.0.1:6379> pfadd h2 e
(integer) 1
127.0.0.1:6379> pfmerge h3 h1 h2
OK
127.0.0.1:6379> pfcount h3
(integer) 5
```

> 参考：
>
> <http://www.redis.cn/commands.html#>
