Redis

Nosql

not only sql 泛指非关系型数据库

Nosql的四大分类

KV键值对

redis

文档型数据库(boson格式)

  • MongoDB
    MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!

    MongoDB是一个介于关系型数据库和非关系型数据中间的产品

  • conchDB

列存储数据库

HBase

分布式文件系统

图关系型数据库

NEO4J

redis

用途

1、内存存储、持久化,内存中是断电即失、所以说持久化很重要( rdb、aof )

2、效率高,可以用于高速缓存

3、发布订阅系统

4、地图信息分析

5、计时器、计数器(浏览量)

安装

Windows

下包 安装 运行

Linux

1、官网下载压缩包

2、解压安装包

tar -zxvf redis-6.0.10.tar.gz

image-20210116132318923

基本文件结构如下

image-20210116132421492

安装gcc:yum install gcc-c++

在当前路径下make

完成后再make检查一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@Shyee redis-6.0.10]# make
cd src && make all
make[1]: Entering directory '/usr/local/redis-6.0.10/src'
CC Makefile.dep

Hint: It's a good idea to run 'make test' ;)

make[1]: Leaving directory '/usr/local/redis-6.0.10/src'
[root@Shyee redis-6.0.10]# make install
cd src && make install
make[1]: Entering directory '/usr/local/redis-6.0.10/src'

Hint: It's a good idea to run 'make test' ;)

INSTALL install
INSTALL install
INSTALL install
INSTALL install
INSTALL install
make[1]: Leaving directory '/usr/local/redis-6.0.10/src'
[root@Shyee redis-6.0.10]#

redis的默认安装路径

user/local/bin

简单的配置

将redis文件夹下的redis.configcopy到local/bin中的rconfig文件夹(自行创建)中

设置后台启动

修改config文件中的 daemonize no,改为yes

启动

user/local/bin下,redis-server rconfig/redis.conf

1
2
3
4
5
6
7
8
9
10
11
[root@Shyee bin]# redis-server rconfig/redis.conf 
[root@Shyee bin]# redis-cli -p 6379/6235
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name Shyee
OK
127.0.0.1:6379> get name
"Shyee"
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379>

如果启动不成功,则在刚刚make之后在去src中make install 一下

关闭

1
2
127.0.0.1:6379> SHUTDOWN
not connected> exit

查看redis服务是否启动

1
2
3
4
[root@Shyee ~]# ps -ef|grep redis
root 216409 1 0 15:49 ? 00:00:00 redis-server 127.0.0.1:6379
root 216414 216382 0 15:49 pts/1 00:00:00 redis-cli -p 6379
root 216443 216421 0 15:50 pts/2 00:00:00 grep --color=auto redis

benchmark 测试

redis 性能测试工具可选参数如下所示:

序号 选项 描述 默认值
1 -h 指定服务器主机名 127.0.0.1
2 -p 指定服务器端口 6379
3 -s 指定服务器 socket
4 -c 指定并发连接数 50
5 -n 指定请求数 10000
6 -d 以字节的形式指定 SET/GET 值的数据大小 2
7 -k 1=keep alive 0=reconnect 1
8 -r SET/GET/INCR 使用随机 key, SADD 使用随机值
9 -P 通过管道传输 请求 1
10 -q 强制退出 redis。仅显示 query/sec 值
11 –csv 以 CSV 格式输出
12 -l 生成循环,永久执行测试
13 -t 仅运行以逗号分隔的测试命令列表。
14 -I Idle 模式。仅打开 N 个 idle 连接并等待。

测试100个并发链接 100000个请求

1
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

部分分析

image-20210221163231907

基础知识

redis 默认有16个数据库

1
2
3
4
127.0.0.1:6379> SELECT 3 --选择第三个数据库
OK
127.0.0.1:6379[3]> DBSIZE --查看数据库大小
(integer) 0

set一个kv后

1
2
3
4
127.0.0.1:6379[3]> SET name Shyee
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 1

查看所有的key

1
2
127.0.0.1:6379[3]> KEYS *
1) "name"

清空当前库

1
2
3
4
127.0.0.1:6379[3]> FLUSHDB
OK
127.0.0.1:6379[3]> KEYS *
(empty array)

清空全部数据库

1
flushall

redis 是单线程的

基于内存操作的 redis 的瓶颈是机器内存及网络带宽。

redis是c语言写的

redis 将所有的数据放在内存中的,所以使用单线程操作时效率最高的。对于内存,没有上下文切换,效率是最高的,多次读写实在一个cpu上的。

redis 的基本数据类型

基本命令

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
127.0.0.1:6379> set name Shyee 
OK
127.0.0.1:6379> SET age 12
OK
127.0.0.1:6379> EXISTS name #查看是否存在关键字
(integer) 1
127.0.0.1:6379> EXPIRE name 10 #设置过期时间
(integer) 1
127.0.0.1:6379> ttl name #查看当前k的剩余时间
(integer) 6
127.0.0.1:6379> ttl name
(integer) 4
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 0
127.0.0.1:6379> ttl name #-2代表已经过期
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> move name 1 #将该关键字转移到1号库
(integer) 1
127.0.0.1:6379> type name #查看当前k的类型
string
127.0.0.1:6379> TYPE age
string

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
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
127.0.0.1:6379> APPEND name " Hello" #字符串的拼接,如果当前key不存在,就相当于set
(integer) 11
127.0.0.1:6379> GET name
"Shyee Hello"
127.0.0.1:6379> STRLEN name #查看当前字符串的长度
(integer) 11

