一、Redis介绍
Redis 是一个高性能的key-value数据格式的内存缓存,NoSQL数据库。NOSQL:not only sql,泛指非关系型数据库。关系型数据库: (mysql, oracle, sql server, sqlite)
1. 数据存放在表中,表之间有关系。 2. 通用的SQL操作语言。 3. 大部分支持事务。
非关系型数据库[ redis,hadoop,mangoDB]:
1. 没有数据表的概念,不同的nosql数据库存放数据位置不同。 2. nosql数据库没有通用的操作语言。 3. 基本不支持事务。 redis支持简单事务
redis是业界主流的key-value nosql 数据库之一。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。redis是一款基于CS架构的数据库,所以redis有客户端,也有服务端。其中,客户端可以使用python等编程语言,也可以终端命令行工具
Redis优点
- 异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。
- 支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- Redis是一个多功能实用工具,可以在很多如,消息传递队列中使用(Redis原生支持发布/订阅)
- 单线程特性,秒杀系统,基于redis是单线程特征,防止出现数据库“爆破”
redis的典型应用
(一)性能
Redis 中缓存热点数据,能够保护数据库,提高查询效率。如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
(二)并发
还是如上图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。
安装Redis环境
要在 Ubuntu 上安装 Redis,打开终端,然后输入以下命令:
1 2 |
|
这将在您的计算机上安装Redis
启动 Redis
1 |
|
查看 redis 是否还在运行
1 |
|
这将打开一个 Redis 提示符,如下图所示:
1 |
|
在上面的提示信息中:127.0.0.1 是本机的IP地址,6379是 Redis 服务器运行的端口。现在输入 PING 命令,如下图所示:
1 2 |
|
这说明现在你已经成功地在计算机上安装了 Redis。
二、Python操作Redis
redis-py 的API的使用可以分类为:
- 连接方式
- 操作
- String 操作
- Hash 操作
- List 操作
- Set 操作
- Sort Set 操作
- 管道
- 发布订阅
2.1、连接方式
1、操作模式
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
1 2 3 4 5 |
|
2、连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
1 2 3 4 5 6 |
|
2.2、操作
1. string类型: 字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,也就是byte类型 最大容量是512M。 2. hash类型: hash用于存储对象,对象的结构为属性、值,值的类型为string。 key:{ 域:值[这里的值只能是字符串], 域:值, 域:值, 域:值, ... } 3. list类型: 列表的元素类型为string。 key:[ 值1,值2,值3..... ] 4. set类型: 无序集合,元素为string类型,元素唯一不重复,没有修改操作。 {值1,值4,值3,值5} 5. zset类型: 有序集合,元素为string类型,元素唯一不重复,没有修改操作。
2.2.1、String 操作
redis中的String在在内存中按照一个name对应一个value来存储。如图:
set(name, value, ex=None, px=None, nx=False, xx=False)
在Redis中设置值,默认,不存在则创建,存在则修改 参数: ex,过期时间(秒) px,过期时间(毫秒) nx,如果设置为True,则只有name不存在时,当前set操作才执行 xx,如果设置为True,则只有name存在时,岗前set操作才执行
setnx(name, value)
1 |
|
setex(name, value, time)
1 2 3 |
|
psetex(name, time_ms, value)
1 2 3 |
|
mset(*args, **kwargs)
1 2 3 4 5 |
|
get(name)
1 |
|
mget(keys, *args)
1 2 3 4 5 |
|
getset(name, value)
1 |
|
getrange(key, start, end)
1 2 3 4 5 |
|
setrange(name, offset, value)
1 2 3 4 |
|
strlen(name)
1 |
|
incr(self, name, amount=1)
1 2 3 4 5 6 7 |
|
incrbyfloat(self, name, amount=1.0)
1 2 3 4 5 |
|
decr(self, name, amount=1)
1 2 3 4 5 |
|
append(key, value)
1 2 3 4 5 |
|
2.2.2、Hash 操作
hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:
hset(name, key, value)
1 2 3 4 5 6 7 8 9 |
|
hmset(name, mapping)
1 2 3 4 5 6 7 8 |
|
hget(name,key)
1 |
|
hmget(name, keys, *args)
1 2 3 4 5 6 7 8 9 10 11 |
|
hgetall(name)
1 |
|
hlen(name)
1 |
|
hkeys(name)
1 |
|
hvals(name)
1 |
|
hexists(name, key)
1 |
|
hdel(name,*keys)
1 |
|
hincrby(name, key, amount=1)
1 2 3 4 5 |
|
hincrbyfloat(name, key, amount=1.0)
1 2 3 4 5 6 7 8 |
|
hscan_iter(name, match=None, count=None)
1 2 3 4 5 6 7 8 9 |
|
2.2.3、List 操作
List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:
lpush(name,values)
1 2 3 4 5 6 7 8 |
|
lpushx(name,value)
1 2 3 4 |
|
llen(name)
1 |
|
linsert(name, where, refvalue, value))
1 2 3 4 5 6 7 |
|
r.lset(name, index, value)
1 2 3 4 5 6 |
|
r.lrem(name, value, num)
1 2 3 4 5 6 7 8 |
|
lpop(name)
1 2 3 4 |
|
lindex(name, index)
1 |
|
lrange(name, start, end)
1 2 3 4 5 |
|
ltrim(name, start, end)
1 2 3 4 5 |
|
2.2.4、Set 操作
Set操作,Set集合就是不允许重复的列表
sadd(name,values)
1 |
|
scard(name)
1 |
|
sdiff(keys, *args)
1 |
|
sinter(keys, *args)
1 |
|
sismember(name, value)
1 |
|
smembers(name)
1 |
|
spop(name)
1 |
|
srandmember(name, numbers)
1 |
|
srem(name, values)
1 |
|
sunion(keys, *args)
1 |
|
sscan_iter(name, match=None, count=None)
1 |
|
2.2.5、Sort Set 操作
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
1 2 |
|
zcard(name)
1 |
|
zcount(name, min, max)
1 |
|
zincrby(name, value, amount)
1 |
|
zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
# 按照索引范围获取name对应的有序集合的元素 aa=r.zrange("zset_name",0,1,desc=False,withscores=True,score_cast_func=int) print(aa) '''参数: name redis的name start 有序集合索引起始位置 end 有序集合索引结束位置 desc 排序规则,默认按照分数从小到大排序 withscores 是否获取元素的分数,默认只获取元素的值 score_cast_func 对分数进行数据转换的函数'''
zscore(name, value)
#获取name对应有序集合中 value 对应的分数
zrank(name, value)
1 2 3 4 |
|
zrem(name, values)
1 2 3 |
|
zremrangebyrank(name, min, max)
1 |
|
zremrangebyscore(name, min, max)
1 |
|
zinterstore(dest, keys, aggregate=None)
1 2 |
|
zunionstore(dest, keys, aggregate=None)
1 2 |
|
2.2.6、其他常用操作
delete(*names)
1 |
|
exists(name)
1 |
|
keys(pattern='*')
1 2 3 4 5 6 7 |
|
expire(name ,time)
1 |
|
rename(src, dst)
1 |
|
randomkey()
1 |
|
type(name)
1 |
|
scan_iter(match=None, count=None)
1 |
|
2.2.7、使用场景
针对各种数据类型使用场景如下:
(一)String
这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存,比如减少库存。
(二)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
(三)list
使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适---取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。
(四)set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。
另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
(五)sorted set
sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。
2.3、管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
1 2 3 4 5 6 7 8 9 10 11 |
|
2.4、发布订阅
订阅者:
1 2 3 4 5 6 7 8 9 10 11 |
|
发布者:
1 2 3 4 |
|
发布订阅的特性用来做一个简单的实时聊天系统再适合不过了,当然这样的东西开发中很少涉及到。再比如在分布式架构中,常常会遇到读写分离的场景,在写入的过程中,就可以使用redis发布订阅,使得写入值及时发布到各个读的程序中,就保证数据的完整一致性。再比如,在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。