redis

发布时间 2023-11-13 16:12:26作者: Meeeoww

介绍

# 1 redis 什么
	-数据库就是个存数据的地方:只是不同数据库数据组织,存放形式不一样
	-mysql  关系型数据库(oracle,sqlserver,postgrasql)
    -非关系型数据(no sql):redis,mongodb,clickhouse,infludb,elasticsearch,hadoop。。。
    	-没有sql:没有sql语句
        -not olny sql 不仅仅是sql 
    -redis:一款纯内存存储的非关系型数据库(数据都在内存),速度非常快
    
# 2 redis特点:https://www.cnblogs.com/liuqingzheng/articles/9833534.html
	-redis是一个key-value存储系统
    -数据类型丰富,支持5大数据类型:字符串,列表,hash(字典),集合,有序集合
    -速度快:单实例redis 10w qps
    -纯内存操作
    -可以持久化:能都把内存数据,保存到硬盘上永久存储
    
    
# 3 redis为什么这快
	-1 纯内存,减少io
    -2 使用了 io多路复用的 epoll 网络模型
    -3 数据操作是单线程,避免了线程间切换
    	-多个客户端同时操作,不会存在并发安全问题

安装

	-redis:最新是7,  公司里5,6比较多
	-redis:开源软件,免费的,他们不支持win
    	-epoll模型不支持win
    -微软官方:基于源码修改---》编译成可执行文件
    -第三方:https://github.com/tporadowski/redis/releases/
        
    -win:下载安装包,一路下一步
    	-安装目录在环境变量中:任意路径敲 redis-server   reidis-cli 都能找到
        -把redis做成了服务,以后通过服务启动即可
        
    -mac:官网下载,解压即可
    
    
    -win,mac:两个可执行文件:
        redis-server   :等同于 mysqld
        reidis-cli     :等同于mysql



####卸载redis
# 1、查看redis进程;
ps aux|grep redis
# 2、kill掉进程;
kill 进程id
# 3、进入到redis目录
cd /usr/local/
# 4、删除redis对应的文件
rm -f /usr/local/redis/bin/redis*
rm -f /usr/local/bin/redis*
# 5、删除对应的文件
rm -rf redis

使用

启动及链接

启动

-使用服务启动
任务管理器>服务>Redis开始
redis-server redis.windows-service.conf

-使用命令启动	
redis-server
ps -ef|grep redis  #查看进程
netstat -antpl|grep redis #查看端口
redis-cli -h ip -p port ping #命令查看

链接

###客户端连接###
redis-cli -h 127.0.0.1 -p 6379
ping #返回PONG

## 有密码的情况可以两种登陆方式
# 方式一
redis-cli -h 127.0.0.1    -p 6370 -a 123456
# 方式二
先登陆,再通过auth输入密码

## redis-cli进入
CONFIG GET *   一百多对建值
CONFIG SET maxmemory 128M  # 设置最大使用的内存
CONFIG set requirepass 123456  # 设置密码
CONFIG REWRITE  # 保存到配置文件

redis普通连接和连接池

#1  python 代码作为客户端---》连接

# 2 安装模块:pip install redis
使用Python读取redis数据的时候,取出来的数据带有'b',是bytes类型的:b'\xe6\x9b\xb9\xe7\x8e\xb2'
解决办法,在连接redis数据库时,多加个参数:decode_responses=True

普通连接

from redis import Redis

conn = Redis(host="localhost",port=6379,db=0,decode_responses=True)
"""参数db不写默认链接0,链接其他修改db即可"""
res=conn.get('name')

print(res) #你好

conn.close()

连接池

  • scripts/pool.py
#一定要保证,池是单例的,以模块导入的形式做成了单例,整个项目中,只有这一个实例(对象)
# 将POOL做成一个单例,这样的话可以全局使用
import redis
POOL=redis.ConnectionPool(host='127.0.0.1',port=6379,max_connections=10,decode_responses=True)
  • scripts/redis_test.py
from redis import Redis
from threading import Thread

