抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

redis简介

redis是一个开源,高性能,基于key-value的缓存存储系统,通过多种键值数据类型来适应不同应用场景的缓存与存储需求。
redis是Remote Dictionary Server(远程字典服务器)的缩写,允许其他应用使用TCP协议读写字典的内容。目前redis提供5种键值数据类型。

  1. string(字符串类型)
  2. hash(散列/哈希类型)
  3. list(列表类型)
  4. set(集合类型)
  5. zset(有序集合类型)

redis优点

为啥要用reids呢?mysql不行嘛?

  1. 快! redis 非常快,每秒可执行大约 110000 次的设置(SET)操作,每秒大约可执行 81000 次的读取/获取(GET)操作。(下图是我本机测试的,当然每台电脑不一样看性能的,使用redis-benchmark -n 100000 -q 测试)

upload successful

  1. 原子性! 所有 Redis 操作都是原子操作,这确保如果两个客户端并发访问,Redis 服务器能接收更新的值,因为redis是单线程的。

  2. 丰富的数据类型! redis 支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得 Redis 很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。

  3. 实用工具! Redis 是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis 本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。

redis基本命令

KEYS pattern(符合规则的键名列表)

KEYS pattern
其中pattern支持glob风格通配符的格式,另外由于KEYS命令需要遍历redis中所有的key,而且redis又是单线程的,当key较多时会影响性能,生产环境建议别用喔。

upload successful

EXISTS key(判断key是否存在)

EXISTS key
如果key存在则返回整数类型1,否则返回0

DEL key…(删除键)

DEL key…
可以删除一个或多个key,返回值是删除键的个数。

TYPE key(获得key的类型)

TYPE key
如果key不存在返回none,否则返回五种数据类型其中一个。

redis五种基本数据结构

Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集合) 和 zset(有序集合)。

string(字符串)

string简介

字符串类型是redis中最基本的数据类型,它能够存储任何形式的字符串,一个字符串类型的最大容量是512MB。从源码的 sds.h/sdshdr 文件 中可以看到 Redis 底层对于字符串的定义 SDS,即 Simple Dynamic String 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};

为了对内存做到极致的优化,不同长度的字符串使用不同的结构体来表示。

SDS 与 C 字符串的区别

为什么不考虑直接使用 C 语言的字符串呢?因为 C 语言这种简单的字符串表示方式 不符合 Redis 对字符串在安全性、效率以及功能方面的要求。我们知道,C 语言使用了一个长度为 N+1 的字符数组来表示长度为 N 的字符串,并且字符数组最后一个元素总是 ‘\0’。(下图就展示了 C 语言中值为 “Redis” 的一个字符数组)。

直接使用c字符串会有哪些问题呢?
获取字符串长度为 O(N) 级别的操作 → 因为 C 不保存数组的长度,每次都需要遍历一遍整个数组;
不能很好的杜绝 缓冲区溢出/内存泄漏 的问题 → 跟上述问题原因一样,如果执行拼接 or 缩短字符串的操作,如果操作不当就很容易造成上述问题;
C 字符串 只能保存文本数据 → 因为 C 语言中的字符串必须符合某种编码(比如 ASCII),例如中间出现的 ‘\0’ 可能会被判定为提前结束的字符串而识别不了;

string基本命令
赋值和取值
1
2
3
4
127.0.0.1:6379> SET key value
OK
127.0.0.1:6379> GET key
"value"

当键不存在时会放回空结果(nil)

递增整数
1
2
3
4
5
6
127.0.0.1:6379> set num 0
OK
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2

当字符串是整数类型的时候,可以通过incr方法递增,并返回递增后的值,如果不是整数类型则会报错。

递减整数
1
2
127.0.0.1:6379> decr num
(integer) 1

decr递减,其他事项如上面一样。

增加指定的整数
1
2
127.0.0.1:6379> incrby num 100
(integer) 102

incrby跟递增一样,只是可以指定增加的整数。

减少指定的整数
1
2
127.0.0.1:6379> decrby num 100
(integer) 2

decrby跟递减一样,只是可以指定减少的整数。

增加指定浮点数
1
2
127.0.0.1:6379> incrbyfloat num 100.123
"101.123"

incrbyfloat同上上,只是加的变成浮点数了。。

向尾部追加值
1
2
3
4
127.0.0.1:6379> set key TOM
OK
127.0.0.1:6379> append key " CAT"
(integer) 7

append作用是向尾部追加value,如果key不存在则设置值为value,返回值是追加后字符串的长度。

获取字符串长度
1
2
127.0.0.1:6379> strlen key
(integer) 7

返回键值的长度,如果键不存在则返回0

同时获得/设置多个值
1
2
3
4
5
6
127.0.0.1:6379> MSET key1 value1 key2 value2 key3 value3
OK
127.0.0.1:6379> MGET key1 key2 key3
1) "value1"
2) "value2"
3) "value3"

MSET/MGET 跟SET/GET一样,只是可以同时设置/获取多个键值。

位操作

一个字节由8位二进制组成,在reids中提供了4个命令来直接对二进制位进行位操作。

