Redis & MemCache

Redis Api的使用和理解

2020-01-07  本文已影响0人  香沙小熊

1.通用命令

1.1通用命令
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> set java best
OK
123.57.251.156:6379> set kpioneer man
OK
123.57.251.156:6379> keys *
1) "java"
2) "hello"
3) "kpioneer"
123.57.251.156:6379> mset hello world hehe haha java best javaer bad
OK
123.57.251.156:6379> keys he*
1) "hello"
2) "hehe"
123.57.251.156:6379> keys he[h-l]*
1) "hello"
2) "hehe"
123.57.251.156:6379> keys jav?
1) "java"
注意:keys命令一般不在生产环境中使用
生产环境key较多,keys 的时间复杂度为 O(n),严格禁止在生产环境中使用

运维人员或开发进行keys *操作,该操作比较耗时,又因为redis是单线程的,所以redis被锁住;
此时QPS比较高,又来了几万个对redis的读写请求,因为redis被锁住,所以全部Hang在那;
因为太多线程Hang在那,CPU严重飙升,造成redis所在的服务器宕机;
所有的线程在redis那取不到数据,一瞬间全去数据库取数据,数据库就宕机了。

123.57.251.156:6379> FLUSHALL 
OK
123.57.251.156:6379> dbsize
(integer) 0
123.57.251.156:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
123.57.251.156:6379> dbsize
(integer) 4
123.57.251.156:6379> sadd myset a b c d e
(integer) 5
123.57.251.156:6379> dbsize
(integer) 5
123.57.251.156:6379> set a b
OK
123.57.251.156:6379> exists a
(integer) 1
123.57.251.156:6379> del a
(integer) 1
123.57.251.156:6379> exists a
(integer) 0
123.57.251.156:6379> set a b
OK
123.57.251.156:6379> get a
"b"
123.57.251.156:6379> del a
(integer) 1
123.57.251.156:6379> get a
(nil)
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> expire hello 18
(integer) 1
123.57.251.156:6379> ttl hello
(integer) 9
123.57.251.156:6379> get hello
(nil)
123.57.251.156:6379> expire hello 18
(integer) 0
123.57.251.156:6379> ttl hello
(integer) -2
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> expire hello 18
(integer) 1
123.57.251.156:6379> ttl hello
(integer) 14
123.57.251.156:6379> get hello
"world"
123.57.251.156:6379> ttl hello
(integer) 2
123.57.251.156:6379> get hello
(nil)
123.57.251.156:6379> ttl hello
(integer) -2
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> expire hello 18
(integer) 1
123.57.251.156:6379> ttl hello
(integer) 14
123.57.251.156:6379> persist hello
(integer) 1
123.57.251.156:6379> ttl hello
(integer) -1
123.57.251.156:6379> get hello
"world"
123.57.251.156:6379> set a b
OK
123.57.251.156:6379> type a
string
123.57.251.156:6379> sadd myset 1 2 3
(integer) 3
123.57.251.156:6379> type myset
set

返回值有: string、 hash、 lis、t set、 zset、 none

1.2时间复杂度
命令 时间复杂度
keys O(n)
dbsize O(1)
del O(1)
exists O(1)
expire、ttl、persist O(1)
type O(1)
1.3数据结构和内部编码
image.png
1.4单线程架构
1、Redis的单线程为什么这么快?

1.完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);主要原因
2.数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3.采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4.使用多路I/O复用模型,非阻塞I/O;
5.Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

2、为什么不采用多进程或多线程处理?

1.多线程处理可能涉及到锁
2.多线程处理会涉及到线程切换而消耗CPU

3、单线程处理的缺点?

1.耗时的命令会导致并发的下降,不只是读并发,写并发也会下降
2.无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来完善

4、Redis不存在线程安全问题?

Redis采用了线程封闭的方式,把任务封闭在一个线程,自然避免了线程安全问题,不过对于需要依赖多个redis操作(即:多个Redis操作命令)的复合操作来说,依然需要锁,而且有可能是分布式锁。

注意事项:
1.一次只运行一条命令
2.拒绝长(慢)命令

keys, flushall, flushdb, slow lua script ,mutil/exec, operate big value(collection)

3.有的地方不是单线程

fysnc file descriptor
close file descriptor

2.字符串类型(String)

字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用其存储用户的邮箱、JSON化的对象甚至是一张图片。一个字符串类型键允许存储的数据的最大容量是512MB。

注释:在Redis 3.0版本中可能会放宽这一限制,但无论如何,考虑到Redis的数据是使用内存存储的, 512MB的限制已经非常宽松了。

字符串类型是其他4种数据类型的基础,其他数据类型和字符串类型的差别从某种角度来说只是组织字符串的形式不同。例如,列表类型是以列表的形式组织字符串,而集合类型是以集合的形式组织字符串。