from pool import POOL # 模块导入实现单例方式,不能相对导入
def task():
    conn = Redis(connection_pool=POOL)
    print(conn.get('name'))
    conn.close()


if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task)
        t.start()


redis数据类型

redis字符串类型

'''
1 set(name, value, ex=None, px=None, nx=False, xx=False)
2 setnx(name, value)
3 setex(name, value, time)
4 psetex(name, time_ms, value)
5 mset(*args, **kwargs)
6 get(name)
7 mget(keys, *args)
8 getset(name, value)
9 getrange(key, start, end)
10 setrange(name, offset, value)
11 setbit(name, offset, value)
12 getbit(name, offset)
13 bitcount(key, start=None, end=None)
14 bitop(operation, dest, *keys)
15 strlen(name)
16 incr(self, name, amount=1)
# incrby
17 incrbyfloat(self, name, amount=1.0)
18 decr(self, name, amount=1)
19 append(key, value)
'''
import redis

conn = redis.Redis(decode_responses=True)
1 set(name, value, ex=None, px=None, nx=False, xx=False)
conn.set('age','19') # 没有就新增,有值就修改
conn.set('hobby','篮球',ex=5) # ex过期时间 秒
conn.set('hobby','篮球',px=5000)# 过期时间,毫秒
conn.set('name','彭于晏')
conn.set('name', 'lqz', nx=True)  # nx如果设置为True,则只有name不存在时,当前set操作才执行值修改;name存在,就修改不了
conn.set('name', 'lqz', xx=True)  # xx如果设置为True,则只有name存在时,当前set操作才执行值修改;值不存在,不会设置新值


2 setnx(name, value) #name不存在时才设置(新增操作)
conn.setnx('name','彭于晏')

3 setex(name, time, value) #设置秒数过期的
conn.setex('name',5,'sdasdfs')

4 psetex(name, time_ms, value)#设置毫秒过期
conn.psetex('name',5000,'lqz')


5 mset(*args, **kwargs)  批量设置
conn.mset({'name': 'lqz', 'age': 29, 'gender': '男'})

6 get(name)
print(conn.get('name'))

7 mget(keys, *args) # 批量获取值
print(conn.mget(['name','age']))
print(conn.mget('name','age','gender'))

8 getset(name, value)  # 等同于  get   set
print(conn.getset('name','oooo'))

9 getrange(key, start, end) #区间取值
print(conn.getrange('name',1,3))  # 前闭后闭区间

10 setrange(name, offset, value)
conn.setrange('name',2,'ooo') # 包含2

# 先不聊---》操作比特位---》后面聊
11 setbit(name, offset, value)
conn.setbit('name',7,1)  # l=[1 1 0 0 0 1 0 0 ]
12 getbit(name, offset)
13 bitcount(key, start=None, end=None)
14 bitop(operation, dest, *keys)


15 strlen(name) #计算字节长度
print(conn.strlen('name'))

16 incr(self, name, amount=1) #对value值自增整数
conn.incr('age',2) # 自加2,默认自增1,单线程,没有并发安全,数据不会错乱,天然适合计数器  计数器---》日活(日活跃用户数,只要有用户登录,就+1)

17 incrby(self, name, amount=1.0)
conn.incrby(age , 10)  #对age这个key的value值增加10

18 incrbyfloat(self, name, amount=1.0) #对value值自增小数
conn.incrbyfloat('age',1.1)

19 decr(self, name, amount=1) #对value值自减1
conn.decr('age')

20 decrby(self, name, amount=1)
conn.decrby(age, 10) #对age这个key的value值减10

21 append(key, value) #将value追加
conn.append('name','ooo')
conn.close()


'''常用
set
get
getrange
strlen
'''

redis hash类型

'''  hash 类型,就是咱们python中的字典类型, 数据结构:数据的组织形式  底层存储 数组---》根据key值使用hash函数得到结构,存到数组中
    字典的key值必须可hash
    字典的key值必须是不可变数据类型
    hash 类型无序,跟放的先后顺序无关的
    python 的字典是 有序的  字典+列表
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)
'''


    def hscan_iter(self,name,match= None,count= None):
        cursor = "0"
        while cursor != 0:
            cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
            yield from data.items()


