Redis用法和总结

什么是Redis

MySQL:关系型数据库

NoSQL:非关系型的数据库,数据之间无关系,无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式,有非常高的读写性能,尤其在大数据量下,表现非常优秀。

  • 键值(Key-Value)存储数据库(Redis)
  • 列存储数据库
  • 文档型数据库(MongoDb)
  • 图形(Graph)数据库

Redis 就是一个 Key-Value 存储系统。支持的数据类型包括string(字符串)、hash(哈希)、list(链表)、set(集合)和zset(有序集合)等等。

Redis的性能

官方的bench-mark数据:
测试完成了50个并发执行100000个请求。
设置和获取的值是一个256字节字符串。
结果:读的速度是110000次/s,写的速度是81000次/s 。

Redis的持久化

Redis运行在内存中但是可以持久化到磁盘

Redis提供了多种不同级别的持久化方式:一种是RDB,另一种是AOF.

RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照。当redis需要做持久化时,redis会fork一个子进程;子进程读出数据并将数据写到磁盘上一个临时RDB文件中;当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处就是可以copy-on-write,缺点就是在redis异常死掉时, 最近的数据会丢失

AOF redis每执行一个修改数据的命令,都会把它添加到aof文件中,当redis重启时,将会读取AOF文件进行“重放”以恢复到redis关闭前的最后时刻。

Redis 可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整

Redis为什么这么快

1)绝大部分请求是纯粹的内存操作(非常快速)
2)采用单线程,避免了不必要的线程切换开销
3)非阻塞IO

Redis用法

redis中文网:http://www.redis.net.cn/
php-redis文档:http://www.cnblogs.com/weafer/archive/2011/09/21/2184059.html

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
31
32
127.0.0.1:6379> set lkj likangjun
OK
127.0.0.1:6379> get lkj
"likangjun"
#返回 key 中字符串值的子字符
127.0.0.1:6379> getrange lkj 0 1
"li"
127.0.0.1:6379> set redis phpredis
OK
127.0.0.1:6379> mget lkj redis
1) "likangjun"
2) "phpredis"
#设定过期时间
127.0.0.1:6379> setex xb 5 xbzh
OK
127.0.0.1:6379> ttl xb
(integer) 3
#只有在 key 不存在时设置 key 的值
127.0.0.1:6379> setnx lkj abcde
(integer) 0
127.0.0.1:6379> setnx kxds kaixindaishu
(integer) 1
127.0.0.1:6379> append lkj again
(integer) 14
127.0.0.1:6379> get lkj
"likangjunagain"
127.0.0.1:6379> incr number
(integer) 1
127.0.0.1:6379> incr number
(integer) 2
127.0.0.1:6379> incrby number 100
(integer) 102

应用场景

1.数据缓存
2.原子计数器(原子性,可用于抢购,秒杀 )

hash(哈希)

Redis 中每个 hash 可以存储 (2^32-1) 键值对(40多亿)

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
127.0.0.1:6379> hmset user_info_7 id 7 name likangjun mobile 18672792276
OK
127.0.0.1:6379> hget user_info_7 id
"7"
127.0.0.1:6379> HGETALL user_info_7
1) "id"
2) "7"
3) "name"
4) "likangjun"
5) "mobile"
6) "18672792276"
127.0.0.1:6379> HKEYS user_info_7
1) "id"
2) "name"
3) "mobile"
127.0.0.1:6379> HVALS user_info_7
1) "7"
2) "likangjun"
3) "18672792276"
127.0.0.1:6379> HEXISTS user_info_7 times
(integer) 0
127.0.0.1:6379> HSETNX user_info_7 times 1
(integer) 1
127.0.0.1:6379> HINCRBY user_info_7 times 1
(integer) 2

应用场景

1.用户信息

list(列表)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 (2^32-1) 个元素(每个列表超过40亿个元素)。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
127.0.0.1:6379> rpush list1 a
(integer) 1
127.0.0.1:6379> rpush list1 b
(integer) 2
127.0.0.1:6379> rpush list1 c
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> rpop list1
"c"
127.0.0.1:6379> lpop list1
"a"
127.0.0.1:6379> lrange list1 0 -1
1) "b"
127.0.0.1:6379> rpush list2 a
(integer) 1
127.0.0.1:6379> rpush list2 b
(integer) 2
127.0.0.1:6379> rpush list2 c
(integer) 3
127.0.0.1:6379> rpoplpush list2 list1
"c"
127.0.0.1:6379> lrange list1 0 -1
1) "c"
2) "b"
127.0.0.1:6379> lrange list2 0 -1
1) "a"
2) "b"
127.0.0.1:6379> linsert list2 before b d
(integer) 3
127.0.0.1:6379> lrange list2 0 -1
1) "a"
2) "d"
3) "b"
127.0.0.1:6379> lpush list3 a
(integer) 1
127.0.0.1:6379> lpush list3 b
(integer) 2
127.0.0.1:6379> lpush list3 c
(integer) 3
127.0.0.1:6379> lrange list3 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> lpop list3
"c"
127.0.0.1:6379> rpop list3
"a"

应用场景

1.消息队列

set(集合)

Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

