[Redis内存数据库]

[Redis内存数据库]

详情点我 ←看什么?猛击左边啊

redis介绍

Redis是什么

redis数据库,非关系型(redis:内存数据库,所有数据放在内存中,mongodb:数据放在硬盘上,es:放在硬盘上) 关系型:mysql,db2,oracle,posgresql,sqlserver,达梦(国产数据),sql都是通用的,表和表之间的关联关系,事务的支持比较好,锁机制

redis是一个基于键值对key-value的存储系统。key-value存储:字典形式 支持5大数据类型:字符串,列表,字典(hash),集合,有序集合

6.0之前单线程,单进程的 redis是单线程为什么这么快? 全内存操作 基于io多路复用的模型 没有多线程多进程的切换

高性能,功能丰富

那些公司在用

github,twitter,*,阿里,百度,微博,美团,搜狐

Redis特性(8个)

1.性能高:10w ops(每秒10w读写),实际生产环境实际6w,数据存在内存中,c语言实现,单线程模型
?
2.持久化:rdb和aof
?
3.多种数据结构:
?
    5大数据结构
?
    BitMaps位图:布隆过滤器 本质是 字符串
?
    HyperLogLog:超小内存唯一值计数,12kb HyperLogLog 本质是 字符串
?
    GEO:地理信息定位 本质是有序集合
?
4.支持多种编程语言:基于tcp通信协议,各大编程语言都支持
?
5.功能丰富:发布订阅(消息) Lua脚本,事务(pipeline)
?
6.简单:源代码几万行,不依赖外部库
?
7.主从复制:主服务器和从服务器,主服务器可以同步到从服务器中
?
8.高可用和分布式:
?
    ? 2.8版本以后使用redis-sentinel支持高可用
?
    ? 3.0版本以后支持分布式

Memcached:内存数据库,key-value存储,支持的数据类型少,只有str类型,没有持久化,断电消失

redis不支持windows,io多路复用的模型,在linux使用的是epoll,在windwos上没有epoll,于是有一些第三方,基于源码改动了一些,让他可以在windows上执行(最新版只到3.x)

redis相比memcached有哪些优势

-都是内存数据库

-redis类型丰富,5大类型,memcached只支持字符串类型

-redis支持持久化,memcached不支持持久化

-redis速度比mencached快

Redis下载安装

1 windows的下载地址
    https://github.com/microsoftarchive/redis/releases
2 windows安装
    一路下一步:添加到环境变量,端口号是6379,使用的最大内存,不限制
    
3 装完之后,就会有个redis服务
    通过启动服务,停止服务来运行停止redis
4 命令方式启动和停止
    启动
        redis-server 配置文件路径   # 使用配置文件启动
        redis-server            # 不使用配置文件启动
   客户端连接
  		-第一种方式:在任意路径下敲:redis-cli -h 127.0.0.1 -p 6379   # 等同于  mysql -h -P
    	-如果连本地的6379端口,直接敲redis-cli
        -使用客户端连接
        -使用python操作:pip3 install redis
        
        在客户端:shutdown
        在客户端敲:ping 回一个pone
        
        
        
5 redis的客户端很多
        redis-desktop-manager
            qt:平台,专门用来写图形化介模
         pyqt:用python代码在qt平台写图形化界面
            
6 在客户端写
    set name lqz 设置指定key,value
    get name    获取值

Redis典型使用场景

缓存系统:使用最广泛的就是缓存

计数器:网站访问量,转发量,评论数(文章转发,商品销量,单线程模型,不会出现并发问题)

消息队列:发布订阅,阻塞队列实现(简单的分布式,blpop:阻塞队列,生产者消费者)

排行榜:有序集合(阅读排行,点赞排行,推荐(销量高的,推荐))

社交网络:很多特效跟社交网络匹配,粉丝数,关注数

实时系统:垃圾邮件处理系统,布隆过滤器

python操作redis

redis之普通连接

# 安装pip install redis
?
from redis import Redis

