redis数据库
- 概念:Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库。它支持存储的value 类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和 hash(哈希类型)。Redis的数据都基于缓存的,所以很快,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。Redis也可以实现数据写入磁盘中,保证了数据的安全不丢 失,而且Redis的操作是原子性的。
redis的优点
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复
杂度都很低
(2)支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
redis 安装与使用
关于版本:
现在出的是:7.x
使用的较多:5.x
安装:
Mac 源码编译安装
Linux 源码编译安装
Windows 微软基于源码,改动编译成安装包
最新5.x版本
https://github.com/tporadowski/redis/releases/
最新3.x版本
https://github.com/microsoftarchive/redis/releases
安装直接点下一步即可
安装好以后,redis会自动加入到服务中,只需要执行两个命令:
redis-server 服务端启动命令
redis-cli 客户端启动命令
配置文件启动前台服务
redis-server 配置文件的绝对路径
redis.windows-service.conf 配置文件
配置文件启动后台服务
注)windows系统默认按Redis安装包下的redis.windows-service.conf配置文件启动
redis-server --service-start
# 启动redis
方式一:
在服务中,点击启动,后台启动
方式二:
redis-server 指定配置文件,如果不指定,会使用默认的
# 客户端连接redis
1 方式一
redis-cli #默认连接本地的6379端口
2 方式二:
redis-cli -h 地址 -p 端口
# 使用图形化界面操作
redis默认有16个库,默认连进去就是第0个
redis 普通链接池
# python 相当于客户端,操作redis
# 安装模块:pip install redis
#补充: django 中操作mysql,没有连接池的,一个请求就是一个mysql连接
-可能会有问题,并发数过高,导致mysql连接数过高,影响mysql性能
-使用django连接池:https://blog.51cto.com/liangdongchang/5140039
# 1 导入模块的Redis类
from redis import Redis
# 2 实例化得到对象
conn = Redis(host='127.0.0.1', port=6379)
# 3 使用conn,操作redis
# 获取name的值
# res = conn.get('name') # 返回数据是bytes格式
# 4 设置值
conn.set('age',19)
conn.close()
连接池连接
import redis
# 创建一个大小为10的redis连接池
POOL=redis.ConnectionPool(max_connections=10,host='127.0.0.1',port=6379)
ps:将它设置成模块,做成模块之后,无论导入多少次,导入的都是那一个POOL对象,就不会出现混乱的现象
-这是一种单例模式,全局只有这一个对象
# 做个测试
import redis
from threading import Thread
from pool import POOL
def task():
conn = redis.Redis(connection_pool=POOL)
# 链接池中的链接数不够的,线程就报错,所以需要设置等待参数
print(conn.get('name'))
# 开启多线程
for i in range(100):
# 每一次都是一个新的连接,就会导致连接数过多
t = Thread(target=task)
t.start()
redis字符串执行命令
1.添加数据
set (name,value,ex=None,px=None,nx=False,xx=False)
在redis中设置值,默认不存在就创建,存在就修改
参数:
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果 (可以用来执行分布式操作)
xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
2.setnx(name,value)
设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改
3.setex(name,time,value) 注意位置
设置过期时间 time 的单位是秒 或timedelta对象
4.pesetex(name, time_ms, value)
time_ms,过期时间(数字毫秒 或 timedelta对象
5.mset(*args, **kwargs)
批量设置值:
mset(k1='v1', k2='v2')或mget({'k1': 'v1', 'k2': 'v2'})
6.get (name)
获取值,值不存在返回none
7.mget(keys, *args)
批量获取:mget('k1', 'k2')或r.mget(['k3', 'k4'])
8.getset(name, value)
设置一个新的值并获取原来的值
9.getrange(key, start, end)
获取子序列(根据字节获取)
参数:
# name,Redis 的 name
# start,起始位置(字节)
# end,结束位置(字节)
10.setrange(name, offset, value)
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
参数:
# offset,字符串的索引,字节(一个汉字三个字节)
# value,要设置的值
11.strlen(name)
返回name对应的字节长度(一个汉子一般是3个字节)
12.incr(self, name, amount=1)
自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
参数:
# name,Redis的name
# amount,自增数(必须是整数)
# 注:同incrby
13.incrbyfloat(self, name, amount=1.0)
自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(浮点型)
14.decr(self, name, amount=1)
自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
参数:
# name,Redis的name
# amount,自减数(整数)
15.append(key, value)
在redis name对应的值后面追加内容
# 参数:
key, redis的name
value, 要追加的字符串
redis :列表操作
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
lpush(name,values)
表示从右向左操作
lpushx(name,value)
表示从右向左操作
llen(name)
name对应的list元素的个数
linsert(name, where, refvalue, value))
参数:
# name,redis的name
# where,BEFORE或AFTER(小写也可以)
# refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
# value,要插入的数据
lset(name, index, value)
参数:
# name,redis的name
# index,list的索引位置
# value,要设置的值
lrem(name, value, num)
参数:
# name,redis的name
# value,要删除的值
# num, num=0,删除列表中所有的指定值;
# num=2,从前到后,删除2个;
# num=-2,从后向前,删除2个
lpop(name)
表示从右向左操作
lindex(name, index)
在name对应的列表中根据索引获取列表元素
lrange(name, start, end)
参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置 print(re.lrange('aa',0,re.llen('aa')))
ltrim(name, start, end)
参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置(大于列表长度,则代表不移除任何)
rpoplpush(src, dst)
参数:
# src,要取数据的列表的name
# dst,要添加数据的列表的name
blpop(keys, timeout)
参数:
# keys,redis的name的集合
# timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
# 更多:
# r.brpop(keys, timeout),从右向左获取数据
爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
PS:增量迭代
hash 操作
hset(name, key, value)
参数:
# name,redis的name
# key,name对应的hash中的key
# value,name对应的hash中的value
# 注:
# hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
hmset(name, mapping)
参数:
# name,redis的name
# mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
hget(name,key)
在name对应的hash中获取根据key获取value
hmget(name, keys, *args)
参数:
# name,reids对应的name
# keys,要获取key集合,如:['k1', 'k2', 'k3']
# *args,要获取的key,如:k1,k2,k3
hlen(name)
获取name对应的hash中键值对的个数
hkeys(name)
获取name对应的hash中所有的key的值
hvals(name)
获取name对应的hash中所有的value的值
hexists(name, key)
检查name对应的hash是否存在当前传入的key
hdel(name,*keys)
将name对应的hash中指定key的键值对删除
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
hscan(name, cursor=0, match=None, count=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
# 参数:
# name,redis的name
# cursor,游标(基于游标分批取获取数据)
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
hscan_iter(name, match=None, count=None)
利用yield封装hscan创建生成器,实现分批去redis中获取数据
参数:
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# for item in r.hscan_iter('xx'):
# print item
hgetall(name) #会一次性取出,效率低,数据多的话肯恩个会占用很多的内存
获取name对应hash的所有键值和值
分批获取:hash类型是无序的
插入一批数据
for i in range(1000):
# conn.hset('hash_test','id_%s'%i,'鸡蛋_%s号'%i)
# res=conn.hgetall('hash_test') # 可以,但是不好,一次性拿出,可能占很大内存
# print(res)
# 13 hscan(name, cursor=0, match=None, count=None) # 它不单独使用,拿的数据,不是特别准备
# res = conn.hscan('hash_test', cursor=0, count=5)
# print(len(res[1])) #(数字,拿出来的10条数据) 数字是下一个游标位置
ps:我们使用这个,它的内部用了hascan,等同于hgetall所有数据都会拿出来,count的作用,生成器,每次那count个个数
PS:generator 只要函数中有yield关键字,这个函数执行的结果就是生成器 ,生成器就是迭代器,可以被for循环
生成器使用__next__每次拿到的数据
res=conn.hscan_iter('hash_test',count=10)
res 的数据的个数是不确定的
hscan_iter 获取所有值,但是省内存 等同于hgetall
redis通用操作
通用操作,不指定类型,所有的类型都支持
1 delete(*names)
根据删除redis中的任意数据类型
2 exists(name)
检测redis的name是否存在
3 keys(pattern='*')
根据模型获取redis的name
更多:
# KEYS * 匹配数据库中所有 key 。
# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
# KEYS h*llo 匹配 hllo 和 heeeeello 等。
# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
4 expire(name ,time)
为某个redis的某个name设置超时时间
5 rename(src, dst)
对redis的name重命名为
6 move(name, db))
将redis的某个值移动到指定的db下
7 randomkey()
随机获取一个redis的name(不删除)
8 type(name)
获取name对应值的类型
redis管道
mysql 开启事物----四大特性:
原子性,一致性,隔离性,持久性
# redis是否支持事物,如果是单实例(就是跑一个服务)的话,就是支持的,而支持事物需要基于管道
redis由于是缓存数据库,所以对于数据的操作,可能断电就会丢失,当我同门同时执行两条命令,一个先执行了,另一个来不及执行,就会导致数据错乱,这时候,我们使用管道将这两条命令放起来,执行了excute,一次性将两条命令执行。
使用方法:
import redis
conn=redis.Redis()
p=conn.pipeline(transaction=True) # 开启一个事物
p.multi() # 批量处理
p.decr('age_andy',10)
# raise Exception('这是主动抛出的一个异常')
p.incr('age_kiki',10)
p.execute() ## 这是一次性去执行
conn.close()
PS;使用管道来支持事物的开启,做到要么都成功,要么都失败
django中使用redis
#@ 方式一:
这是一个自定义包的方案(是通用的,所有的框架都能使用)
在utils中写一个半连接池 POOL
import redis
POOL = redis.ConnectionPool(max_connections=100)
在view.py 中写一个测试文件,使用的时候直接导入即可
from utils.pool import POOL
import redis
# 测试接口 计算访问接口的次数
def test_redis(request):
conn=redis.Redis(connection_pool=POOL)
conn.incr('count')
res=conn.get('count',)
return JsonResponse({'count':'访问这个接口的次数 :%s'%res},json_dumps_params={'ensure_ascii':False})
# 在redis数据库中创建一个字段 count 做为计数
#@ 方式二: django中推荐使用缓存的方法
使用django,django自身是支持redis的
方案一:django的缓存使用的就留是redis
- 需要在配置文件中配置一些数据
- django原生也是不支持使用的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",
}
}
}
ps: 配置文件中如果没有写的话默认使用的是global_settings 文件中的默认的(内存)
# 这里的count在redis的数据库中会创建一个文件来存放
from django.core.cache import cache
# 使用django操作redis
def test_redis(request):
# 这里的count是django自己的,和之前redis的不一样
res=cache.get('count')
cache.set('count',res+1)
# 1,这个字段需要提前创建好
# cache.set('count',0)
return JsonResponse({'count':'访问这个接口的次数 :%s'%res},json_dumps_params={'ensure_ascii':False})
- 在使用的地方直接使用cache.set('count',0) 即可
- redis只支持五大类型的数据格式存储,但是python是什么类型的都支持的,使用到的就是python自带的pike 序列化模块,redis原生是不支持的对象的
---django的缓存使用的是pickle序列化后存入的
class Person:
name='jason'
from django.core.cache import cache
# 使用django操作redis
def test_redis(request):
# 这里的count是django自己的,和之前redis的不一样
p=Person()
# 数据会以Bytes类型存入数据
# cache.set('person',p)
p=cache.get('person')
print(p.name)
return JsonResponse()
方案二:如果在django中使用上面的这种写法(方式一),需要借助于第三方 django_redis模块
from django_redis import get_redis_connection
def test_redis(request):
conn=get_redis_connection()
# 从配置文件中的链接池中拿一个链接
print(conn.get('count'))
return JsonResponse({'count':'hhh'})
PS:上面的类放在外面的原因,是因为如果放在函数内部,函数一执行,就会在全局找person,它就找不到这个类,这就触及到了作用于范围,函数执行在全局中找不到person这个类对象,所以就会出现报错(所以需要将它定义在全局,全局作用域范围)
小知识点:
1.Memcache 与 Redis 的区别都有哪些?
1、存储方式 Memecache 把数据全部存在内存之中, 断电后会挂掉, 数据不能超过内存大小。 Redis有部份存在硬盘上, 这样能保证数据的持久性
2、数据支持类型 Memcache 对数据类型支持相对简单。 Redis 有复杂的数据类型。
3、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话, 会浪费一定的时间去移动和请求。
2.Redis 是单进程单线程的?
Redis 是单进程单线程的, redis 利用队列技术将并发访问变为串行访问, 消除了传统数据库串行控制的开销。
3.Redis 的持久化机制是什么?各自的优缺点?
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
RDB:是Redis DataBase缩写快照
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
优点:
1.只有一个文件 dump.rdb,方便持久化。
2.容灾性好,一个文件可以保存到安全的磁盘。
3.性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单 独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
4.相对于数据集大时,比 AOF 的启动效率更高。
缺点:
1.数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数 据丢失。所以这种方式更适合数据要求不严谨的时候)
2.AOF(Append-only fifile)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式 完全持久化存储)保存为 aof 文件。
AOF:持久化:
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件
中,当重启Redis会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
优点:
1.数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录 到 aof 文件中一次。
2.通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一 致性问题。
3.AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flflushall))
缺点:
1.AOF 文件比 RDB 文件大,且恢复速度慢。
2.数据集大的时候,比 rdb 启动效率低。
3.为什么 Redis 需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis 具有快速和数据持久化的特征。如果数据放在内存中, 磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天, redis 将会越来越受欢迎。如果设置了最大使用的内存, 则数据已有记录数达到内存限值后不能继续插入新值。
4.Redis 的同步机制了解么?
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave, 并同时将后续修改操作记录到内存 buffffer, 待完成后将 rdb 文件全量同步到复制节点, 复制节点接受完成后将 rdb 镜像加载到内存。加载完成后, 再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
跟多详情