集合中最大的成员数为(2^32-1) (每个集合可存储40多亿个成员)。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
127.0.0.1:6379> sadd lkj-code nginx
(integer) 1
127.0.0.1:6379> sadd lkj-code php
(integer) 1
127.0.0.1:6379> sadd lkj-code mysql
(integer) 1
127.0.0.1:6379> sadd lkj-code html
(integer) 1
127.0.0.1:6379> sadd abc-code css
(integer) 1
127.0.0.1:6379> sadd abc-code html
(integer) 1
127.0.0.1:6379> SMEMBERS lkj-code
1) "mysql"
2) "php"
3) "html"
4) "nginx"
127.0.0.1:6379> sinter lkj-code abc-code
1) "html"
127.0.0.1:6379> sinterstore common-code lkj-code abc-code
(integer) 1
127.0.0.1:6379> SMEMBERS common-code
1) "html"
127.0.0.1:6379> sdiff lkj-code common-code
1) "php"
2) "mysql"
3) "nginx"
127.0.0.1:6379> sunion lkj-code abc-code
1) "html"
2) "nginx"
3) "css"
4) "mysql"
5) "php"
127.0.0.1:6379> sadd lkj-code html
(integer) 0
127.0.0.1:6379> SRANDMEMBER lkj-code
"nginx"
127.0.0.1:6379> SPOP lkj-code
"html"
127.0.0.1:6379> SMEMBERS lkj-code
1) "nginx"
2) "php"
3) "mysql"
127.0.0.1:6379> srem lkj-code mysql
(integer) 1

应用场景

1.共同好友
2.利用唯一性,可以统计访问网站的所有独立 IP
3.好友推荐的时候,根据 tag 求交集,大于多少个就可以推荐

zset(有序集合)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 (2^32-1) (每个集合可存储40多亿个成员)。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
127.0.0.1:6379> zadd rank1 10 php
(integer) 1
127.0.0.1:6379> zadd rank1 2 ios
(integer) 1
127.0.0.1:6379> zadd rank1 8 java
(integer) 1
127.0.0.1:6379> ZINCRBY rank1 1 ios
"3"
127.0.0.1:6379> ZRANGE rank1 0 -1 withscores
1) "ios"
2) "3"
3) "java"
4) "8"
5) "php"
6) "10"
127.0.0.1:6379> zrevrange rank1 0 -1 withscores
1) "php"
2) "10"
3) "java"
4) "8"
5) "ios"
6) "3"
127.0.0.1:6379> zrevrange rank1 0 -1
1) "php"
2) "java"
3) "ios"
127.0.0.1:6379> zscore rank1 php
"10"
127.0.0.1:6379> zrevrank rank1 php
(integer) 0
127.0.0.1:6379> zadd rank2 99 php
(integer) 1
127.0.0.1:6379> zadd rank2 10 ios
(integer) 1
127.0.0.1:6379> zadd rank2 50 java
(integer) 1
127.0.0.1:6379> zrevrange rank1 0 -1 withscores
1) "php"
2) "10"
3) "java"
4) "8"
5) "ios"
6) "3"
127.0.0.1:6379> zrevrange rank2 0 -1 withscores
1) "php"
2) "99"
3) "java"
4) "50"
5) "ios"
6) "10"
#计算给定的一个或多个有序集的并集
127.0.0.1:6379> ZUNIONSTORE rank 2 rank1 rank2 WEIGHTS 2 1
(integer) 3
127.0.0.1:6379> zrevrange rank 0 -1 withscores
1) "php"
2) "119"
3) "java"
4) "66"
5) "ios"
6) "16"

应用场景

1.带有权重的元素,比如投票排行榜

事务

Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
一个事务从开始到执行会经历以下三个阶段:

开始事务。
命令入队。
执行事务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set book likangjun
QUEUED
127.0.0.1:6379> get book
QUEUED
127.0.0.1:6379> ZINCRBY rank 1 php
QUEUED
127.0.0.1:6379> zrevrange rank1 0 -1 withscores
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) "likangjun"
3) "120"
4) 1) "php"
2) "11"
3) "java"
4) "8"
5) "ios"
6) "3"
7) "html"
8) "1"

发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

HyperLogLog

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

1
2
3
4
5
6
7
8
127.0.0.1:6379> PFADD code "redis"
1) (integer) 1
127.0.0.1:6379> PFADD code "php"
1) (integer) 1
127.0.0.1:6379> PFADD code "java"
1) (integer) 1
127.0.0.1:6379> PFCOUNT w3ckey
(integer) 3

主从同步

主从同步可以防止主机坏掉导致网站不能正常运作,这种方法即把从机设置成主机即可

Redis主从同步设置很简单,设置到Slave服务器后,Slave自动和Master建立连接

第一阶段
1.Slave服务器主动连接到Master服务器
2.Slave服务器发送SYCN命令到Master服务器请求同步
3.Master服务器备份数据到rdb文件
4.Master服务器把rdb文件传输到Slave服务器
5.Slave服务器清空数据库数据,把rdb文件数据导入到数据库
第二阶段
Master服务器把用户所有更改数据的操作,通过命令转发给所有的Slave服务器,Slave服务器只需要执行Master服务器发送过来的命令就可以达到同步的效果

总结

Redis远远不只是缓存这么简单,缓存只是其中的一个小部分
Redis更多的在于其丰富的数据结构和灵活性,可以使很多功能实现起来更加简单有效,极大的提高效率和性能

Redis数据库在以下的这几种情况下比较适用:
1、数据模型比较简单;
2、需要灵活性更强的IT系统;
3、对数据库性能要求较高;
4、不需要高度的数据一致性;
5、对于给定key,比较容易映射复杂值的环境。

注:O(1)指的是常数时间运行,比如操作对象为一个链表,对其有一个算法,O(1)时间指的是,无论链表大或者小,所耗费的时间都是一样的;
O(n)指的是某算法的运行时间与输入规模成正比,即,若输入规模为T,花费时间为N,则输入规模2T时花费时间为2N

Copy-on-Write简称COW,基本的原理是:
当我要修改数据块A的内容的时候,我先把A读出来,写到B块里,如果写的过程掉电了,原来A的内容还在,如果是还写到原来的位置上,那么写入的数据究竟写了多少就不确定了,会不会破坏原来的数据也不好说。