「路飞项目09」redis

1 Redis介绍和安装

# Redis :软件,存储数据的,速度非常快,redis是一个key-value存储系统(没有表的概念),cs架构的软件
-服务端  客户端(python作为客户端,java,go,图形化界面,命令窗口的命令)
# es:存数据的地方

# 关系型数据库和非关系型数据库
-关系型:mysql,PostgreSQL【PG】,oracle,sqlserver,db2
		-去 IOE:国产化
				-IBM---》浪潮信息,曙光,联想
				-Oracle---》数据----》达梦。。。。
				-EMC存储--》国产存储
        
-非关系型数据库(nosql):redis(缓存),mongodb(json文档数据存储),es(大数据量存储)。。。。
		-nosql 指非关系型数据库: not only sql,对关系型数据库的补充
        
# redis特点:
-开源软件,存数据,cs架构
-key-value存储 ,5大数据类型  value的类型是5种:'''字符串,hash(字典),列表,集合,有序集合'''
-性能高:官方每秒查询率qps是10w+
-速度快:
		-1 纯内存存储(核心)
		-2 使用了IO多路复用的网络模型
		-3 数据操作是单线程,避免了线程间切换和加锁的消耗,也不会数据错乱
-支持持久化
-纯内存,可以存到硬盘上,防止数据丢失
-redis又被称之为 缓存数据库

1.1 安装redis

# redis 是用c语言编写的,需要在不同平台编译成可执行文件,才能在这个平台上运行
-redis 使用了io多路复用种的epoll模型,win不支持epoll
-redis官方,不支持win版本
-微软官方,就把redis改动,编译成可执行,能运行在win上,滞后 3.x版本
-第三方:5.x版本
    
# redis 官方网:https://redis.io/download/
# redis中文网:http://redis.cn
# win:3.x:https://github.com/microsoftarchive/redis/releases
# win:5.x:https://github.com/tporadowski/redis/releases/


# 安装:
-将端口6379置于防火墙之外
-安装完成后,在安装路径下有
		-redis-cli.exe     # mysql
		-redis-server.exe   # mysqld
		-redis.windows-service.conf  # my.ini
-并且会自动做成服务
		-服务的命令:redis-server.exe  redis.windows-service.conf
        
# mac系统:安装redis
# 通过homebrew这个mac特有的包管理工具来安装redis服务
1 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
-下载brew

2 brew search redis
-这个命令可以知道当前redis有哪些版本,从而确定自己需要安装哪个版本的redis。通过@来指定redis的版本,如其中的redis@3.2 redis@4.0等版本。没有@的redis就是最新版本的redis。

3 brew install redis 
-安装自己需要的版本,这里安装的是redis的最新版本,因为没有@。

4 redis安装位置 `/opt/homebrew/Cellar`目录下,配置文件在 `/opt/homebrew/etc`目录下

5 brew services start redis
-启动redis,brew services list  # 该命令可以看到在mac上所有服务目前的状况

'''
brew的其他一些常用的命令:
    1.启动redis服务
    brew services start redis

    2.关闭redis服务
    brew services stop redis

    3.重启redis服务
    brew services restart redis

    4.打开图形化界面(打开客户端......)
    redis-cli
'''

 一些常用的配置
    1.开机启动redis服务
    ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents

    2.使用配置文件启动redis-server
    redis-server /opt/homebrew/etc/redis.conf

    3.停止redis服务
    shutdown
    # 默认save,保存的文件为dump.rdb,作用是是Redis数据库的持久化文件,用于将内存中的数据保存到磁盘上。它确保了数据的持久性和可靠性,并提供了备份、恢复和数据迁移等功能
    '''
    如何查找MacBook redis的dump.rdb文件存放位置?
    1 redis-cli config get dir 找到配置文件位置
    2 cd .. 切换到该路径
    3 ls -l dump.rdb 查看详细信息
    
    '''

    4. redis配置文件位置
    /usr/local/etc/redis.conf

    5.卸载redis
    brew uninstall redis 
    rm ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
    
    6.通过bin目录下的redis-server命令来启动redis服务
      cd /usr/local/Cellar/bin/7.0.11/bin
      ./redis-server


  

        
        
# 启动redis服务端
-1 命令行中  redis-server 就可以启动服务,这样启动后关闭窗口服务就停止了,不推荐
-2 命令行中,启动服务,并指定配置文件
    	redis-server 配置文件路径
-3 使用服务启动

# 客户端链接
-1 命令行客户端:
		-redis-cli  # 默认连本地的6379端口
		-redis-cli -p 6379 -h 127.0.0.1