#设计自增减
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> GET views
"0"
127.0.0.1:6379> INCR views #自增
(integer) 1
127.0.0.1:6379> GET views
"1"
127.0.0.1:6379> DECR views #自减
(integer) 0
127.0.0.1:6379> GET views
"0"
127.0.0.1:6379> INCRBY views 10 #设置步长的增减
(integer) 10
127.0.0.1:6379> DECRBY views 8
(integer) 2
127.0.0.1:6379> GET views
"2"
#截取
127.0.0.1:6379> get name
"Shyee Hello"
127.0.0.1:6379> GETRANGE name 2 5 #截取2~5
"yee "
127.0.0.1:6379> GETRANGE name 0 -1 #查看全部
"Shyee Hello"
#setrange
127.0.0.1:6379> set k1 abcdefg
OK
127.0.0.1:6379> SETRANGE k1 2 ggg #修改第二个以后的三个字符
(integer) 7
127.0.0.1:6379> GET k1
"abgggfg"

#setex(set with expire) 设置过期时间
#setnx(set if not exist) 不存在设置
127.0.0.1:6379> SETEX k2 5 "只有5秒"
OK
127.0.0.1:6379> ttl k2
(integer) 3
127.0.0.1:6379> SETNX k2 "ifexist"
(integer) 1
127.0.0.1:6379> SETNX k2 "ife"
(integer) 0 #SET 失败,因为存在了
#一下设置很多值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
127.0.0.1:6379> KEYS *
1) "k4"
2) "k3"
3) "k2"
4) "k1"
#一下拿很多值
127.0.0.1:6379> MGET k1 k2 k3 k4
1) "v1"
2) "v2"
3) "v3"
4) "v4"
127.0.0.1:6379> MSETNX k1 vv1 k5 v5
(integer) 0 #mset是原子性的操作,要么一起成功,要么一起失败
#对象的批量set
127.0.0.1:6379> MSET user:1:name Shyee user:1:age 13 user:3:name XY
OK
127.0.0.1:6379> MGET user:3:name user:1:name user:1:age
1) "XY"
2) "Shyee"
3) "13"
#取值并设置值
127.0.0.1:6379> getset name XY #如果不存在 则返回nil
(nil)
127.0.0.1:6379> GETSET name Shyee
"XY"
127.0.0.1:6379> GET name
"Shyee"

List

redis中的list是双向链表

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
61
62
63
64
65
66
67
127.0.0.1:6379> lpush list1 abcd
(integer) 1
127.0.0.1:6379> rpush list1 aaa
(integer) 2
127.0.0.1:6379> get list1
(error) WRONGTYPE Operation against a key holding the wrong kind of value
#不能直接使用get来取list中的值
127.0.0.1:6379> lrange list1 0 5
1) "abcd"
2) "aaa"
127.0.0.1:6379> lpush list1 a b c d e
(integer) 7
127.0.0.1:6379> lrange list1 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
6) "abcd"
7) "aaa"
127.0.0.1:6379> rrange 0 -1
(error) ERR unknown command `rrange`, with args beginning with: `0`, `-1`,
127.0.0.1:6379> rpop list1 2
1) "aaa"
2) "abcd"
127.0.0.1:6379> lrange list1 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379> rpush o p q r s t
(integer) 5
127.0.0.1:6379> lrange list1 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379> rpush list1 o p q r s t
(integer) 11
127.0.0.1:6379> lrange list1 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
6) "o"
7) "p"
8) "q"
9) "r"
10) "s"
11) "t"
127.0.0.1:6379> lpop list1 5
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "p"
3) "q"
4) "r"
5) "s"
6) "t"
#pop到最后没值了,键就不存在了

rpoplpush :从key1 的右边pop一个值放到key2的左边

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> lrange list1 0 -1
1) "o"
2) "p"
3) "q"
4) "r"
5) "s"
6) "t"
127.0.0.1:6379> lpush list2 a
(integer) 1
127.0.0.1:6379> lrange list2 0 -1
1) "a"
127.0.0.1:6379> rpush list2 b c d
(integer) 4
127.0.0.1:6379> lrange list2 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379> rpoplpush list1 list2
"t"
127.0.0.1:6379> lrange list2 0 -1
1) "t"
2) "a"
3) "b"
4) "c"
5) "d"
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "p"
3) "q"
4) "r"
5) "s"

取指定下标元素

1
2
127.0.0.1:6379> lindex list1 4
"s"

获取list长度

1
2
127.0.0.1:6379> llen list1
(integer) 5

linsert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> linsert list1 before "p" ddd
(integer) 6
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "ddd"
3) "p"
4) "q"
5) "r"
6) "s"
127.0.0.1:6379> linsert list1 after "p" aaa
(integer) 7
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "ddd"
3) "p"
4) "aaa"
5) "q"
6) "r"
7) "s"

lrem key n value

从左边删除n个value

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> lrem list1 2 "aaa"
(integer) 1
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "ddd"
3) "p"
4) "q"
5) "r"
6) "s"

lset 替换

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> lset list1 4 qwerewr
OK
127.0.0.1:6379> lrange list1 0 -1
1) "o"
2) "ddd"
3) "p"
4) "q"
5) "qwerewr"
6) "s"

list 的数据结构

list的数据结构是quicklist

当列表中元素较少的时候会使用一块连续的存储空间,这个结构是ziplist,压缩列表

当数据量比较多的时候才会使用quicklist ,是一种双向链表

且redis 将ziplist组合起来组成quicklist