1
2
127.0.0.1:6379> SET bit bar
OK

upload successful

GETBIT 命令可以获得一个字符串类型指定位置的二进制位的值(0/1),索引从0开始。

1
2
3
4
127.0.0.1:6379> GETBIT bit 0
(integer) 0
127.0.0.1:6379> GETBIT bit 6
(integer) 1

SETBIT命令可以设置字符串类型指定位置的二进制位的值(0/1),返回值是该位置的旧值。

1
2
3
4
5
6
127.0.0.1:6379> SETBIT bit 6 0
(integer) 1
127.0.0.1:6379> SETBIT bit 7 1
(integer) 0
127.0.0.1:6379> GET bit
"aar"

BITCOUNT命令可以获得字符串类型中二进制位位1的个数。当然也可以只统计指定范围的。

1
2
3
4
5
127.0.0.1:6379> BITCOUNT bit
(integer) 10

127.0.0.1:6379> BITCOUNT bit 0 1
(integer) 6

BITOP命令可以对多个字符串类型进行位运算,并将结果存储在指定key中,BITOP命令支持AND、OR、XOR、NOT等位运算操作。

1
2
3
4
127.0.0.1:6379> BITOP OR result bit1 bit2
(integer) 3
127.0.0.1:6379> GET result
"car"

BITPOS命令可以对指定键的第一个位是0或者1的位置,例如bit1为bar,返回值为1,则表示“bar”中第一个值为1的二进制偏移量为1。

1
2
127.0.0.1:6379> BITPOS bit1 1
(integer) 1

hash(哈希/散列)

redis的hash类型相当于java中的HashMap,内部实现也差不多一样的,都是通过数组+链表的链地址法解决哈希冲突的。散列类型适合存储对象。

upload successful

hsah基本命令
赋值和取值

在哈希列表中使用HSET和HGET来设置/获取字段值。
HSET key field value 设置值
HGET key field 获取值
另外HSET命令不区分是INSERT还是UPDATE操作,当执行的是INSERT操作时会放回1,执行时UPDATE操作会返回0,当键本身不存在,HSET命令也会自动创建的拉。

1
2
3
4
127.0.0.1:6379> HSET student:1 name xiaoming
(integer) 1
127.0.0.1:6379> HGET student:1 name
"xiaoming"

跟字符串一样,可以通过HMSET和HMGET来同时设置/获取多个字段值。

1
2
3
4
5
127.0.0.1:6379> HMSET student:1 age 18 sex male
OK
127.0.0.1:6379> HMGET student:1 age sex
1) "18"
2) "male"
判断字段是否存在

HEXISITS key field 用来判断一个字段是否存在,如果存在则返回1,否则返回0。

1
2
127.0.0.1:6379> HEXISTS student:1 name
(integer) 1
当字段不存在时赋值

HSETNX key field value HSETNX与HSET类似,区别在于字段如果已经存在了,HSETNX将不执行任何操作。

1
2
3
4
127.0.0.1:6379> HSETNX student:1 name xiaohong
(integer) 0
127.0.0.1:6379> HGET student:1 name
"xiaoming"
删除字段

HDEL key field… HDEL可以删除一个或多个字段,返回值时删除字段的个数。

1
2
127.0.0.1:6379> HDEL student:1 name
(integer) 1
只获得字段名或者字段值

HKEYS key 获得键中所有字段名
HVALS key 获得键中所有字段值

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> HMSET student:1 name xiaoming  age 18 sex male
OK
127.0.0.1:6379> HKEYS student:1
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> HVALS student:1
1) "xiaoming"
2) "18"
3) "male"

list(列表)类型

list简介

redis的list相当于java中的LinkedList,它内部时基于双向链表实现的。这意味着它插入和删除特别快,但是随机访问比较慢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;

typedef struct listIter {
listNode *next;
int direction;
} listIter;

typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
list基本命令
向两端添加元素

LPUSH key value… 向列表左边添加元素,返回新列表长度
RPUSH key value… 向列表右边添加元素,返回新列表长度

1
2
3
4
127.0.0.1:6379> LPUSH num 1
(integer) 1
127.0.0.1:6379> RPUSH num 2
(integer) 2
向两端弹出元素

LPOP key 从列表左边弹出一个元素,返回元素值,如果列表为空则返回nil
RPOP key 从列表右边弹出一个元素,返回元素值,如果列表为空则返回nil

1
2
3
4
5
6
127.0.0.1:6379> LPUSH num 1 2 3 4 5
(integer) 5
127.0.0.1:6379> LPOP num
"5"
127.0.0.1:6379> RPOP num
"1"
获得列表中元素个数

LLEN key 获得列表长度,如果列表不存在则返回0

1
2
127.0.0.1:6379> LLEN num
(integer) 3
获得列表片段

LRANGE key start stop 获得列表从start到stop(包括stop)的片段,redis起始索引从0开始。LRANGE命令支持负索引,例如-1表示最右边第一个元素,-2表示最右边第二个元素。
注意事项:

  1. 如果start索引位置比stop索引位置靠后则会返回空列表。
  2. 如果stop大于实际索引范围则会返回列表最右边的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> LPUSH num 1 2 3 4 5 6 7 8 9