# 实例化对象,创建连接对象
conn = Redis(host=‘127.0.0.1‘,port=6379) # 什么都不写,连的就是本地的6379

# 添加name等于lpf
conn.set(‘name‘,‘lpf‘)

# 获取name这个key对应的值
res=conn.get(‘name‘)
print(res)
conn.close()

redis连接之连接池

import redis

# 先定义一个池子,池子大小指定
# 这个pool必须做成一个单例(这句话只写一次)

# 将池子pool放到redis_pool.py文件中改成大写POOL
import redis
POOL = redis.ConnectionPool(max_connections=10)
?
# 当该py文件以脚本运行,其中包的导入不能使用相对导入
# 如果是以模块形式导入使用,这个模块内可以使用相对导入
?
from redis_pool import POOL  # 天然的单例,不管导入多少次,只会实例化一次
# 建立连接,从池子中获取一个连接
conn=redis.Redis(connection_pool=POOL)
?
res=conn.get(‘name‘)
print(res)

# conn.close()  # 不是关闭连接,把该连接,放回到连接池

Redis的String操作

String操作,redis中的String在在内存中按照一个name对应一个value来存储

[Redis内存数据库]
‘‘‘
set(name, value, ex=None, px=None, nx=False, xx=False)       重点
setnx(name, value)
setex(name, value, time)
psetex(name, time_ms, value)
mset(*args, **kwargs)         重点
get(name)                    重点
mget(keys, *args)             重点
getset(name, value)
getrange(key, start, end)
setrange(name, offset, value)
setbit(name, offset, value)
getbit(name, offset)
bitcount(key, start=None, end=None)

strlen(name)                     重点
incr(self, name, amount=1)            重点
incrbyfloat(self, name, amount=1.0)
decr(self, name, amount=1)
append(key, value)
‘‘‘
---------------------------------------------------------------------------------
from redis import Redis

conn = Redis(host=‘127.0.0.1‘,port=6379) # 什么都不写,连的就是本地的6379

1 set(key, value):设置指定key的值
# conn.set(‘age‘,16)
# conn.set(‘name‘,‘LPF‘) # 插入中文可以正常插入
# conn.set(‘hobby‘,‘篮球‘)
# conn.set(‘hobby‘,‘篮球‘,3) # 设置值3秒自动销毁
# conn.set(‘hobby‘,‘篮球‘,ex=3,nx=True)# 设置值3秒自动销毁,无法修改
# conn.set(‘hobby‘,‘足球‘,nx=True) # 改篮球的值,没法生效
# conn.set(‘hobby‘,‘足球‘,xx=True) # 可以修改篮球的值
# conn.set(‘hobby1‘,‘足球‘,xx=True) # key不存在无法修改,不会生效
‘‘‘
在Redis中设置值,默认,不存在则创建,存在则修改
参数:
   ex,过期时间(秒)
   px,过期时间(毫秒)
   nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
   xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
‘‘‘

?
2 get(key):获取指定 key 的值。
# print(str(conn.get(‘name‘),encoding=‘utf-8‘))  # 取出来的值是bytes格式,指定以utf-8格式显示

?
3 setnx(key, value):只有在 key 不存在时设置 key 的值
# conn.setnx(‘sex‘, ‘male‘)
# conn.setnx(‘name‘, ‘孙宗伟‘) # 存在就不会添加

?
4 setex(key, seconds, value):将值value关联到key ,并将key的过期时间设为seconds(以秒为单位)。
# conn.setex(‘xx‘,5,‘yy‘) # 设置key值xx,value值yy,5秒钟过期

?
5 psetex(key, milliseconds, value):这个命令和setex命令相似,但它以毫秒为单位设置 key 的生存时间。
# conn.psetex(‘xx‘,5000,‘yy‘)

?
6 mset( key1, value1 ,key2, value2):同时设置一个或多个 key-value 对。
# conn.mset({‘name‘:‘lqz‘,‘age‘:19})
?