如果只使用redis中的字符串类型,且不使用redis的持久化功能,那么,redis就和memcache非常非常的像了。这说明strings类型是一个很基础的数据类型,也是任何存储系统都必备的数据类型。

基本操作
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> get hello
"world"
123.57.251.156:6379> del hello
(integer) 1
123.57.251.156:6379> get hello
(nil)
123.57.251.156:6379> exists java
(integer) 0
123.57.251.156:6379> set java best
OK
123.57.251.156:6379> setnx java bad
(integer) 0
123.57.251.156:6379> set java good xx
OK
123.57.251.156:6379> get java
"good"
123.57.251.156:6379> exists kpioneer
(integer) 0
123.57.251.156:6379> setnx kpioneer goodMan
(integer) 1
123.57.251.156:6379> exists php
(integer) 0
123.57.251.156:6379> set php hehe xx
(nil)
计数操作
123.57.251.156:6379> get counter
(nil)
123.57.251.156:6379> incr counter
(integer) 1
123.57.251.156:6379> get counter
"1"
123.57.251.156:6379> incrby counter 99
(integer) 100
123.57.251.156:6379> decr counter
(integer) 99
123.57.251.156:6379> get counter
"99"
123.57.251.156:6379> decrby counter 99
(integer) 0
123.57.251.156:6379> get counter
"0"
批量操作
123.57.251.156:6379> mset hello world java best php bad
OK
123.57.251.156:6379> mset hello world java best C good
OK
123.57.251.156:6379> mget hello java C
1) "world"
2) "best"
3) "good"
其他操作
123.57.251.156:6379> set hello world
OK
123.57.251.156:6379> getset hello java
"world"
123.57.251.156:6379> get hello
"java"
123.57.251.156:6379> append hello ",C++"
(integer) 8
123.57.251.156:6379> get hello
"java,C++"
123.57.251.156:6379> strlen hello
(integer) 8
123.57.251.156:6379> incr counter
(integer) 1
123.57.251.156:6379> incrbyfloat counter 1.2
"2.2"
123.57.251.156:6379> get counter
"2.2"
123.57.251.156:6379> set hello javabest
OK
123.57.251.156:6379> getrange hello 0 2
"jav"
123.57.251.156:6379> setrange hello 4 m
(integer) 8
123.57.251.156:6379> get hello
"javamest"
命令 含义 复杂度
get key 获取key对应value O(1)
set key value 不管key是否存在,都设置key-value O(1)
setnx key value key不存在时,才设置key-value O(1)
set key value xx key存在时,才设置key-value O(1)
del key 删除key-value O(1)
incr key
decr key
incrby key k
decrby key k
计数 O(1)
mset key1 value1 key2 value2 ... 批量设置key-value O(n)
mget key1 key2 ... 批量获取key,原子操作 O(n)

3.哈希类型(Hash)

基本操作
123.57.251.156:6379> hset user:1:info age 23
(integer) 1
123.57.251.156:6379> hget user:1:info age
"23"
123.57.251.156:6379> hset user:1:info name kpioneer
(integer) 1
123.57.251.156:6379> hgetall user:1:info
1) "age"
2) "23"
3) "name"
4) "kpioneer"
123.57.251.156:6379> hdel user:1:info age
(integer) 1
123.57.251.156:6379> hgetall user:1:info 
1) "name"
2) "kpioneer"
123.57.251.156:6379> hgetall  user:1:info
1) "name"
2) "kpioneer"
3) "age"
4) "23"
123.57.251.156:6379> hexists user:1:info name
(integer) 1
123.57.251.156:6379> hlen user:1:info 
(integer) 2
批量操作

hmget key field1 field2 ... :批量获取hash key的一批field对应值
hmset key field1 value1 field2 value2 ... :批量设置hash key的一批field value

123.57.251.156:6379> hmset user:2:info age 28 name Jack page 48
OK
123.57.251.156:6379> hlen user:2:info 
(integer) 3
123.57.251.156:6379> hmget user:2:info age name page
1) "28"
2) "Jack"
3) "48"
其他操作

hgetall key :返回hash key对应所有的field和value
hvals key :返回hash key对应所有的field的value
hkeys key :返回hash key对应所有的field

123.57.251.156:6379> hgetall user:2:info
1) "age"
2) "28"
3) "name"
4) "Jack"
5) "page"
6) "48"
123.57.251.156:6379> hvals user:2:info
1) "28"
2) "Jack"
3) "48"
123.57.251.156:6379> hkeys user:2:info
1) "age"
2) "name"
3) "page"
其他操作

时间复杂度

命令 复杂度
hget
hset
hdel
O(1)
hexists
hlen
O(1)
hsetnx
hincrby
hincrbyfloat
O(1)
hgetall
hvals
hkeys
O(n)
hmget
hmset
O(n)

4.列表类型(List)