-2 图形化客户端链接
        -1 最新版的Navicate支持链接redis了(收费的)
  
        -2 Redis Desktop Manager(https://resp.app/)  收费的  用的多  是用qt写的图形化界面
                -qt是个平台,做GUI[图形化界面]开发
                -用c写,用python写的叫pyqt5
            
        -3 python的模块
                -pip install redis
      
      
        -4 macos的store下载官方RedisInsight,FREE
        

补充

# 区分字符和字节
字符: "a" "你" "🐶" 需要更长的字节来存储--》gbk、utf-8--》一个字符占多少字节由编码格式决定
字节: 8个比特位是一个字节,一个字节能存1个字符吗?只能存ASCII的字符

# 字符串和字节转换的两种方式
a = '保龄球'

1 decode、encode
b = a.encode('utf-8') # b'\xe4\xbf\x9d\xe9\xbe\x84\xe7\x90\x83' 16进制
c = b.decode() # '保龄球'


2 直接类型转换 
d = str(b,encoding='utf-8') # '保龄球'
print(len(d))  # 3 字符长度
e = bytes(a,encoding='utf-8') # b'\xe4\xbf\x9d\xe9\xbe\x84\xe7\x90\x83'
print(len(e)) # 9 字节长度


# bytes格式的16进制、2进制、10进制的显示
f = b'\xe4\xbf\x9d\xe9\xbe\x84\xe7\x90\x83' 
l = [i for i in f]
print(l) # 十进制 [228, 191, 157, 233, 190, 132, 231, 144, 131]


l1= [hex(i) for i in f]
print(l1) # 16进制 ['0xe4', '0xbf', '0x9d', '0xe9', '0xbe', '0x84', '0xe7', '0x90', '0x83']

l2= [bin(i) for i in f]
print(l2) # 二进制 ['0b11100100', '0b10111111', '0b10011101', '0b11101001', '0b10111110', '0b10000100', '0b11100111', '0b10010000', '0b10000011']

2 redis普通链接和连接池

2.1 普通链接

from redis import Redis

# conn = Redis()  # 建立redis的链接
conn = Redis(host="127.0.0.1",
             port=6379,
             db=0,decode_responses=True)  # 建立redis的链接   decode_responses=True,查询回来返回的结果是字符串类型,否则是byte格式
res = conn.get('name')  # 获取key名为name的value值
print(res)
conn.close()  # 关闭链接

2.2 连接池连接

# 一定要保证,池是单例的,以模块导入的形式做成了单例

'''
单例是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。简而言之,单例模式只允许创建一个类的实例,并提供静态方法来访问该实例,以确保该实例全局唯一并且在整个应用程序中保持一致性。这种模式经常应用于需要资源共享或避免重复资源消耗的场景中,例如线程池、配置文件、数据库连接等。
'''

'''
mysql有链接池,redis也有连接池,Django的orm链接没有连接池。Django一个请求是一个线程,4个请求就是4个数据库的连接。并发多个线程操作,会出现数据错乱或并发安全的问题吗?不会,因为mysql有行锁、表锁来控制。
'''

'''
连接池概念:为了防止客户端连接数过多导致服务挂掉,所以用连接池来限制连接数。
无论客户端有多少线程,总连接数被连接池控制了
'''
SHOW VARIABLES LIKE 'max_connections';可查mysql的默认最大连接数
# pool.py
import redis
POOL = redis.ConnectionPool(max_connections=100,host='127.0.0.1',port=6379,db=0, decode_responses=True)

# 导入POOL的文件
import redis
from pool import POOL # 模块导入的方式, 天然单例
# 代码同python伪多线程的连接池.py  
'''
POOL必须是单例:全局只有一个POOL对象
python有大概6种方式实现单例,最简单---》以模块导入的形式做成单例
'''


# python伪多线程的连接池.py
import redis
from threading import Thread
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0, decode_responses=True, max_connections=100)

def task():
    conn = redis.Redis(connection_pool=POOL)# 以后拿到链接,是从POOL种取,如果没有可用的了,默认不阻塞,可以通过某个参数配置,设置阻塞等待
    res = conn.get('name') # 获取key名为name的value值
    print(res)
    conn.close() # 关闭链接
if __name__ == '__main__':
    # python的多线程是伪多线程
    for i in range(1000):
        t = Thread(target=task)
        t.start()


3 redis之字符串类型

# https://www.cnblogs.com/liuqingzheng/articles/9833534.html



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', '篮球', ex=6)
conn.set('hobby', '篮球', px=3000)
conn.set('hobby', '足球', nx=True)
conn.set('hobby', '足球', xx=True)


2 setnx(name, value)
# 设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改
conn.setnx('hobby1', '乒乓球')  # 等同于conn.set('hobby', '足球', nx=True)


3 psetex(name, time_ms, value)
# 设置值
# 参数:
# time_ms,过期时间(数字毫秒 或 timedelta对象
conn.psetex('name',3000,'xxx')