7 mget(key1, key2):获取所有(一个或多个)给定 key 的值。
# print(conn.mget([‘name‘,‘age‘]))
# print(conn.mget(‘name‘,‘age‘)) # 本质跟上面是一样的
?
8 getset(key, value):将给定key的值设为 value ,并返回 key 的旧值(old value)
# res=conn.getset(‘name‘,‘egon‘) # 先获取再更新
# print(res) # 打印的是lqz,但已经更新为egon了

?
9 getrange(key, start, end):返回 key 中字符串值的子字符。
"""
gbk:  2个字节表示一个字符
utf-8  3个字节表示一个字符
"""
# res=conn.getrange(‘name‘,0,0)  # 前闭后闭区间,取到的是egon的e
# res=conn.getrange(‘name‘,0,2).decode(‘utf-8‘)  # 将key值手动改为中文了,打印显示一个字,取3个就会报错
# print(res) 

?
10 setrange(key, offset, value):用value参数覆写给定key所储存的字符串值,从偏移量offset开始。
# conn.setrange(‘name‘,2,‘zzz‘)

?
11 strlen(key):返回 key 所储存的字符串值的长度(一个汉字,是3个字节)。
# print(conn.strlen(‘name‘))

?
12 incr(key):将 key 中储存的数字值增一。
# print(conn.incr(‘age‘))
?

13 decr(key):将 key 中储存的数字值减一。
# print(conn.decr(‘age‘))
?

14 append( key, value):如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。
# conn.append(‘name‘,‘nb‘) # 在原有的egon后面加nb
# conn.append(‘xxxxxxx‘,‘nb‘) # key值不存在就新增
?
conn.close()

Redis的hash操作

Hash操作,redis中Hash在内存中的存储格式如下图:

[Redis内存数据库]
‘‘‘
hset(name, key, value)   重点
hmset(name, mapping)   重点
hget(name,key)        重点
hmget(name, keys, *args)  重点
hgetall(name)
hlen(name)              重点
hkeys(name)
hvals(name)
hexists(name, key)
hdel(name,*keys)
hincrby(name, key, amount=1)   重点
hincrbyfloat(name, key, amount=1.0)
hscan(name, cursor=0, match=None, count=None)
hscan_iter(name, match=None, count=None)
‘‘‘
--------------------------------------------------------------------------------
from redis import Redis
?
conn = Redis()
?
1 hset(key, field, value):将hash表 key 中的字段 field 的值设为 value 。(不存在,则创建;否则,修改)
# conn.hset(‘hash_test‘,‘name‘,‘lqz‘)
# conn.hset(‘hash_test‘,‘age‘,‘19‘)
?

2 hmset(key, {field1, value1, field2,value2}):同时将多个 field-value (域-值)对设置到hash表 key 中。
# conn.hmset(‘hash_test1‘,{‘name‘:‘egon‘,‘age‘:18}) # 报错被弃用了,但是还能正常插入值,原生未被弃用
?

3 hget(key, field):获取存储在hash表中指定字段的值。
# print(conn.hget(‘hash_test1‘,‘name‘))
?

4 hmget(key, [field1, field2]):获取所有给定字段的值
# print(conn.hmget(‘hash_test1‘,[‘name‘,‘age‘]))
?

5 hgetall(key):获取在hash表中指定key的所有字段和值。
# print(conn.hgetall(‘hash_test1‘))  # 尽量少执行,慢长命令,可能会撑爆应用程序的内容
?

6 hlen(key):获取hash中key对应的字段的个数
# print(conn.hlen(‘hash_test1‘))
?

7 hkeys(key):# 获取所有hash表中的字段
# print(conn.hkeys(‘hash_test1‘))
?

8 hvals(key):获取hash中key对应所有的value值
# print(conn.hvals(‘hash_test1‘))
?

9 hexists(key, field):查看hash表 key 中,指定的字段是否存在。
# print(conn.hexists(‘hash_test1‘,‘hobby‘)) # False
# print(conn.hexists(‘hash_test1‘,‘name‘)) # True
?