123.57.251.156:6379> rpush list1 1 2 3 4
4
123.57.251.156:6379> lrange list1 0 -1
1
2
3
4
123.57.251.156:6379> lpush list1 7 8 9
7
123.57.251.156:6379> lrange list1 0 -1
9
8
7
1
2
3
4
123.57.251.156:6379> linsert list1 after 4 a
8
123.57.251.156:6379> lrange list1 0 -1
9
8
7
1
2
3
4
a
123.57.251.156:6379> linsert list1 after 4 b
9
123.57.251.156:6379> lrange list1 0 -1
9
8
7
1
2
3
4
b
a
123.57.251.156:6379> lpop list1
9
123.57.251.156:6379> rpop list1
a
123.57.251.156:6379> lrange list1 0 -1
8
7
1
2
3
4
123.57.251.156:6379> rpush list a c a c b f
6
123.57.251.156:6379> lrem list -1 c
1
123.57.251.156:6379> lrange list 0 -1
a
c
a
b
f
123.57.251.156:6379> lrem list 0 a
2
123.57.251.156:6379> lrange list 0 -1
c
b
f
123.57.251.156:6379> rpush list a b c d e f
6
123.57.251.156:6379> ltrim list 1 4
OK
123.57.251.156:6379> lrange list 0 -1
b
c
d
e
123.57.251.156:6379> rpush list a b c d e 
5
123.57.251.156:6379> lindex list 1
b
123.57.251.156:6379> lindex list -1
e
123.57.251.156:6379> rpush list a b c d e 
5
123.57.251.156:6379> lset list 2 java
OK
123.57.251.156:6379> lrange list 0 -1
a
b
java
d
使用列表api组合实现其它数据结构
  1. lpush + lpop = Stack
  2. lpush + rpop = Queue
  3. lpush + ltrim = Capped Collection(有界集合)
  4. lpush + brpop = Message Queue

5.集合类型(Set)

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

123.57.251.156:6379> sadd myset "hello" "world" 
2
123.57.251.156:6379> sadd myset "hello"
0
123.57.251.156:6379> smembers myset
hello
world
123.57.251.156:6379> srem myset "hello"
1
123.57.251.156:6379> smembers myset
world
123.57.251.156:6379> 
123.57.251.156:6379>  sadd myset 1 2 3 4 5 6 7 8
8
123.57.251.156:6379> scard myset
8
123.57.251.156:6379> sismember myset 2
1
123.57.251.156:6379> sismember myset 100
0
123.57.251.156:6379> srandmember myset 3
2
7
5
123.57.251.156:6379> spop myset 2
7
2
123.57.251.156:6379> smembers myset
1
3
4
5
6
8
123.57.251.156:6379> sadd myset1  1 3 5 7 9 10
6
123.57.251.156:6379> sadd myset2  1 2 4 6 8 10
6
123.57.251.156:6379> sdiff myset1 myset2
3
5
7
9
123.57.251.156:6379> sinter myset1 myset2 
1
10
123.57.251.156:6379> sunion myset1 myset2
1
2
3
4
5
6
7
8
9
10
123.57.251.156:6379> sunionstore dest myset1 myset2
10
123.57.251.156:6379> smembers dest
1
2
3
4
5
6
7
8
9
10
  1. "0"
    1. "hello"
    2. "h1"
集合类型(Set)实际运用

SADD = Tagging 给用户加标签
SPOP/SANDMEMBER = Random item
SADD + SINTER = Social Graph 社交图谱 如微博共同兴趣爱好

6.有序集合类型(Sorted Set)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。

数据类型对比
集合 有序集合
无重复元素 无重复元素
无序 有序
element element + score
列表 有序集合
可以有重复元素 无重复元素
有序 有序
element element + score
阿里云redis:0>ZADD myset 1 "hello"
1

阿里云redis:0>ZADD myset 1 "foo"
1

阿里云redis:0>ZADD myset 2 "world" 3 "bar"
2

阿里云redis:0>ZRANGE my阿里云redis:0>set 0 -1 WITHSCORES
1) foo
2) 1
3) hello
4) 1
5) world
6) 2
7) bar
8) 3
方法 时间复杂度
zadd key score element o(logN)
zscore key element o(1)
zincrby key increScore element o(1)
zcard key o(1)
zrank key element O(log(N))
zrevrank key member O(log(N))
zrange key start end [withscores] o(log(n)+m)
zrevrange key start stop [withscores] o(log(n)+m)
zrangebyscore key minScore maxScore [withscores] o(log(n)+m)
zrevrangebyscore key maxScore minScore [withscores] o(log(n)+m)
zcount key minScore maxScore o(log(n)+m)
zrem key element o(1)
zremrangebyrank key start end o(log(n)+m)
zremrangebyscore key minScore maxScore o(log(n)+m)
特别感谢:

Redis 教程
一站式学习Redis 从入门到高可用分布式实践

上一篇 下一篇

猜你喜欢

热点阅读