# print(hash(1))
# print(hash('asdfasdf'))
# # print(hash([1,2,3,])) # unhashable
# print(hash((1,2,3))) # 不可变数据类型
# a={(1,2,3):8}
# print(a[(1,2,3)])



# class Person(object):
#     pass
# p=Person()
#
# a={p:'asdf'}
# print(a[p])


import redis

conn = redis.Redis(decode_responses=True)
1 hset(name, key, value) #设值
conn.hset('userinfo','name','lqz')
conn.hset('userinfo','age','19')

2 hset(name, mapping)  #批量放入
conn.hset('userinfo3',mapping={'name':'xxx',"age":33})


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

4 hmget(name, keys, *args) #批量取值
print(conn.hmget('userinfo',['name','age']))
print(conn.hmget('userinfo','name','age'))


5 hgetall(name)  # 返回hash key 对应的所有field和value,慎用对应的value值非常多,一次性拿出来,很耗时
print(conn.hgetall('userinfo'))

6 hlen(name) #获取hash key field的数量
print(conn.hlen('userinfo'))

7 hkeys(name) #返回hash key对应的所有field
print(conn.hkeys('userinfo'))

8 hvals(name)  #返回hash key 对应的所有field的value
print(conn.hvals('userinfo'))

9 hexists(name, key) #判断hash key 是否存在field 
print(conn.hexists('userinfo','hobby'))


10 hdel(name,*keys) #删除hash key对应的field的值
conn.hdel('userinfo',['name','age'])
conn.hdel('userinfo','name','age')

11 hincrby(name, key, amount=1) #hash key 对应的field的value自增
conn.hincrby('userinfo2','age')

12 hincrbyfloat(name, key, amount=1.0) #hash key 对应的field的value自增小数


13 hscan(name, cursor=0, match=None, count=None)  # 增量式迭代获取,对于数据大的数据非常有用,数据量小时会全部获取,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
"""
# 参数:
    name,redis的name
    cursor,游标(基于游标分批取获取数据的位置)
    match,模糊匹配指定key,默认None 表示所有的key
    count,指定每次返回数量,默认None表示采用Redis的默认数为10
"""
## 造数据
for i in range(1000):
   conn.hset('hash2','egg_%s'%i,'鸡蛋%s号'%i)

#第一步先取从0开始的数据
res=conn.hscan('hash2',cursor=0,count=10)   # 无序,所以不是从egg_0开始的
print(len(res[1]))

#第二步从上面的索引开始取值
 res=conn.hscan('hash2',cursor=res[0],count=10)
 print(res)


14 hscan_iter(name, match=None, count=None)  # 替代hgetall,一次性全取出值,如果占内存很大,会有风险 , 使用hscan_iter 分批获取值,内存占用很小
for item in conn.hscan_iter('hash2',count=10):
    print(item)

# 分批获取数据

conn.close()


'''常用
hset
hget
hlen  
hexists
hincrby
hscan_iter
'''

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)