10 hdel(key ,field1,field2):删除一个或多个hash表key中指定的字段
# conn.hdel(‘hash_test1‘,‘name‘,‘wife‘)
?
# 将key对应的hash中指定字段的键值对删除
# print(re.hdel(‘xxx‘ ,‘sex‘ ,‘name‘))
?

11 hincrby(key, field, amount=1):为hash表key中的指定字段field的整数值加上增量
conn.hincrby(‘hash_test1‘,‘age‘,amount=10)
?"""
自增key对应的hash中的指定字段的值,不存在则创建key=amount参数:key, hash对应的key
name,hash中key对应的字段amount,自增数(整数)
"""


12 hincrbyfloat(key, field, amount=1.0):为hash表key中的指定字段的浮点数值加上增量
# conn.hincrbyfloat(‘hash_test1‘,‘age‘,1.2)  # 存在精度损失问题
?

13 hscan(key, cursor=0, match=None, count=None):迭代hash表中的键值对。
"""
curso:游标,表示从哪个位置开始取match:匹配的字段,不写表示所有count:要取的条数
"""

# 存数据进去,在hash表key中加1000行键值对数据
# for i in range(1000):
#     conn.hset(‘test‘,‘%s-%s‘%(‘key‘,i),i)
?
# res=conn.hscan(‘test‘,cursor=0,count=100)
# print(res) # 从0开始取,取出的100条数据是无序的
# print(len(res[1])) # 打印条数
?
# 下面取到的跟上面的不会有重复,会根据上次的游标位置开始取
# res=conn.hscan(‘test‘,cursor=res[0],count=100)
# print(res)
# print(len(res[1]))
?

14 hscan_iter(name, match=None, count=None) # 获取所有采取这种方法
?
# hgetall---》一次性全取出来   ----》生成器用的位置
for item in conn.hscan_iter(‘test‘,count=10): # 取出全部的value值,但是每次取10个
    print(item)
"""
生成器用在了什么位置?
在取redis的hash类型的时候,因为hash类型的值很多,所以我没有用hgetall,
自己写了一个生成器,来分片取
"""
conn.close()

Redis的列表(list)操作

List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:

[Redis内存数据库]
‘‘‘