(integer) 9
127.0.0.1:6379> LRANGE num 0 3
1) "9"
2) "8"
3) "7"
4) "6"
127.0.0.1:6379> LRANGE num 0 -1
1) "9"
2) "8"
3) "7"
4) "6"
5) "5"
6) "4"
7) "3"
8) "2"
9) "1"
删除列表中指定值

LREM key count value 删除列表中前count个值为value的元素,返回值是实际删除元素个数,根据count值的不同,执行起来也不一样

  1. 当count>0,列表会从左到右开始删除前count个value值元素。
  2. 当count<0,列表会从右到左开始删除前|count|个value值元素。
  3. 当count=0.列表会删除所有值为value的元素。

set(集合)类型

set简介

redis集合相当于java中的HashSet,内部键值对是无序,唯一的。在redis内部实现中是使用value为空的散列表实现的,所有这些操作时间复杂度是O(1),而且多个集合类型之间还可以进行并集,交集,差集等操作。

set基本命令
添加/删除元素

SADD key member.. 添加一个或多个元素,如果元素已经存在则忽略,返回成功加入元素个数
SREM key member.. 删除一个或多个元素,返回删除成功的元素个数。

1
2
3
4
5
6
127.0.0.1:6379> SADD set Hello
(integer) 1
127.0.0.1:6379> SADD set WORLD! "I LOVE YOU"
(integer) 2
127.0.0.1:6379> SREM set Hello
(integer) 1
获得集合中的所有元素

SMEMBERS key 返回集合中所有的元素

1
2
3
127.0.0.1:6379> SMEMBERS set
1) "I LOVE YOU"
2) "WORLD!"
集合间运算

SDIFF key… 对多个集合进行差集运算。集合A与集合B的差集表示为A-B,代表属于A且不属于B的元素集合,例如:

upload successful

1
2
3
4
5
6
127.0.0.1:6379> SADD set1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> SADD set2 2 3 4 5 6
(integer) 5
127.0.0.1:6379> SDIFF set1 set2
1) "1"

SINTER key… 对多个集合进行交集运算。集合A与集合B的交集表示为A∩B,代表属于A且属于B的元素集合,例如:

upload successful

1
2
3
4
5
127.0.0.1:6379> SINTER set1 set2
1) "2"
2) "3"
3) "4"
4) "5"

SUNION key… 对多个集合进行并集运算。集合A与集合B的并集表示为A∪B,代表属于A或属于B的元素集合,例如:

upload successful

1
2
3
4
5
6
7
127.0.0.1:6379> SUNION set1 set2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"

zset(有序列表)类型

zset简介

zset有序列表,它比较特殊算的上是redis中最高级的类型了,内部实现是通过【跳跃表】来实现的。一方面他是集合,保证了唯一性,另一方面它每个value都有score值,用来实现排序权重。

zset基本命令
添加元素

ZADD key score member [score member…]
ZADD用来向有序集合中加入一个元素和元素分数,如果元素值存在则会用新分数替换已有分数,返回值是新加入到集合中的元素个数。
其中score可以是整数,浮点数。

1
2
127.0.0.1:6379> ZADD score 87 Tom 67 Peter 100 Xiaoming
(integer) 3
获得元素分数

ZSCORE key member
获得元素分数

1
2
127.0.0.1:6379> ZSCORE score Xiaoming
"100"
获得在某个范围的元素列表

ZRANGE key start stop [WITHSOCRES]
ZRANGE命令会按照元素分数从小到大顺序返回索引从start到stop之间的所有元素(包含stop)。同时如果想要获得元素的元素分数可以在 ZRANGE命令尾部加上WITHSCORES。

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> ZRANGE score 0 -1
1) "Peter"
2) "Tom"
3) "Xiaoming"


127.0.0.1:6379> ZRANGE score 0 -1 WITHSCORES
1) "Peter"
2) "67"
3) "Tom"
4) "87"
5) "Xiaoming"
6) "100"
获得指定分数范围的元素

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZRANGEBYSCORE命令按照元素分数从小到大的顺序返回,分数在min和max之间(包含max)的元素。另外如果不想包括端点值可以在前面加(
后面那两个可选参数跟数据库一样的就不在写了。

1
2
3
127.0.0.1:6379> ZRANGEBYSCORE score 80 +inf
1) "Tom"
2) "Xiaoming"
1
2
127.0.0.1:6379> ZINCRBY score 10 Tom
"97"
增加某个元素的分数

ZINCRBY key incrment member
ZINCRBY 可以增加一个元素的分数,返回值是更改后的分数。

redis命名推荐

在redis中对键的命名没有强制的要求,但比较好的命名是—对象类型:对象ID:对象属性,例如使用user:1:friends来存储ID为1的用户好友列表。另外键的命名一定要有可读性有意义,例如u:1:f虽然可以节省内存空间但是可读性很明显不好,孰重孰轻还是要分的清的。

学习资料引用

5种基本数据结构
《 Redis入门指南第2版》