目录
Redis介绍
redis是一款非关系型的nosql数据库【存数据的地方,使用内存存储,速度非常快, 可以持久化存储数据数据【从内存同步到硬盘】。
数据类型丰富【5大数据类型:字符串,列表,哈希(字典),集合,有序集合】,以key-value形式存储【根本没有表的结构,相当于咱们的字典】
-nosql:指非关系型数据库:1 不限于SQL 2 没有sql
redis 为什么这么快
-1 高性能的网络模型:IO多路复用的epoll模型,承载住非常高的并发量
-2 纯内存操作,避免了很多io
-3 单线程架构,避免了线程间切换的消耗
-6.x之前:单线程,单进程
-6.x以后,多线程架构,数据操作还是使用单线程,别的线程做数据持久化,其他操作
redis 应用场景(了解)
1 当缓存数据库使用,接口缓存,提高接口响应速度
-请求进到视图---》去数据查询[多表查询,去硬盘取数据:速度慢]----》转成json格式字符串---》返回给前端
-请求进到视图---》去redis[内存]----》取json格式字符串---》返回给前端
2 做计数器:单线程,不存在并发安全问题
-统计网站访问量
-个人站点浏览量
-文章阅读量
3 去重操作:集合
4 排行榜:有序集合
-阅读排行榜
-游戏金币排行榜
5 布隆过滤器
6 抽奖
7 消息队列
下载安装
redis是一款开源软件:使用c语言写的---【编译型语言,在操作系统运行,要编译成可执行文件,由于采用了IO多路复用的epoll模型,所以它不支持windows,只有linux操作系统支持epoll】
微软官方:改了,编译成可执行的安装包,下载一路下一步安装
-版本没有最新
官网:https://redis.io/
-下载完是源代码:c语言源码 :
https://redis.io/download/#redis-stack-downloads
-最稳定:6.x
-最新7.x
中文网:
http://redis.cn/download.html
-上面最新只到5.x
![](https://i-blog.csdnimg.cn/blog_migrate/7a3779cdfb902498dd5c9e8285458063.png)
win版本下载地址
最新5.x版本 https://github.com/tporadowski/redis/releases/
最新3.x版本 https://github.com/microsoftarchive/redis/releases
下载完一路下一步即可,具体可参照:https://www.cnblogs.com/liuqingzheng/p/9831331.html
redis服务
win装完会有redis服务
-启动服务,手动停止
-客户端链接:
redis-cli -h 127.0.0.1 -p 6379
-简单命令:
set name yietong
get name
ping
-停掉服务:
-去win服务点关闭
-客户端关闭:
shutdown
redis本质也是cs架构的软件
redis 服务器端
redis 客户端
-redis-cli
-图形化工具:redis-desktop-manager
-python操作
pycharm 操作redis
安装包
pip3 install redis
from redis import Redis
conn=Redis( host="localhost",port=6379)
# conn.set('name','yietong')
print(conn.get('name'))
conn.close()
redis连接池
POOL.py
import redis
pool = redis.ConnectionPool(max_connections=200, host='127.0.0.1', port=6379)
redis-pool-demo.py
# 直接连接
from redis import Redis
from threading import Thread
# 直接链接
def get_name_from_redis():
conn = Redis(host="localhost", port=6379)
print(conn.get('name'))
conn.close()
for i in range(100):
t=Thread(target=get_name_from_redis)
t.start()
import time
time.sleep(10)
![](https://i-blog.csdnimg.cn/blog_migrate/0f7adea099dfdb89a3cfc3ba3b7a5f46.png)
使用连接池链接
import redis
from POOL import pool
def get_name_from_redis():
# 创建一个连接池,保证它是单例,全局只有一个pool对象:使用模块导入方式实现单例
conn = redis.Redis(connection_pool=pool) #m每执行一次会从池中取一个链接,如果没有,等待
res=conn.get('name')
print(res)
conn.close()
for i in range(100):
t=Thread(target=get_name_from_redis)
t.start()
import time
time.sleep(10)
redis 数据库
Redis 支持的数据类型概述
Redis 是一个数据结构服务器。 Redis 的核心是提供一系列本机数据类型,可帮助您解决从缓存到队列再到事件处理的各种问题。
字符串
Redis 字符串是最基本的 Redis数据类型,表示字节序列。
# 字符串
1 set(name, value, ex=None, px=None, nx=False, xx=False)
2 setnx(name, value)
3 psetex(name, time_ms, value)
4 mset(*args, **kwargs)
4 get(name)
5 mget(keys, *args)
6 getset(name, value)
7 getrange(key, start, end)
8 setrange(name, offset, value)
9 setbit(name, offset, value)
10 getbit(name, offset)
11 bitcount(key, start=None, end=None)
12 strlen(name)
13 incr(self, name, amount=1)
14 incrbyfloat(self, name, amount=1.0)
15 decr(self, name, amount=1)
16 append(key, value)
## 记住的:get set strlen
set()
def set(
self,
name: KeyT,
value: EncodableT,
ex: Union[ExpiryT, None] = None, # 过期时间(秒)
px: Union[ExpiryT, None] = None, # 过期时间(毫秒)
nx: bool = False, # 如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果。
xx: bool = False, # 如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值。
keepttl: bool = False, # 如果为True,保留与该键关联的生存时间。
get: bool = False, # 如果为True,将键name处的值设置为value并返回旧值存储在key中,如果key不存在则为None。
exat: Union[AbsExpiryT, None] = None, # 设置指定的Unix时间,该键将在该时间到期,单位为秒。
pxat: Union[AbsExpiryT, None] = None, # 设置指定的Unix时间,该键将在该时间到期,单位为毫秒。
import redis
conn = redis.Redis()
conn.set('name', 'yietong')
conn.set('age', 22)
conn.close()
setnx()
# 相当于set(..., nx=True)
def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
conn.setnx('name','xxx') # 存在不修改,执行没效果
conn.setnx('hobby','pingpang')
psetex()
# 本质就是 set(..., px)设置时间(毫秒)
def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
conn.psetex('name', 3000, 'yietong')
mset()
# 批量设置
def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
conn.mset({'name': 'yietong', 'age': 22, 'xxx': 'yyy'})
get()
def get(self, name: KeyT) -> ResponseT:
# 获取值,取到是bytes格式
# 可以在实例化redis对象时指定参数decode_responses=True
res = conn.get('name')
print(res) # b'yietong'
mget()
# 批量获取
def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
res = conn.mget('name', 'age')
print(res) # [ b'yietong', b'22']
getset()
# 先获取在设置
def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
res = conn.getset('age', 30)
print(res) # b'22'
getrange()
# 取的是字节,左闭右闭区间。
def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
res = conn.getrange('name',0,3)
print(res) # b'yietong'
setrange()
'''
从offset开始重新设置值,如果``offset``加上``value``的长度超过原始值的长
度,新值将比以前大。如果``offset``超过了原始值的长度,则用null填充。
'''
def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
res = conn.setrange('name',10, 'goodmorning')
print(res) # 22
setbit()
# 设置或清除存储在键的字符串值中的偏移位。
# 根据值设置或清除位,该值可以是 0 或 1.
def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:
bitcount()
# 返回` ' key ' `的值中设置的位数。可选``start``和``end``参数表示要考虑哪些字节
def bitcount(
self,
key: KeyT,
start: Union[int, None] = None,
end: Union[int, None] = None,
mode: Optional[str] = None,
) -> ResponseT
res = conn.bitcount('hobby', 0, 6)
print(res) # 28
strlen()
# 返回` ' name ' `的值中存储的字节数
def strlen(self, name: KeyT) -> ResponseT:
print(conn.strlen('hobby')) # 8
incr()
def incr(self, name: _Key, amount: int = ...) -> int: ...
res = conn.incr('age', amount=2)
print(res)
incrbyfloat()
def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:
decr()
def decr(self, name, amount: int = ...) -> int: ...
print(conn.decr('age'))
append()
将字符串’ ’ value ’ ‘附加到' key '的值上。如果“键”,不存在value创建它。返回值’ ’ key ’ '的新长度。
def append(self, key: KeyT, value: EncodableT) -> ResponseT:
print(conn.append('name', 'hahaha')) # 14
hash
'''
1 hset(name, key, value)
2 hmset(name, mapping)
3 hget(name,key)
4 hmget(name, keys, *args)
5 hgetall(name)
6 hlen(name)
7 hkeys(name)
8 hvals(name)
9 hexists(name, key)
10 hdel(name,*keys)
11 hincrby(name, key, amount=1)
12 hincrbyfloat(name, key, amount=1.0)
13 hscan(name, cursor=0, match=None, count=None)
14 hscan_iter(name, match=None, count=None)
'''## 记住:hset hget hexists hincrby hlen
hset()
'''
在hash `name`中设置`key`为`value`,
``mapping``接受一个由键值对组成的字典
添加到hash ``name``中。
``items``接受一个键值对列表作为参数
添加到hash ``name``中。
返回添加的字段数。
'''
def hset(
self,
name: str,
key: Optional[str] = None,
value: Optional[str] = None,
mapping: Optional[dict] = None,
items: Optional[list] = None,
) -> Union[Awaitable[int], int]:
res = conn.hset('info', 'name', 'yietong', mapping={'age': 12, 'xx': 'yy'})
print(res) # 3
hmset()
将key设置为对应哈希name中的值,mapping字典中的键和值。
def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
res = conn.hmset('info',{'xx':'xx', 'yy':'yy'})
print(res)
# DeprecationWarning: Redis.hmset() is deprecated. Use Redis.hset() instead.
# res = conn.hmset('info',{'xx':'xx', 'yy':'yy'})
# 已被弃用,使用hset()即可。
hget()
返回哈希值key中的值name
def hget(
self, name: str, key: str
) -> Union[Awaitable[Optional[str]], Optional[str]]:
res = conn.hget('info', 'xx')
print(res) # b'xx'
hmget()
返回与keys顺序相同的值的列表。
def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
res = conn.hmget('info', ('xx', 'yy'))
print(res) # [b'xx', b'yy']
hgetall()
返回一个Python字典,由散列的名称/值对组成
def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
res = conn.hgetall('info')
print(res) # {b'name': b'yietong', b'age': b'22', b'xx': b'xx', b'yy': b'yy'}
hlen()
返回哈希值name中的元素个数
def hlen(self, name: str) -> Union[Awaitable[int], int]:
res = conn.hlen('info')
print(res) # 4
hkeys()
返回hash name 中的键列表
def hkeys(self, name: str) -> Union[Awaitable[List], List]:
res = conn.hkeys('info')
print(res) # [b'name', b'age', b'xx', b'yy']
hvals()
返回hash name中的值列表
def hvals(self, name: str) -> Union[Awaitable[List], List]:
res = conn.hvals('info')
print(res) # [b'yietong', b'18', b'xx', b'yy']
hexists()
返回一个布尔值,表示key是否存在于哈希name中。
def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
res = conn.hexists('info','xx')
print(res) # True
hdel()
从hash name中删除keys
def hdel(self, name: str, *keys: List) -> Union[Awaitable[int], int]:
res = conn.hdel('info','xx', 'yy')
print(res) # 2
hincrby()
将hash name中的key的值增加amount
def hincrby(
self, name: str, key: str, amount: int = 1
) -> Union[Awaitable[int], int]:
conn.hset('ingo','age',18)
res = conn.hincrby('info','age',2)
print(res) # 20
hincrbyfloat()
将hash name中的key的值增加amount
def hincrby(
self, name: str, key: str, amount: int = 1
) -> Union[Awaitable[int], int]:
hscan()
在哈希表中递增地返回键值对切片,同样返回一个游标。
表示扫描位置。
match允许按模式过滤键
count允许提示最小返回次数
def hscan(
self,
name: KeyT,
cursor: int = 0,
match: Union[PatternT, None] = None,
count: Union[int, None] = None,
) -> ResponseT:
count不准确,有点上下浮动。
res = conn.hscan('test', cursor=0, count=7)
print(res)
print(len(res[1]))
print(res[0])
res = conn.hscan('test', cursor=res[0], count=7)
print(res)
print(len(res[1]))
print(res[0])
hscan_iter()
使用HSCAN命令创建一个迭代器,这样客户端就不需要迭代,需要记住光标的位置。
match允许按模式过滤键
count允许提示最小返回次数
def hscan_iter(
self,
name: str,
match: Union[PatternT, None] = None,
count: Union[int, None] = None,
) -> Iterator:
cursor = "0"
while cursor != 0:
cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
yield from data.items()
res = conn.hscan_iter('test')
print(res) # 生成器 <generator object ScanCommands.hscan_iter at 0x000001ADF783B9E0>
for i in res:
print(i)
conn.close()
列表操作
1 lpush(name,values)
2 lpushx(name,value)
3 rpushx(name, value) 表示从右向左操作
4 llen(name)
5 linsert(name, where, refvalue, value))
6 lset(name, index, value)
7 lrem(name, value, num)
8 lpop(name)
9 lindex(name, index)
10 lrange(name, start, end)
11 ltrim(name, start, end)
12 rpoplpush(src, dst)
13 blpop(keys, timeout)
14 brpoplpush(src, dst, timeout=0)
15 自定义增量迭代
lpush()
将values推到列表的头部name
def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
res = conn.lpush('redis_list', 1, 2, 3, 4, 5)
print(res) # 5
lpushx()
如果name存在,将value推到name的头部
def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
res = conn.lpushx('redis_list', 6, 7, 8)
print(res) # 8
rpush()和rpushx()
与上面两个相反,推到列表的尾部。
llen()
返回列表的长度name
def llen(self, name: str) -> Union[Awaitable[int], int]:
res = conn.llen('redis_list')
print(res) # 8
linsert()
在列表中插入元素,该元素存储在基准值基准值之前或之后。
如果key不存在,则认为它是一个空列表,不会执行任何操作。
如果key存在但没有保存列表值,则返回错误。
def linsert(
self, name: str, where: str, refvalue: str, value: str
) -> Union[Awaitable[int], int]:
res = conn.linsert('redis_list', 'before', '3', 'new')
print(res) # 9
lset()
将list name的index元素设置为value
def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
res = conn.lset('redis_list', 0, 'lset')
print(res) # True
lrem()
删除第一次count等于value的元素从存储在name中的列表中。
def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
res = conn.lrem('redis_list', 1, '5')
print(res) # 1
lpop()
删除并返回列表name的第一个元素。
默认情况下,该命令从开头弹出一个元素从列表中。当提供可选的count参数时,响应将由多数量的元素组成,这取决于列表的长度。
从 Redis 版本 6.2.0 开始:添加了参数count。
def lpop(self, name: str, count: Optional[int] = None) -> Union[str, List, None]:
res = conn.lpop('redis_list')
print(res) # b'lset'
lindex()
从列表name中返回位置index
支持负索引,从列表末尾开始
def lindex(
self, name: str, index: int
) -> Union[Awaitable[Optional[str]], Optional[str]]:
res = conn.lindex('redis_list', 0)
res1 = conn.lindex('redis_list', -1)
lrange()
返回列表name之间的一个切片
定位start和end
start和end也可以是负数
Python切片表示法
def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
res = conn.lrange('redis_list',0,-1)
print(res)
ltrim()
修剪列表name,删除所有不在切片内的
在start和end之间
start和end也可以是负数
Python切片表示法
def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
res = conn.ltrim('redis_list',1,-2)
rpoplpush()
从src列表中取出一个值RPOP,然后在dst列表上LPUSH。并返回rpop的值。
def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
conn.lpush('new_list', '1')
res = conn.rpoplpush('redis_list', 'new_list')
print(res) # b'new'
blpop()
从第一个非空列表中取出一个值
在 keys 列表中命名。
如果keys中的列表都没有值可以LPOP,则阻塞timeout秒,或者直到一个值被推送给一个值名单的。
如果timeout为0,则无限期阻塞。
def blpop(
self, keys: List, timeout: Optional[int] = 0
) -> Union[Awaitable[list], list]:
res = conn.blpop('redis_list')
print(res)
brpoplpush()
BRPOPLPUSH是RPOPLPUSH 的阻塞变体。 当包含元素时,此命令的行为与RPOPLPUSH 完全相同。 当在MULTI/EXEC块中使用时,此命令的行为与RPOPLPUSH 完全相同。 当为空时,Redis 将阻止连接,直到另一个客户端 推到它或直到到达。 零点可用于无限期阻止。
def brpoplpush(
self, src: str, dst: str, timeout: Optional[int] = 0
) -> Union[Awaitable[Optional[str]], Optional[str]]:
conn.lpush('redis_list', 1, 2, 3, 4)
res = conn.brpoplpush('redis_list', 'new_list')
print(res)
redis管道
MySQL事务的四大特性为ACID:隔离性,一致性,原子性,永久性。
redis数据库是否支持事务呢?
redis事务机制留一保证一致性和隔离性, 无法保证持久性,打死你hi对于redis而言,本身他是应该内存数据库,所以持久化不是必须属性, 原子性需要自己进行检测,尽可能保证。
redis不像mysql一样支持强事务, 事务的四大特性只能满足其中一部分, 但是通过redis的管道可以实现部分事务。
redis通过管道,来保证命令要么都成功,要么都失败, 完成十五的一致性,但是管道只能哟弄个在单实例,集群的环境中,不支持pipline
import redis
conn = redis.Redis() # 实例化
pipline = conn.pipeline(transaction=True)
pipline.decr('a', 2) # a减2
raise Exception('我崩了')
pipline.incr('b', 2) # b加2
pipline.execute()
conn.close()
redis其他操作
集合,有序集合, redis模块提供的方法API
通用操作: 五大类行都支持
import redis
conn = redis.Redis()
# 1 delete(*names)
conn.delete('age', 'name')
# 2 exists(name)
res=conn.exists('xx')
print(res) # 0
# 3 keys(pattern='*')
res=conn.keys('*o*')
# res=conn.keys('?o*')
print(res)
# 4 expire(name ,time)
conn.expire('test_hash',3)
# 5 rename(src, dst) # 对redis的name重命名为
conn.rename('xx','xxx')
# 6 move(name, db) # 将redis的某个值移动到指定的db下
# 默认操作都是0 库,总共默认有16个库
conn.move('xxx',2)
# 7 randomkey() 随机获取一个redis的name(不删除)
res=conn.randomkey()
print(res)
# 8 type(name) 查看类型
res = conn.type('aa') # list hash set
print(res)
conn.close()
django中集成redis
方式一: 直接使用
from user.POOL import pool
import redis
def index(request):
conn = redis.Redis(connection_pool=pool)
conn.incr('page_view')
res = conn.get('page_view')
return HttpResponse('被你看了%s次' % res)
方式二: 使用第三方模块django-redis
下载模块:
pip3 install django=redis
配置文件配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",
}
}
}
使用
from django_redis import get_redis_connection
def index(request):
conn = get_redis_connection(alias="default") # 每次从池中取一个链接
conn.incr('page_view')
res = conn.get('page_view')
return HttpResponse('被你看了%s次' % res)
方式三: 借助于django的缓存使用redis
如果配置文件配置了CACHES,以后django的缓存,数据直接放在redis中
直接使用cache.set设置值,【可以上传过期时间】
使用cache.get来获取值。 其强大之处在于: 可以直接缓存任意的python对象,因为其底层使用的pickle实现的。pickle可以序列化python的任意数据类型
访问test