lpush(name,values)   #从左侧插入值
lpushx(name,value)
llen(name)           # 重点
linsert(name, where, refvalue, value))  # 重点
lset(name, index, value)
lrem(name, value, num)        # 删除值
lpop(name)             #从左侧弹出
lindex(name, index)
lrange(name, start, end)
ltrim(name, start, end)
rpoplpush(src, dst)
blpop(keys, timeout)          # 阻塞式弹出
brpoplpush(src, dst, timeout=0)
‘‘‘
--------------------------------------------------------------------
from redis import Redis
?
conn = Redis()
1 lpush(key ,value):将一个或多个值插入到列表头部
# conn.lpush(‘list_test‘, ‘egon‘)
# conn.lpush(‘list_test‘, ‘lqz‘)
?
2 rpush(key ,value):将一个或多个值添加到列表尾部
# conn.rpush(‘list_test‘, ‘ylf‘, ‘szw‘)
?
3 lpushx(key ,value):在key对应的list中添加元素,只有key已经存在时,值添加到列表的最左边
# conn.lpushx(‘yyyyy‘, ‘egon‘)  # key不存在,无法添加
# conn.lpushx(‘list_test‘, ‘egon‘)  # 成功添加到列表头部,列表可重复
?
4 llen(key):key对应的list元素的个数
# print(conn.llen(‘list_test‘))
?
5 linsert(key, where, refvalue, value)):在key对应的列表的某一个元素前或者后插入元素
# conn.linsert(‘list_test‘,‘before‘,‘lqz‘,‘dlrb‘) # 在lqz前面插入dlrb
# conn.linsert(‘list_test‘,‘after‘,‘lqz‘,‘jason‘) # 在lqz后面插入jason
"""
参数:
key,redis的key
where,BEFORE(前)或AFTER(后)(小写也可以)
refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
value,要插入的数据
"""
?
6 lset(key, index, value):将key对应的list中的某一个索引位置重新赋值
# conn.lset(‘list_test‘,0,‘xxx‘) # 将0这个位置的值改为xxx
"""
参数:
key,redis的key
index,list的索引位置
value,要设置的值
"""
?
7 lrem(key,count, value):在key对应的list中删除指定的值
# conn.lrem(‘list_test‘, count=1, value=‘lqz‘)
"""
参数:
key,redis的key
value,要删除的值
count,  count=0,删除列表中所有的指定值;
count=2,从前到后,删除2个;
count=-2,从后向前,删除2个
"""
?
8 lpop(key):将key对应的list的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
# print(conn.lpop(‘list_test‘))
?
9 rpop(key):将key对应的list的右侧获取最后一个元素并在列表中移除,返回值为移除的元素
# print(conn.rpop(‘list_test‘))
?
10 lindex(key, index):在key对应的列表中根据索引获取列表元素
# print(conn.lindex(‘list_test‘,3)) # 从0开始取
?
11 lrange(key, start, stop):在key对应的列表分片获取元素(指定范围内)
# print(conn.lrange(‘list_test‘, 0, 2))
"""
参数:
key,redis的key
start,索引的起始位置
stop,索引结束位置  
"""
?
12 ltrim(key, start, stop):将key对应的列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
# conn.ltrim(‘list_test‘,2,4)   # 保留2-4之间的
"""
参数:
key,redis的key
start,索引的起始位置
stop,索引结束位置(大于列表长度,则代表不移除任何)
"""
?
13 rpoplpush(src, dst):从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;
# 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
"""
从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
参数:
src,要取数据的列表的name
dst,要添加数据的列表的name
"""
?
###记住
?
14 blpop(keys, timeout):移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
# print(conn.blpop(‘list_test‘))
?
?
15 brpop(keys, timeout):移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
# print(conn.brpop(‘list_test‘))

Redis其他操作

‘‘‘

delete(*names)
# 根据删除redis中的任意数据类型

exists(name)
# 检测redis的name是否存在

keys(pattern=‘*‘)
# 根据模型获取redis的name

expire(name ,time)
# 为某个redis的某个name设置超时时间

rename(src, dst)
# 对redis的name重命名为

move(name, db))
# 将redis的某个值移动到指定的db下

randomkey()
# 随机获取一个redis的name(不删除)

type(name)
#查看类型
‘‘‘
---------------------------------------------------------------------------------
from redis import Redis
?
conn=Redis()
?
1 delete(key):该命令用于key存在时删除key,可以删除一个或多个。
# conn.delete(‘name‘,‘age‘,‘test‘)
?
2 exists(key):检查指定key是否存在。
# res=conn.exists(‘hash_test‘,‘hash_test1‘)
# print(res) # 返回2,基本上是判断一个,1代表在,0代表不在
?
3 keys(pattern=‘*‘):查找所有符合给定模式(pattern)的 key 。
# res=conn.keys(‘h?sh_test‘)  # 慎用
# res=conn.keys(‘hash_*‘)  # 慎用
# print(res)
"""
更多:
KEYS * 匹配数据库中所有 key 。
KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
KEYS h*llo 匹配 hllo 和 heeeeello 等。
KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
"""
?
4 expire(name, time):指定某个key设置过期时间,以秒为单位。
# conn.expire(‘hash_test‘,5) # 五秒过后自动销毁
?
?
5 rename(src, dst):修改key的名称(对redis的key重命名)
# conn.rename(‘hash_test1‘,‘xxx‘)
?
?
6 move(key, db)):将当前数据库的key移动到指定的数据库db当中。
# conn.move(‘xxx‘,1)  # 将db0库中的xxx移动到db1库下
?
?
7 randomkey():从当前数据库中随机返回一个key(不删除)。
# print(conn.randomkey())
?
?
8 type(key):返回key所储存的值的类型。
# print(conn.type(‘name‘))  # b‘string‘
# print(conn.type(‘list‘))  # b‘list‘