'''

import redis

conn = redis.Redis(decode_responses=True)

1 lpush(name, values)  #在name对应的list中添加元素
conn.lpush('girls','刘亦菲','迪丽热巴')
conn.lpush('girls','小红')

2 rpush(name, values) 表示从右向左操作
conn.rpush('girls', '小绿')


3 lpushx(name, value)  #表示从左往右操作只有key存在,才能追加
conn.lpushx('girls','小紫')
conn.lpushx('girls1', '小紫')

4 rpushx(name, value) #表示从右向左操作,只有key存在,才能追加


5 llen(name) # name对应的list元素的个数
print(conn.llen('girls'))

6 linsert(name, where, refvalue, value))# 在name对应的列表的某一个值前或后插入一个新值
conn.linsert('girls', where='before', refvalue='刘亦菲', value='新刘亦菲')
conn.linsert('girls', where='after', refvalue='刘亦菲', value='老刘亦菲')


7 lset(name, index, value)#设置列表指定索引值为newValue
conn.lset('girls',0,'oooo')  # 按索引修改某个位置值

8 lrem(name, count, value) #删除指定数量的value
"""
count=0,删除列表中所有的指定值;
count为+,从前到后删除;
count为-,从后向前删除
"""
conn.lrem('girls',3,'刘亦菲')   # 删除3个刘亦菲
conn.lrem('girls',-1,'刘亦菲')   # 从右侧删一个
conn.lrem('girls',0,'刘亦菲')   # 全删除


9 lpop(name)
print(conn.lpop('girls'))  # 左侧弹出一个

10 rpop(name) 表示从右向左操作
print(conn.rpop('girls')) # 右侧弹出

11 lindex(name, index) #返回name对应索引的value
print(conn.lindex('girls',0))

12 lrange(name, start, end) #返回name对应的区间value
print(conn.lrange('girls',1,10000)) # 前闭后闭

# 一次性把列表中数据都取出来
print(conn.lrange('girls', 0, -1))   # 可以用-1
print(conn.lrange('girls', 0, conn.llen('girls')))   # 可以用-1


13 ltrim(name, start, end) #在name对应的列表中移除没有在start-end索引之间的值
conn.ltrim('girls',2,4)

14 rpoplpush(src, dst) #移除列表的最后一个元素,并将该元素添加到另一个列表并返回
"""src popKey和dst pushKey为同一个列表的情况,这时等于是把自身的表尾(右侧)元素移动到表头(左侧)"""
conn.rpoplpush('girls','girls')


15 blpop(keys, timeout)  #左侧弹出
""" block:阻塞   实现分布式的系统     消息队列
timeout是阻塞超时时间,timeout=0为拥有不阻塞"""
res=conn.blpop('girls',timeout=5)
print(res)

16 r.brpop(keys, timeout),#右侧弹出
17 brpoplpush(src, dst, timeout=0)


conn.close()

# utf-8 编码的 bytes格式
# b=b'\xe8\xbf\xaa\xe4\xb8\xbd\xe7\x83\xad\xe5\xb7\xb4'
# print(b.decode('utf-8'))
#
# for i in b:
#     print(bin(i))



'''常用
lpush
rpush
llen
lrange
lpop
'''
       
![](https://img2023.cnblogs.com/blog/3098700/202311/3098700-20231113151659849-266855200.png)

# redis其它操作

```python
'''
delete(*names)
exists(name)
keys(pattern='*')
expire(name ,time)
rename(src, dst)
move(name, db))
randomkey()
type(name)
'''

import redis

conn=redis.Redis()
1 delete(*names) #删除对应name
 conn.delete('userinfo2')

2 exists(name)  #判断name是否存在,返回0不存在1存在
 print(conn.exists('name')) #0

3 keys(pattern='*') #匹配模式
print(conn.keys('user*')) #匹配以user开头
print(conn.keys('*')) #所有
print(conn.keys())


4 expire(name ,time) #设置过期时间后删除
conn.expire('name',5)


5 rename(src, dst) #name改名
conn.rename('userinfo3','us')

6 move(name, db) #移动某个键所有数据到某个db中
conn.move('us',3)

7 randomkey() #随机获取键名
 print(conn.randomkey())



8 type(name) #查看键的数据类型
print(conn.type('hash2'))
print(conn.type('us'))


conn.close()

redis管道

# 事务四大特性
	-原子性:要么都成功,要么都失败
    -一致性:数据前后要一致
    -隔离性:多个事务之间相互不影响
    -持久性:事务一旦完成,数据永久改变
    
# 关系型数据库,支持事务  
    
# redis 有没有事务?没有专门的事物,但是通过别的方式,可以实现事务的几个特性,所以咱们认为它具备事务
	-redis要支持事务,要完成事务的几大特性,需要使用管道来支持
    -单实例redis是支持管道的
    -集群模式下,不支持管道,就不支持事务


模拟转账场景

from redis import Redis
conn=Redis()
#### 没有管道的情况,一旦失败,之前执行的 不会回退
conn.decrby('my_money',10)
l=[0,1,2]
print(l[3]) 
"""代码执行至此抛出异常,而my_money减去10,your_money没有增加10"""
conn.incrby('your_money',10)
conn.close()

通过管道实现事务

"""创建了一个管道pipeline,把命令都一个个放到管道中,先不执行,当执行execute,才执行管道中所有的命令"""
conn = redis.Redis()
pipline = conn.pipeline(transaction=True)#创建管道

pipline.decrby('my_money', 10)

l = [0, 2, 3]
print(l[0])
"""代码执行至此抛出异常,而my_money没有减去10,your_money也没有增加10"""
pipline.incrby('zs_money', 10)

pipline.execute() #执行管道命令

conn.close()

django中使用redis

通用方案

  • utils\pool.py
# 写一个池
import redis

POOL = redis.ConnectionPool(max_connections=20)
  • user\views.py
from utils.pool import POOL
from redis import Redis
from django.http import HttpResponse

# 在要使用的地方,导入使用即可
def redis_demo(request):
    conn = redis.Redis(connection_pool=POOL, decode_responses=True)
    res = conn.incrby('count')
    print(res)

    return HttpResponse(f'您是我们第:{res}个用户')
  • urls.py
from user.views import redis_demo
urlpatterns = [

    path('redis_demo', redis_demo),
]

image-20231018190731031

第三方模块

# django-redis ---》配置文件中配置即可
pip install 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",
        }
    },
}

# 在使用的位置,导入使用
from django_redis import get_redis_connection
def redis_demo(requset):
    conn = get_redis_connection()
    res = conn.incrby('count')
    print(res)

    return HttpResponse(f'您是我们第:{res}个用户')
  • dev.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",
        }
    },
}
  • user\views.py
from django_redis import get_redis_connection
def redis_demo(requset):
    conn = get_redis_connection()
    res = conn.incrby('count')
    print(res)

    return HttpResponse(f'您是我们第:{res}个用户')
  • urls.py
from user.views import redis_demo
urlpatterns = [

    path('redis_demo', redis_demo),
]

django缓存

# redis数据存在内存中,取放速度快---》非常适合做缓存
	本来数据在mysql中,每次都查询,速度慢---》把查询出来的数据,暂时存储到redis(缓存),下次请求再来,直接从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",
            }
            
            
 	-把数据放到缓存中
      from django.core.cache import cache
      cache.set(key,value)#value可以是任意类型,过期时间
      cache.set('key','value',5) # 存放值
	  res=cache.get('key') # 取值      
         -redis只支持5大数据类型,可以放python的任意类型
         -本质:pickle序列化---》bytes格式---》以redis字符串的形式放在了redis中
      cache.get(key)
            
            
            
            
# 后期咱么在项目中,使用redis作为django的缓存,多一些,尽量不使用原生redis操作

            
# 前后端分离中,使用 cache.set   cache.get   
# 前后端混合中
     可以整站缓存 
     可以要缓存一个页面
     可以缓存页面中的某个位置
# 可以缓存的位置:
     内存中
     本地文件中
     数据库中
     reids中 (咱们用的多)
	
  • user\views.py
from django.core.cache import cache
from django.http import HttpResponse

class Person():
    pass

def cache_demo_set(request):
    p=Person()
    p.name='lqz'
    # 缓存中存值
    cache.set('count',p) 
    return HttpResponse('成功')

def cache_demo_get(request): 
    # 缓存中取值
    print(cache.get('count').name) #lqz
    return HttpResponse(cache.get('count').name)

  • urls.py
from user.views import redis_demo,cache_demo_set,cache_demo_get
urlpatterns = [
    path('cache_demo_set', cache_demo_set),
    path('cache_demo_get', cache_demo_get),
]
  • settings.dev.py
#### 使用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",
        }
    },
}