缓存数据库redis 及各种value数据类型的使用

Redis介绍与安装

老刘博客
https://www.cnblogs.com/liuqingzheng/articles/17146235.html
https://www.cnblogs.com/liuqingzheng/articles/9833534.html

redis:缓存数据库(大部分时间做缓存,不仅仅可以做缓存),非关系型数据库【区别与mysql关系型数据库】
   nosql :非关系型数据库
   c语言写的 服务(监听端口),用来存储数据的,数据是存储在内存中 取值,放值速度非常块,10w qps

面试题:redis为什么这么快
  1 纯内存操作
  2 网络模型使用的IO多路复用(epoll)(可以处理的请求数更多)
  3  6.x之前,单进程,单线程架构,没有线程进程间切换,更少的消耗资源
  
key-value 形式存储,没有表的概念


   
区别:
"""
redis: 内存数据库(读写快)、非关系型(操作数据方便、数据固定)
mysql: 硬盘数据库(数据持久化)、关系型(操作数据间关系、可以不同组合)

大量访问的临时数据,才有redis数据库更优
"""
"""
1、官网下载:安装包或是绿色面安装
2、安装并配置环境变量

# 官网:https://redis.io/
     下载完是源代码:c语言源码 :https://redis.io/download/#redis-stack-downloads
     最稳定:6.x
     最新7.x
     公司里 5.x比较多
# 中文网:http://redis.cn/download.html
      上面最新只到5.x
    
# 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自动加入到服务中
   redis-server    # mysqld 服务端的启动命令
   redis-cli       # mysql  客户端的启动命令
安装目录
    redis-server
    redis-cli
    redis.windows-service.conf  配置文件
       bind  127.0.0.1  # 服务,跑在的地址
       port  6379       # 监听的端口

启动redis 
   方式一:
      在服务中,点击启动,后台启动
   方式二:使用命令
       redis-server  指定配置文件,如果不指定,会默认

客户端连接redis
    1 方式一:
        cmd输 redis-cli  # 默认连接本地6379端口
    2 方式二:
        redis-cli -h 地址 -p 端口

     3 使用图形化界面操作 
       Redis Desktop Manager :开源的,原来免费,后来收费了  推荐用(mac,win,linux 都有)
             Qt5  qt是个平台,专门用来做图形化界面的 
             可以使用c++写
             可以使用python写  pyqt5  使用python写图形化界面 (少量公司再用)
             resp-2022.1.0.0.exe 一路下一步,安装完启动起来
        Redis Client  小众
        
        图形化界面,连接redis 输入地址和端口,点击连接即可

# redis默认有16个库,默认连进去就是第0个

Redis普通连接和连接池

python  相当于客户端,操作redis
安装模块:pip install redis

补充:django 中操作mysql 没有连接池,一个请求就是一个mysql连接
    可能有问题,并发数过高,导致mysql连接数过高,影响mysql性能
    使用django连接池:下载模块django-redis
    https://blog.51cto.com/liangdongchang/5140039

普通连接

安装redis模块:pip install redis

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()

连接池连接

pool.py

import redis
POOL=redis.ConnectionPool(max_connections=10,host='127.0.0.1',port=6379)  # 创建一个大小为10的redis连接池

测试代码
import redis
from threading import Thread
from pool import POOL
# 做成模块后,导入,无论导入多少次,导入的都是那一个POOL对象
def task():
   conn= redis.Redis(connection_pool=POOL) # 报错的原因是拿连接,池里不够了,没有等待,线程报错设置等待,参数
   print(conn.get('name'))

for i in range(100):
   t=Thread(target=task) # 每次都是一个新的连接,会导致的连接数过多
   t.start()
   

单例模式:设计模式  23种设计模式
   全局只有一个这个对象
   p1=Person()  # p1 对象
   p2=Person()  # p2 新对象
   
   单例模式的6种方式
      1模块导入
        。。。。。

Redis之字符串类型

redis 是key-value形式存储
redis 数据放在内存中,如果断电,数据丢失--》需要持久化的方案

5 种数据类型,value类型
   字符串:用的最多,做缓存,做计数器
   列表:简单的消息队列
   字典(hash):缓存
   集合:去重
   有序集合:排行榜

字符串类型使用


import redis

conn = redis.Redis()

# 1 set(name, value, ex=None, px=None, nx=False, xx=False)
# ex,过期时间(秒)
# px,过期时间(毫秒)
# nx,如果设置为True,则只有name不存在时,当前set操作才执行, 值存在,就修改不了,执行没效果
# xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
# conn.set('hobby','篮球1',ex=3)
# conn.set('hobby','篮球1',px=3)
# conn.set('hobby', 'lqz', nx=True)
# conn.set('name', 'lqz', xx=True)

# redis --》实现分布式锁 底层基于nx实现的

# setnx(name,value) 值存在修改,不存在不操作
# 等同于:conn.set('name','lqz',nx=True)
# conn.setnx('wife','刘亦菲')

# 3 setex(name,value,time) # 秒消失,时间要写在中间
# 等同于:conn.set('name','lqz',ex=3)
# conn.setex('wife', 3, '刘亦菲')

# 4 psetex(name,time_ms,value) # 毫秒消失
# conn.psetex('wife',3000,'刘亦菲')

# 5 mset(*args,**kwargs) 批量添加
# conn.mset({'wife': '刘亦菲', 'hobby': '篮球'})

# 6 get(name) 获取
# print(str(conn.get('wife'), encoding='utf-8'))

# 7 mget(keys,*args) 批量获取
# res = conn.mget('wife', 'hobby')
# res = conn.mget(['wife', 'hobby'])
# print(res)

# 8 getset(name,value)  
# res = str(conn.getset('wife', '迪丽热巴'), encoding='utf-8')
# res=conn.getset('wife','迪丽热巴')
# print(res)

# 9 getrange(key, start, end) 
# res = str(conn.getrange('wife', 0, 2), encoding='utf-8')  # 字节长度,不是字符长度  前闭后闭区间
# print(res)

# 10 setrange(name, offset, value)
# conn.setrange('wife',2,'bbb')


# ---- 比特位---操作
# 11 setbit(name, offset, value)
# 12 getbit(name, offset)
# 13 bitcount(key, start=None, end=None)
# ---- 比特位---操作

# 14 bitop(operation,dest,*keys)   获取多个值,并将做位运算,将最后的结果保存至新的name对应的值

# 15 strlen(name) 
# res = conn.strlen('hobby')  # 统计字节长度
# print(res)


# 16 incr(self,name,amount=1)
# 自增,不会出并发安全问,单线程架构,并发量高
# conn.incr('age')
# incrby
# 17 incrbyfloat(self,name,amount=1.0)
# conn.incrbyfloat('age',1.2)

# 18 decr(self,name,amount=1) 自减
# conn.decrby('age')
# conn.decrby('age',-2)

# 19 append(key,value) 添加
# conn.append('hobby','sb')
# print(conn.strlen('hobby'))


# conn.close()

'''
需要记住的
set
get
strlen  字节长度
incr

'''

redis列表

'''
1 lpush(name, values)
2 rpush(name, values) 表示从右向左操作
3 lpushx(name, value)
4 rpushx(name, value) 表示从右向左操作
5 llen(name)
6 linsert(name, where, refvalue, value))
7 r.lset(name, index, value)
8 r.lrem(name, value, num)
9 lpop(name)
10 rpop(name) 表示从右向左操作
11 lindex(name, index)
12 lrange(name, start, end)
13 ltrim(name, start, end)
14 rpoplpush(src, dst)
15 blpop(keys, timeout)
16 r.brpop(keys, timeout),从右向左获取数据
17 brpoplpush(src, dst, timeout=0)
'''


"""
长风破浪会有时 直挂云帆济沧海
@time: 2023/3/8 15:01
"""
import redis

conn = redis.Redis()

# 1 lpush(name,value)  从 左侧插入
# conn.lpush('girls','刘亦菲','迪丽热巴')
# conn.lpush('girls','周淑怡')
# 2 rpush(name,value) 表示从右往左操作
# conn.rpush('girls', '小红')

# 3 lpushx(name,value)  # 有就增加,没有就不操作
# conn.lpushx('boys','刚')
# conn.lpushx('boys','小刚')
# conn.lpush('boys','刚')

# 4 rpushx(name,value) 表示从右往左操作

# 5 llen(name)  统计列表长度
# res = conn.llen('girls')
# print(res)

# 6 linsert(name,where,refvalue,value)
# conn.linsert('girls','before','迪丽热巴','古力娜扎')  # 以第一个值为标杆,在它之前插入值
# conn.linsert('girls','after','古力娜扎','刚') # 以第一个值为标杆,在它之后插入值
# conn.linsert('girls','after','小黑','嘿嘿嘿')  # 没有标杆,插不进去

# 7 r.lset(name,index,value) # 按索引位置改值
# conn.lset('girls', 1, 'xxx')

# 8 r.lrem(name,value,num)
# conn.lrem('girls', 1, 'xxx')  # 从左侧开始  删除一个相同值       多个的话,一次只删一个
# conn.lrem('girls',-1,'xxx') # 从右侧开始  删除一个相同值    多个的话,一次只删一个
# conn.lrem('girls',0,'xxx') # 把相同的值全删

#  9 lpop(name)  # 从左侧开始弹出
# res=conn.lpop('girls')
# print(res)

# 10 rpop(name)  从右侧开始弹出

# 11 lindex(name,index)  索引取值
# res = str(conn.lindex('girls',1),encoding='utf-8')
# print(res)
# 12 lrange(name,start,end) 从左侧开始索引取值
# res = conn.lrange('girls',0,2)  # 前闭后闭区间
# print(res)

# 13  ltrim(name,start,end)  按索引 切割取值

# conn.ltrim('girls',2,3)


# 14 rpoplpush(src,dst)

# 15 blpop(keys,timeout)  # 可以做消息队列使用  阻塞式弹出,如果没有,就阻塞
# res= conn.blpop('boys')
# print(res)

# 16 r.brpop(keys,timeout)  从右往左获取数据
# 17 brpoplpush(src,dst,timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
 
# 参数:
    # src,取出并要移除元素的列表对应的name
    # dst,要插入元素的列表对应的name
    # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
conn.close()

'''
lpush
lpop
llen
lrange
'''

redis之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)

'''
import redis
conn = redis.Redis()

# 1 hset(name,key,value) 添加值
# conn.hset('userinfo','name','lqz')
# conn.hset('userinfo',mapping={'age':19,'hobby':'篮球'})

# 2 hmset(name,mapping) # 批量设置,被启用后都使用hset
# conn.hmset('userinfo2',{'age':19,'hobby':'篮球'})

# 3 hget(name,key)  获取值
# res=conn.hget('userinfo','name')
# print(res)

# 4 hmget(name,keys,*args) 拿部分值
# res=conn.hmget('userinfo',['name','age'])
# res=conn.hmget('userinfo','name','age')
# print(res)


# 5 hgetall(name)  # 慎用  拿所有值
# res = conn.hgetall('userinfo')
# print(res)

# 6 hlen(name)  获取key长度
# res=conn.hlen('userinfo')
# print(res)


# 7 hkeys(name)  获取所有的key值
# res=conn.hkeys('userinfo')
# print(res)

# 8 hvals(name)   获取所有的value值
# res=conn.hvals('userinfo')
# print(res)


# 9 hexists(name,key)  判断是否有值,  有值为True 没有为False
# res = conn.hexists('userinfo','name')
# res = conn.hexists('userinfo','name1')
# print(res)
# 10 hdel(name,*keys)   有值显示1 无值显示0
# res = conn.hdel('userinfo','age1')
# print(res)

# 11 hincrby(name,key,amount=1)  自增
# conn.hincrby('userinfo','age',2)

# 12 hincrbyfloat(name,key,amount=1.0)

# hgetall  会一次性全取出,效率低,可以能占内存很多
# 分批获取,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条数据)   数字是下一个游标位置

# 咱们用这个,它内部用了hscan,等同于hgetall 所有数据都拿出来,count的作用是,生成器,每次拿count个个数
# 14 hscan_iter(name, match=None, count=None)
res=conn.hscan_iter('hash_test',count=10)
# print(res)  # generator 只要函数中有yield关键字,这个函数执行的结果就是生成器 ,生成器就是迭代器,可以被for循环
# for i in res:
#     print(i)

'''
hset
hget
hmget
hlen
hdel
hscan_iter  获取所有值,但是省内存 等同于hgetall
'''

conn.close()

redis其他操作

'''
通用操作,不指定类型,所有类型都支持
1 delete(*names)
2 exists(name)
3 keys(pattern='*')
4 expire(name ,time)
5 rename(src, dst)
6 move(name, db))
7 randomkey()
8 type(name)
'''
import redis

conn = redis.Redis()
# 1 delete(*names)  删除值
# conn.delete('name', 'userinfo2')
# conn.delete(['name', 'userinfo2'])  # 不能用它
# conn.delete(*['name', 'userinfo2'])  # 可以用它

# 2 exists(name) 判断值是否存在 返回0 或1
# res=conn.exists('userinfo')
# print(res)

# 3 keys(pattern='*')   匹配拿值
# res=conn.keys('w?e')  #  ?表示一个字符,   * 表示多个字符
# print(res)
# 4 expire(name ,time)  3秒钟删除
# conn.expire('userinfo',3)

# 5 rename(src, dst) key值改名
# conn.rename('hobby','hobby111')
# 6 move(name, db))  移动 移到其他库上
# conn.move('hobby111',8)
# 7 randomkey()  随机弹出key值
# res=conn.randomkey()
# print(res)
# 8 type(name)  看类型
# print(conn.type('girls'))
print(conn.type('age'))

conn.close()

管道

# 事务---》四大特性:
     原子性
     一致性
     隔离性
     持久性

# redis支持事务吗   单实例才支持所谓的事物,支持事务是基于管道的
    执行命令  一条一条执行
        张三 金额 -100    conn.decr('zhangsan_je',100)
        挂了
         你   金额 100     conn.incr('李四_je',100)

     把这两条命令,放到一个管道中,先不执行,执行excute,一次性都执行完成
     conn.decr('zhangsan_je',100)   conn.incr('李四_je',100)


# 如何使用
import redis
conn = redis.Redis()
p=conn.pipeline(transaction=True)
p.multi()
p.decr('zhangsan_je', 100)
# raise Exception('崩了')
p.incr('lisi_je', 100)

p.execute()
conn.close()

django中使用redis

## 方式一:自定义包方案(通用的,不针对于框架,所有的框架都可以用)
   第一步:写一个pool.py
    import redis
    POOL = redis.ConnectionPool(max_connections=100)

    第二步:以后在使用的地方,直接导入使用即可
    需要配置路由
    path('test_redis/', views.test_redis),
    视图函数
    from utils.pool import POOL
from django_redis import get_redis_connection
###  django中使用redis
def test_redis(request):
    conn = redis.Redis(connection_pool=POOL)
    conn.incr('count')
    res=conn.get('count')

    return JsonResponse({'count':'打印一次加1  数量:%s'% res},json_dumps_params={'ensure_ascii':False} )


## 方式二:django方案,
  方案一:django的缓存使用redis  【推荐使用】
      settings.py 中配置
      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",
                }
            }
        }

    在使用redis的地方:from django.core.cache import cache
###  django中使用redis
def test_redis(request):
    res = int(cache.get('count'))
    cache.set('count',res+1) # 需要先放 和redis里面的count不冲突

    return JsonResponse({'count':'打印一次加1  数量:%s'%res},json_dumps_params={'ensure_ascii':False} )

    pickle序列化后,存入的
  
  方案二:第三方:django-redis模块
     from django_redis import get_redis_connection
def test_redis(request):
    conn=get_redis_connection()
    print(conn.get('count'))
    return JsonResponse({'count':'打印一次加1  数量:%s'},json_dumps_params={'ensure_ascii':False} )

     进程:资源分配的最小单位,一个程序运行起来,可能一个进程,也可能多个进程
     线程:cpu调度的最小单位,遇到io操作,操作系统层面切换
     协程:单线程下的并发,程序层面控制,遇到io操作,切换到别的任务执行

posted @ 2023-03-07 20:16  缀月  阅读(152)  评论(0编辑  收藏  举报