管道

"""1 非关系型数据库,本身不支持事务2 redis中的管道可以实现事务的支持(要么都成功,要么都失败)    -实现的原理:多条命令放到一个管道中,一次性执行3 具体代码4 如果是集群环境,不支持管道,只有单机才支持?redis当中如何实现事务?  使用redis管道,具体实现:开启管道,把命令放进去  调用execute依次执行管道中的所有命令,它的原理就  是要么一次性都执行,要么都不执行,保证了事务 """?import redis?pool = redis.ConnectionPool()?r = redis.Redis(connection_pool=pool)?pipe = r.pipeline(transaction=True)  # 开启事务?pipe.multi()    # 管道等待放入多条命令pipe.set(‘name‘, ‘egon‘)pipe.set(‘role‘, ‘admin‘)?# 到此,命令都没有执行pipe.execute()  # 执行管道中的所有命令?

django中使用redis

方式一(通用方式,任何框架都可以用)

# 写一个pool连接池from redis import ConnectionPoolPOOL = ConnectionPool(max_connections=5, host=‘127.0.0.1‘, port=6379)?# 在使用的位置,获取连接,获取数据from luffyapi.libs.redis_pool import POOLfrom redis import Redisconn = Redis(connection_pool=POOL)name = conn.get(‘name‘)

第二种:django框架专用

1 安装 pip install django-redis2 在配置文件中配置CACHES = {    "default": {        "BACKEND": "django_redis.cache.RedisCache",        "LOCATION": "redis://127.0.0.1:6379",        "OPTIONS": {            "CLIENT_CLASS": "django_redis.client.DefaultClient",  #这句指的是django中的缓存也缓存到redis中了            "CONNECTION_POOL_KWARGS": {"max_connections": 100}   #连接池的大小            # "PASSWORD": "123",        }    }}?3 在使用的位置conn=get_redis_connection()  # 从连接池中取一个连接name = conn.get(‘name‘)?# 这个好,他可以缓存任意对象from django.core.cache import cachecache.set(‘user‘,user)cache.get()?4 以后django中都用这种,好处是django中的缓存也会放到redis中

接口缓存

# 以首页轮播图为例,加入缓存,后期所有接口都可使用缓存# 用户来了,先去缓存中看,如果有直接返回,如果没有,去mysql中查,查完以后在缓存中放一份# 双写一致性:处理方案有三种    -只要插入数据,就删除缓存from django.shortcuts import render?# 轮播图接口from rest_framework.viewsets import ViewSetMixinfrom rest_framework.generics import ListAPIViewfrom .serializer import BannerSerializerfrom .models import Bannderfrom django.conf import settingsfrom django.core.cache import cachefrom rest_framework.response import Response??class BannerView(ViewSetMixin, ListAPIView):    queryset = Bannder.objects.all().filter(is_show=True, is_delete=False).order_by(‘-orders‘)[0:settings.BANNER_SIZE]    serializer_class = BannerSerializer?    # 轮播图加入缓存,重写list,先去缓存中查,如果有直接返回,没有再去数据库查    def list(self, request, *args, **kwargs):        banner_list = cache.get(‘banner_cache_list‘)  # 有就不会往下走        if not banner_list:  # 没有值,走它,去数据库中查            response = super().list(request, *args, **kwargs)            # 查到以后在缓存中放一份,data就是返回的数据,放data就可以            cache.set(‘banner_cache_list‘, response.data)        else:            # 走了缓存,速度很快            print(‘走了缓存‘)            response = Response(data=banner_list)        return response

[Redis内存数据库]

[Redis内存数据库]

上一篇:web api使用SignalR


下一篇:jmeter测试基于SignalR的Websocket过程中的一些总结