一、redis介绍
1、redis是cs架构的储存数据的软件,纯内存数据库(所有数据都放在内存中),跟语言无关,key-value储存(没有表结构,各种关系)
2、速度非常快,指出的数据类型多样(5大数据类型:字符串,列表,字典,集合,有序集合)
3、Memcached和redis对比:
- redis是单线程,单进程(只针对于 redis 6.0以下,6.0以上是多线程、多进程);数据类型丰富
- memcache多线程多进程,数据类型单一,只支持字符串
3.1、 redis为什么单线程,单进程速度这么快?(老面试题)
-理论上支持10w并发,实际生产单机6w左右
-1 纯内存数据库
-2 io多路复用,epoll模型(linux上支持),windows上支持select ,非阻塞式io
-3 单线程单进程,避免了线程间切换(6.0以后已经是多线程了)
4、redis:开源软件,c语言写的,早期版本2w3千行,对windows不支持,现在最新版本6.0.6
-在不同平台编译---》可执行文件---》执行
-windows官方:又有大神把源码,在windows上编译--》可以允许在windows上(3.x)
二、redis的windows安装和配置
1 https://github.com/microsoftarchive/redis/releases
Redis-x64-3.2.100.msi:一路下一步,安装完成
2 6379:端口号
3 自动创建服务,服务运行
本质是一条命令::\Programs\Redis\redis-server.exe --service-run "D:\Programs\Redis\redis.windows-service.conf"
redis-server redis.windows-service.conf
(redis-server 配置文件) :以这个配置文件启动服务
4 手动启动redis的服务端
redis-server:服务端启动命令
redis-server:就启动了,但是使用的是默认配置
redis-server [配置文件]:以该配置文件启动
redis-cli:客户端
redis-cli:默认连接到本地的6379端口
redis-cli -h 127.0.0.1 -p 6379 (完整命令)
图形化客户端:
redis-desktop-manager:图形化客户端(收费)
三、普通链接和链接池
1、使用python连接redis
2、pip install redis
3、使用python代码连接redis服务端,python代码就是客户端,和使用图形化界面是一样的
GUI--> 可以开发出图形化界面的redis客户端
1、普通连接
from redis import Redis
# 得到一个redis连接
conn = Redis() # 默认host=localhost,port=6379
# conn = Redis(host='127.0.0.1', port=6379)
# 使用连接操作
conn.set('age', 18)
print(conn.get('age'))
conn.close()
- 写成with上写文管理
import redis
class MyRedis:
def __enter__(self):
self.conn = redis.Redis()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
with MyRedis() as conn:
conn.set('sex', 'male')
2、连接池
import redis
# 创建一个连接池
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=100)
# 从连接池中取一个连接
conn = redis.Redis(connection_pool=pool)
conn.set('foo', 'Bar')
print(conn.get('foo'))
conn.close()
3、实际应用
- redis_pool.py文件
import redis
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=100)
-
脚本文件s1.py
(在使用时以单例的模式导入模块使用POOL,避免每次使用都创建一个连接池)
import redis
from redis_pool import POOL # POOL 是单例
conn = redis.Redis(connection_pool=POOL)
conn.set('age', 18)
conn.close()
四、redis之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操作才执行,值存在才能修改,值不存在,不会设置新值
示例: conn.set('name', 'jason',nx=True)
# setnx(name,value)
设置值,只有name不存在时,执行设置操作,如果存在,不会修改
# setex(name, time, value)
设置值, 参数time是过期时间,(数字秒或者timedelta对象)
示例:conn.setex('name', 5, 'cc')
# psetex(name, time_ms, value)
设置值,参数time_ms是毫秒数
# mset(*args, **kwargs)
批量设置值
示例:
mset(name='cc', age=18)
mset({'name':'cc', 'age':18})
# get(name)
取值
# mget(keys. *args)
批量获取值
示例:
mget('k1', 'k2')
r.mget(['k1', 'k2'])
# getset(name, value)
设置新值并获取原来的值
# getrange(name, start, end)
获取子序列(根据字节获取,非字符),前闭后闭区间
参数:
name: redis中的key
start: 起始位置字节索引
end: 结束位置字节索引(包含)
# setrange(name, offset, value)
修改字符串内容,从指定字符串的索引开始向后替换,新值太长时,则向后添加
参数:
offset:字符串的索引,字节(一个汉字三个字节)
value: 要设置的值
# setbit(name, offset, value)
对name对应值的二进制表示的位进行操作,
参数:
name:redis中的name
offset: 位的素引(将值变成二进制后再进行索引)
value: 值只能是0或者1
# 注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
# getbit(name, offset)
获取name对应的值的二进制表示 的某位的值(0或者1)
# bitcount(key, start=None, end=None)
获取name对应的值的值的二进制表示中1的个数
参数:
key: redis中的name
start: 起始位置
end: 结束位置
# strlen(name)
返回name对应值的字节长度(一个汉字3个字节)
# incr(self, name, amount=1)
同incrby
自增name对应的值,当name不存在时,则创建name=amount,否则,则自增(用于统计网页的访问量,文章的阅读量,粉丝数量,计数相关,不会存在并发安全的问题)
参数:
name: redis的name
amount:自增数,(必须是整数)
conn.incr(age, 2)
conn.incr(age, -2)
# decr(self, name, amount=1)
自减name对应的值,当name不存在时,则创建name=amount, 否则将自减
参数:
name:redis的name
amount: 自减数(整数)
conn.decr('age', 1) # 同 conn.incr('age', -1)
# append(key, value)
在redis name对应的值后面追加内容
参数:
key: redis的name
value: 要追加的字符串
示例:
conn.append('name', '_dsb')
五、redis之Hash操作
hash操作,redis中的hash在内存中的储存格式如下图:

# hset(name, key, value)
name对应的hash中设置一个键值对(不存在就创建,否则就修改)
参数:
name: redis的name
key: name对应的hash中的key
value: name对应的hash中的value
# 注:hsetnx(name,key,value),当name对应的hash中不存在当前key时则创建,相当于添加
# hmset(name, mapping)
在name对应的hash中批量设置键值对
参数:
name; redis的name
mapping: 字典:如{'k1':'v1', 'k2':'v2'}
示例:
conn.hmset('xx',{'name':'cc','age':'18'})
# hget(name, key)
在name对应的hash中获取根据key获取value
# hmget(name, keys, *args)
在name对应的hash中获取多个key的值
参数:
name: redis对应的name
keys: 要获取key集合,如:{'k1', 'k2', 'k3'}
*args: 要获取的key,如: k1, k2, k3
示例:
conn.hmget('xx', ['k1', 'k2'])
或者conn.hmget('xx', 'k1', 'k2')
# hgetall(name)
获取name对应的hash的所有键值
示例:
print(conn.hgetall('xx').get(b'name'))
# hlen(name)
获取name对应的hash中键值对的个数
# hkeys(name)
获取name对应的hash中所有的key的值
# hvalS(name)
获取name对应的hash是否存在当前传入的key
# hexists(name, key)
检查name对应的hash是否存在当前传入的key
# hdel(name, *keys)
将name对应的hash中指定key的键值对删除
print(conn.hdel('xx', 'name', 'sex')) # 显示的是删除的个数
# hincrby(name, key, amount=1)
自增name对应的hash中的指定key的值,不存在则创建key=amount
参数:
name: redis中的name
key: hash对应的key
amount: 自增数(整数)
# hincrbyfloat(name, key, amount=1.0)
自增name对应的hash中指定的key的值,不存在则创建key=amount
参数:
name: redis中的name
key: hash对应的key
amount: 自增数(浮点数)
# hscan(name, cursor=0, match=None, count=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而防止内存溢出
参数:
name: redis中的name
cursor: 游标(基于游标分批获取数据)
match: 匹配出指定的key,默认None,表示素所有的key
count:每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# hscan_iter(name, match=None, count=None)
利用yield封装hscan创建生成器,实现分批去redis中获取数据
参数:
match: 匹配指定key,默认None表示所有的key
count: 每次分片最少获取个数,默认None表示采用的Redis的默认分片个数
示例:
for item in conn.hscan_iter('xx'):
print(item)
六、redis操作之list操作
list操作: redis中的list在内存中按照一个name对应一个List来存储
# lpush(name, values)
在name对应的list中添加元素,每个新的元素都添加到列表的最左边
示例:
r.lpush('num', 11,22,33)
保存顺序为: 33,22,11
# rpush(name, values)
表示从右向左操作
# lpushx(name, value)
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
# rpushx(name, value)
表示从右向左操作
# llen(name)
name对应的list元素的个数
# linsert(name, where, refvalue, value)
在name对应的list的某一个值前面或后面插入一个新值
参数:
name: redis的name
where:BEFORE或者AFTER(大小写均可),表示在前面或者后面
refvalue: 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
value: 要插入的数据
# lset(name, index, value)
对name对应的list中某一个索引位置重新赋值
参数:
name: redis的name
index: list的索引位置
value: 要设置的值
# lrem(name, value, num)
在name对应的list中删除指定的值
参数:
name: redis的name
value: 要删除的值
num:
# num=0, 删除列表中所有的指定值
# num=1, 从前到后,删除1个
# num=-2,从后向前,删除2个
# pop(name)
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值是第一个元素
# rpop(name)
表示从右向左操作
# lindex(name, index)
在name对应的list中根据索引获取列表元素
# lrange(name, start, end)
在name对应的list分片获取数据(前闭后闭)
参数:
name:redis中的name
start: 索引的起始位置
end: 素引的结束位置
示例:
print(conn.lrange('aa', 0, conn.llen('aa'))) # 全部取出来
# ltrim(name, start, end)
在name对应的list中索引不在[start, end]区间的值全部移除
参数:
start: 索引的起始位置
end: 索引的结束位置(如果大于列表长度,则代表不移除)
# rpoplpush(src, dst)
从一个list取出最左边的元素,同时将其添加至另一个列表的最左边(两个列表可以是同一个)
参数:
src: 要取数据的列表的name
dst: 要添加数据的列表name
# blpop(keys, timeout)
将多个列表排列,按照从左到右去pop对应列表的元素
参数:
keys:redis的name的集合
timeout: 超时时间,当元素所有的元素取完之后,阻塞等待列表内有数据的时间(秒),0表示永远阻塞
# brpop(keys, timeout),表示从右往左获取数据
爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
# brpoplpush(src, dst, timeout=0)
从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
参数:
src: 取出并移除元素的列表的name
dst:要插入元素的列表的name
timeout:当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0表示永远阻塞
- 自定义增量迭代
# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
# 1、获取name对应的所有列表
# 2、循环列表
# 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
import redis
conn = redis.Redis(host='127.0.0.1', port=6379)
# conn.lpush('test', *[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])
# conn.flushall()
print(conn.lrange('test', 0 ,conn.llen('test'))) # 全部取出来,因为不知道列表有多大,有可能会导致内存撑爆
def scan_list(name, count=2):
index = 0
while True:
data_list = conn.lrange(name, index, index+count-1)
if not data_list:
break
index += count
for item in data_list:
yield item
# 用生成器,每次取固定长度的值
for item in scan_list('test', count=5):
print('---')
print(item)
七、redis之其他操作
import redis
# 公共操作
# 待见哨兵,用了集群,这个模块就不够用了
conn = redis.Redis()
# delete
res = conn.delete('aa', 'name')
# exists
res = conn.exists('l1', 'l2', 'l3') # 存在几个就等于几,没有就是0
# expire (给某个name设置超时时间)
res = conn.expire('l_test', 5) # 5秒后自动过期删除
# rename
conn.lpush('l1', 1, 2, 3)
res = conn.rename('l1', 'l2') # 重命名,成功就返回True
# move
res = conn.move('l1', '3') # 将l1移动到db3 (redis的库是隔离的)
# 随机一个key值 (用于抽奖)
res = conn.randomkey()
conn.sadd('choujiang', 1)
conn.sadd('choujiang', 2)
conn.sadd('choujiang', 3)
res = conn.spop('choujiang') # 随机抽一个,被抽出来的会被自动删除,
print(res)
# type (查看类型)
res = conn.type('choujiang')
res = conn.type('ss')
print(res)
conn.close()
八、django中使用redis
1、通用方案
- redis_pool.py (创建一个连接池)
import redis
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=100)
- view.py
from utils.redis_pool import POOL
import redis
def test_redis(request):
conn = redis.Redis(connection_pool=POOL)
age = str(conn.get('age'), encoding='utf-8')
return HttpResponse('age:%s' % age)
2、django提供的方案
安装:
pip install django-redis
- settings.py中配置:
# redis的配置:django的缓存使用redis,很方便
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",
}
}
}
- view.py
from django_redis import get_redis_connection
from django.core.cache import cache
def test_django_redis(request):
conn = get_redis_connection() # 从连接池中拿到连接
age = conn.get('age').decode('utf-8')
cache.set('name', 'cc', 60) # 往缓存中放key-value,其实放到了redis中,60是过期时间
cache.get('name') # 取值
# cache中还可以直接放对象
cache.set('xxx', test_redis) # test_redis是一个函数对象(通过pickle转成字符串存在redis中了)
return HttpResponse('age: %s' % age)
九、管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
# redis是非关系型数据库,不支持事务
# redis提供了管道,通过管道,实现事务
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
conn = redis.Redis(connection_pool=pool)
# 开启一个管道
pipe = conn.pipeline(transaction=True)
pipe.multi()
pipe.set('name', 'cc')
pipe.set('age', '18')
# 向管道中发送上面两个命令,并没有真正的执行,
pipe.execute() # 这条命令才让 一次性执行管道中的命令
conn.close()