4 mset(*args, **kwargs)
conn.mset({'name': "lqz", 'height': 183})  # 批量设置值,跟一次次设置的区别是,少了网络交互的时间


5 get(name)
res=conn.get('hobby')  # utf-8编码,一个中文占3-4个字节    GBK编码,一个中文占2个字节
print(res)


6 mget(keys, *args)
# 批量获取
res=conn.mget('name','age')
res=conn.mget(['name','age'])
print(res)


7 getset(name, value)  
# 设置新值并获取原来的值
res=conn.getset('name','彭于晏')
print(res)


8 getrange(key, start, end)
# 获取子序列(根据字节获取,非字符)
res=conn.getrange('name',0,1)  # 前闭后闭区间
print(res)


9 setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
conn.setrange('name',2,'lqz')

10 setbit(name, offset, value)
11 getbit(name, offset)
12 bitcount(key, start=None, end=None)
13 bitop(operation, dest, *keys)


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


15 incr(self, name, amount=1)  
# 做计数器。好处是不会出现并发安全问题
# 自增name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:amount,自增数(必须是整数)
conn.incrby('height')


16 incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:amount,自增数(浮点型)


17 decrby(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
conn.decrby('height',5)


18 append(key, value) 
# 在redis key对应的值后面追加内容
conn.append('name', 'xxxx')

conn.close()


'''
get
set
strlen
append
'''

4 redis之hash类型

# hash类型就是咱们python中的字典,key-value,字典又叫hash类型。字典的key必须可hash
-字典类型在底层存储,基于数组存的 key---{key:value,key:value}
    
    
1 hset(name, key, value)
'''
name对应的hash中设置一个键值对(不存在,则创建;否则,修改)

参数:
name,redis的name
key,name对应的hash中的key
value,name对应的hash中的value

hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
'''
conn.hset('userinfo', 'name', '渣渣辉')
conn.hset('userinfo', mapping={'age': 33, 'hobby': 'sing'})


2 hget(name,key)
# 在name对应的hash中获取根据key获取value
conn.hget('userinfo','hobby')


3 hmget(name, keys, *args)
# 在name对应的hash中获取多个key的值
# 参数:
    # name,reids对应的name
    # keys,要获取key集合,如:['k1', 'k2', 'k3']
    # *args,要获取的key,如:k1,k2,k3
conn.hmget('userinfo',['hobby','name']) 
conn.hmget('userinfo','hobby','name')


4 hgetall(name)
# 获取name对应hash的所有键值
# 慎用,如果hash中key非常多,可能会撑爆内存
conn.hgetall('userinfo') # {'name': '渣渣辉', 'age': '33', 'hobby': 'dance'}


5 hlen(name)
# 获取name对应的hash中键值对的个数
conn.hlen('userinfo') # 3


6 hkeys(name)
# 获取name对应的hash中所有的key的值
conn.hkeys('userinfo') # ['name', 'age', 'hobby']


7 hvals(name)
# 获取name对应的hash中所有的value的值
conn.hvals('userinfo') # ['渣渣辉', '33', 'dance']


8 hexists(name, key)
# 检查name对应的hash是否存在当前传入的key
conn.hexists('userinfo','sex') # False


9 hdel(name,*keys)
# 将name对应的hash中指定key的键值对删除
conn.hdel('userinfo','sex') # 1



10 hincrby(name, key, amount=1)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
# 参数:
    # name,redis中的name
    # key, hash对应的key
    # amount,自增数(整数)
conn.hincrby('userinfo','age') # 34

11 hincrbyfloat(name, key, amount=1.0)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount 
# 参数:
    # name,redis中的name
    # key, hash对应的key
    # amount,自增数(浮点数)

    



12 hscan(name, cursor=0, match=None, count=None)
# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,【获取的数据为约等于,不一定为count,不单独用】并非一次性将数据全部获取完,从而防止内存被撑爆
 
# 参数:
    # 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时,表示数据已经通过分片获取完毕
res = conn.hscan('hen', cursor=0, count=10)
print(len(res[1])) # 10或者11

    
13 hscan_iter(name, match=None, count=None)
# 占内存小,区别于hgetall
# 利用yield封装hscan创建生成器,实现分批去redis中获取所有数据
# 参数:
    # match,匹配指定key,默认None 表示所有的key
    # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
 
# 如:
    # for item in r.hscan_iter('xx'):
    #     print item
res = conn.hscan_iter('hen',count=10)
for item in res:
    print(item)
    
    
    
'''
hset
hget
hlen
'''
posted @ 2023-06-28 15:36  雀雀飞了  阅读(32)  评论(0编辑  收藏  举报