• Redis基础


    一、Redis介绍

    1、redis介绍

    开源:早起版本2w3千行

    基于键值对的存储系统:字典形式

    多种数据结构:字符串,hash,列表,集合,有序集合

    高性能,功能丰富

    使用的公司有:github,twitter,stackoverflow,阿里,百度,微博,美团,搜狐

    2、8个特性:

    (1)速度快:10w ops(每秒10w读写),数据存在内存中,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版本以后支持分布式

    3、典型使用场景

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

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

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

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

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

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

    二、Redis安装 及配置

    1、官网下载,如果没有更改安装路径,就直接下一步就可以了

    2、redis相关配置

    在安装目录下,找到redis.windows-service.conf,在该文件中进行配置

    """
    1)绑定的ip地址,多个ip用空格隔开
    bind 127.0.0.1
    
    2)端口,默认6379,一般不做修改
    port 6379
    
    3)是否以守护进程启动,默认为no,一般改为yes代表后台启动(windows系统不支持)
    daemonize no
    
    4)定义日志级别,默认值为notice,有如下4种取值:
        debug(记录大量日志信息,适用于开发、测试阶段)
        verbose(较多日志信息)
        notice(适量日志信息,使用于生产环境)
        warning(仅有部分重要、关键信息才会被记录)
    loglevel notice
    
    5)配置日志文件保持地址,默认打印在命令行终端的窗口上
        如果填写 "./redis.log" 就会在启动redis服务的终端所在目录下,用redis.log记录redis日志
    logfile ""
    
    eg)终端首先切断到log文件夹所在目录(一般就可以采用redis的安装目录,也可以自定义),再启动reids服务
    logfile "./log/redis.log"
    
    6)数据库个数,默认是16个,没特殊情况,不建议修改
    databases 16
    
    7)数据持久化
    save 900 1  # 超过900秒有1个键值对操作,会自动调用save完成数据持久化
    save 300 10  # 超过300秒有10个键值对操作,会自动调用save完成数据持久化
    save 60 10000  # 超过60秒有10000个键值对操作,会自动调用save完成数据持久化
    
    8)数据库持久化到硬盘失败,redis会立即停止接收用户数据,让用户知道redis持久化异常,避免数据灾难发生(重启redis即可),默认为yes,不能做修改
    stop-writes-on-bgsave-error yes
    
    9)消耗cpu来压缩数据进行持久化,数据量小,但会消耗cpu性能,根据实际情况可以做调整
    rdbcompression yes
    10)增持cpu 10%性能销毁来完成持久化数据的校验,可以取消掉
    rdbchecksum yes
    
    11)持久化存储的文件名称
    dbfilename dump.rdb
    
    12)持久化存储文件的路径,默认是启动服务的终端所在目录
    dir ./
    
    13)reids数据库密码
    requirepass 密码
    """

    三、Redis的基本操作

    1、启动服务:

    """
    windows系统
    1)前台启动
        i)打开终端切换到redis安装目录
        >: cd C:AppsRedis
        
        ii)启动服务
        >: redis-server redis.windows.conf
    
    2)后台启动
        i)打开终端切换到redis安装目录
        >: cd C:AppsRedis
        
        ii)启动服务(后面的配置文件可以省略)
        >: redis-server --service-start redis.windows-service.conf
    """
    # linux启动
    redis-server

    2、连接redis:

    """
    1)默认连接:-h默认127.0.0.1,-p默认6379,-n默认0,-a默认无
    >: redis-cli
    
    2)完整连接:
    >: redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码
    
    3)先连接,后输入密码
    >: redis-cli -h ip地址 -p 端口号 -n 数据库编号
    >: auth 密码
    """

    3、密码设置、修改、查找

    """
    1)提倡在配置文件中配置,采用配置文件启动
    requirepass 密码
    
    2)当服务启动后,并且连入数据库(redis数据库不能轻易重启),可以再改当前服务的密码(服务重启,密码重置)
    config set requirepass 新密码
    
    3)已连入数据库,可以查看当前数据库服务密码
    config get requirepass
    """

    4、其他操作:

    1、切换数据库:
    >: select 数据库编号
        
    2、关闭服务(前提是数据库已连接)
    >: shutdown
    # 直接连接数据库并关闭redis服务
    >: redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码 shutdown
    
    3、清空redis数据库(前提数据库已连接)
    >: flushall
        
    4、数据持久化:
    1)配置文件默认配置
    >: save 900 1  # 超过900秒有1个键值对操作,会自动调用save完成数据持久化
    >: save 300 10  # 超过300秒有10个键值对操作,会自动调用save完成数据持久化
    >: save 60 10000  # 超过60秒有10000个键值对操作,会自动调用save完成数据持久化
    
    2)安全机制
    # 当redis服务不可控宕机,会默认调用一下save完成数据持久化(如果数据量过大,也可能存在部分数据丢失)
    
    3)主动持久化
    >: save  # 连入数据库时,主动调用save完成数据持久化
    
    注:数据持久化默认保存文件 dump.rdb,保存路径默认为启动redis服务的当前路径

    四、Redis在python中的使用

    1、导入依赖库
    >: pip3 install redis
    2、使用;
    import redis
    #decode_response=True 得到的结果会自动解码(不是二进制数据)
    conn=redis.Redis(host='127.0.0.1',port=6379,db=1,password=密码,decode_responses=True)
    #连接池:
    pool=redis.ConnectionPool(host='127.0.0.1',port=6379,db=1,max_connections=100,password=密码,decode_responses=True)
    conn_pool=redis.Redis(conncetion_pool=pool)

    五、Redis数据类型

    String类型操作

    String类型在内存中的存储是按照一个name对应一个value来存储的

    '''
    ex:过期时间 (单位:秒)
    px: 过期时间 (单位:毫秒)
    nx:如果设置为True,则只有name不存在时,set操作才执行,如果name存在,则修改不了,执行没有效果
    xx: 如果设置为True,则只有name存在是,set操作才执行,name值存在才能修改,不存在,不会设置新值
    '''
    conn.set('height','180',ex=1)
    conn.set('height',180,px=1)
    conn.set('height','100',nx=True)
    # height结果:height=180
    conn.set('height','100',xx=True)
    # height结果:height=100
    conn.set('height1','102',xx=True)
    # height1结果:None
    
    '''
    setnx:只用当key不存在的时候,才会执行,存在,则不会执行 等价于set('height','100',nx=True)
    setex(name,time,value) 等价于 set('height','180',ex=1)
    psetex(name,time_ms,value) 等价于 conn.set('height',180,px=1)
    '''
    conn.set('height', '180')
    conn.setnx('height', '100')
    # 此时height 还是 180
    conn.setex('height1',1,'120')
    # 等价于set('height',180,px=1)
    conn.psetex('height', 1, '110')
    # print(conn.get('height'))
    print(conn.get('height1'))
    '''
    mset:以字典的形式插入多个
    mget: 返回列表的形式
    getset: 设置一个值,并返回一个原来的旧值
    '''
    # conn.mset({'k1': 'hello', 'k2': 'world'})
    conn.mget(['k1', 'k2'])
    # 结果:[b'hello',b'world']
    re = conn.getset('k1', '111')
    # 结果:返回的是k1旧值 :hello
    
    
    '''
    getrange(key,start,end);截取value的长度,类似切片,截取超过本身长度则只取本身
    setrange(key,offset,value): 在value的offset起始位置插入value值
    getbit('key,offset):截取value的二进制中指定位置的值(0或者1)
    setbit(key,offset,value): 将key对应的值的二进制表示的位进行操作,注意value值只能是1或者0 incr('key',amount=1): 只要执行该语句,value数字就会加amount的值,一般用于访问量统计
    incrbyfloat(key,amount=1.0): 同incr一样,自增数是浮点型 decr('key',amount=1): 只要执行该语句,value数字就会减1 append('key',insert_value):原来value+insert_value。
    bitop(operation,dest,*keys):
    operations: AND(并)、OR(或)、NOT(非)、XOR(异或)
    dest:新的Redis的key
    *key: 要查找的Redis的name
    bitcount(key,start=None,end=None): 获取key对应值的二进制中的某位的值
    strlen(key): 返回key对应值的字节长度(一个汉字3个字节)
    ''' ret=conn.getrange('k1',0,3) # 原来k1 = 11111,执行后得到结果是 111, conn.setrange('k4',2,1) # 原来k4=8888 现在变成 k4=88188 print(conn.get('k4')) ret=conn.getbit('k4',3) #结果:ret=1 conn.set('k5',1) conn.incr('k5',2) conn.incr('k5') print(conn.get('k5')) # 结果:k5=4 conn.decr('k5') print(conn.get('k5')) # 结果:k5=2 conn.append('k5','oo') # 结果 k5=200
    conn.bitop('AND','k6','k1','k2','k3')

     Hash操作(value是字符类型)

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

    '''
    在对应的hash中设置一个键值对(不存在,则创建,否则,修改)
    hset(name,key,value):
        name:redis的name
        key:字典关键字
        value:字典关键字对应的值
    
    hget(name,key): 获取name对应的hash中key对应的值
    '''
    conn.hset('hash1','h1',11)
    conn.hset('hash1','h2',22)
    conn.hset('hash1','h1',333)
    ret=conn.hget('hash1','h1')
    # >: 333
    '''
    在对应的hash中批量设置键值对
    hmset(name,mapping):
        name: redis的name
        mapping: 字典
    
    hmget(name,keys,*args):
        keys:要获取key集合
        *args: 要获取的key
    '''
    conn.hmset('hash2',{'k1':1,'k2':2,'k3':3})
    ret=conn.hmget('hash2','k1','k2','k3')
    #两者等价
    ret=conn.hmget('hash2',['k1','k2','k3'])
    # >:结果都是 ['1','2','3']
    
    '''
    hgetall(name): 获取name对应hash的所有键值,以字典的形式返回
    '''
    ret=conn.hgetall('hash2')
    # >: {'k1': '1', 'k2': '2', 'k3': '3'}
    
    '''
    hlen(name): 获取name对应的hash中键值对的个数
    hkeys(name): 获取name对应的hash中所有key的值,以列表形式返回
    hvals(name): 获取name对应hash中所有value的值,以列表形式返回
    hexists(name,key): 判断当前hash中是否存在key
    hdel(name,*keys): 删除hash中指定的key的键值对
    '''
    ret=conn.hlen('hash2')
    # >: 3
    ret=conn.hkeys('hash2')
    # >: ['k1', 'k2', 'k3']
    ret=conn.hvals('hash2')
    # >: ['1', '2', '3']
    ret=conn.hexists('hash2','k1')
    # >: True
    conn.hdel('hash2','k1','k2')
    # >: 2
    
    '''
    自增name对应的hash中指定key的值,不存在则创建key=amount
    hincrby(name,key,amount=1):
           amount: 是自增数(整数)
    hincrbyfloat(name,key,amount=1.0) 同hincrby一样,只不过是自增数是浮点类型
    '''
    conn.hincrby('hash2','k1',amount=1)
    conn.hincrby('hash2','k1',amount=1)
    conn.hincrbyfloat('hash2','k2',amount=1.1)
    conn.hincrbyfloat('hash2','k2',amount=1.1)
    # >: hahs2所有键值对:{'k3': '3', 'k1': '2', 'k2': '2.2'}
    
    '''
    hscan(name,cursor=0,match=None,count=None):
          cursor: 游标(基于游标分批获取数据)
          match: 匹配指定key,默认None 表示所有的key
          count: 每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    '''
    #eg:
    # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
    # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
    # ...
    # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
    cursor1,data1=conn.hscan('hash2',cursor=0,match=None,count=None)
    # >:0 ,{'k3': '3', 'k1': '2', 'k2': '2.2'}
    
    '''
    # 利用yield封装hscan创建生成器,实现分批去redis中获取数据
    hscan_iter(name,match=None,count=None):
    # 参数:
        # match,匹配指定key,默认None 表示所有的key
        # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    '''
    for item in conn.hscan_iter('hash2'):
        print(item)
    # >: ('k3', '3')
    # >: ('k1', '2')
    # >: ('k2', '2.2')

    List列表操作

    在内存中存储方式

    '''
    lpush(name,values): 在对应的list中添加元素,每个新元素都是添加在列表的最左边
    rpush(name,values)则是添加在列表的右边
    '''
    conn.lpush('l1',11,22,33)
    # >: 33,22,11
    '''
    lpushx(name,value): 跟lpush一样,也是添加元素,
    但是,只有当name存在是才会添加,当name不存在时,则不会添加
    注意: rpushx则是添加在列表右边
    '''
    conn.lpushx('l2',1)
    conn.lpushx('l1',1)
    '''
    llen(name): 获取name对应的list元素的个数
    '''
    conn.llen('l1')
    # >: 19
    '''
    linsert(name,where,refvalue,value):
     参数:
         name,redis的name
         where,BEFORE或AFTER(小写也可以)
         refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
         value,要插入的数据
    '''
    conn.linsert('l1',where='before',refvalue=1,value=22222)
    
    '''
    lset(name,index,value): 在list的某个位置插入一个值
        index: list的索引位置
        value: 要设置的值
        
    lrem(name,count,value): 删除name对应list中指定的值
        value: 要删除的值
        count:  count=0,删除列表中所有的指定值;
              count=2,从前到后,删除2个;
              count=-2,从后向前,删除2个
              
    lpop(name): 删除name对应list的左侧第一个元素,并返回该元素
    rpop:则是右侧第一个元素
    '''
    conn.lset('l1',2,'1111111111')
    conn.lrem('l1',value=11,count=0)
    l=conn.lpop('l1')
    # >: 22222
    
    '''
    lindex(name,index): 返回索引对应的元素
    lrange(name,start,end): 跟列表切片一致
    ltrim(name,start,end): 在name对应的列表中移除没有在start-end索引之间的值
    '''
    l= conn.lindex('l1',2)
    # >: 22
    l=conn.lrange('l1',1,10)
    # >: ['1111111111', '22', '33', '22', '33', '22', '33', '22', '33', '22']
    conn.ltrim('l1',1,9)
    '''
    从一个列表取出最右边的元素,同时,将取出来的元素添加至另一个列表的最左边
    rpoplpush(src,dst):
         src: 要取数据的列表name
         dst: 要添加数据的列表name
         
    同rpoplpush一样,只不过,如果src这个列表没有数据的话,会进入阻塞状态。
    brpoplpush(src,dst,timeout=0):
         timeout: 超时时间,0表示永远阻塞
    '''
    conn.rpoplpush('l1','l2')
    '''
    获取并移除列表第一个元素,如果列表没有元素,则会计入阻塞,直到发现可弹出元素或者等待超时为止
    blpop(keys,timeout):
        keys: redis的name的元素集合
        timeout: 超时时间,默认是0,表示永远阻塞
    '''
    conn.blpop(('l2','l1'),timeout=2)
    # >: ('l1','33')
    
    #自定义增量迭代
    # 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
        # 1、获取name对应的所有列表
        # 2、循环列表
    # 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
    import redis
    conn=redis.Redis(host='127.0.0.1',port=6379)
    # conn.lpush('test',*[1,2,3,4,45,5,6,7,7,8,43,5,6,768,89,9,65,4,23,54,6757,8,68])
    # conn.flushall()
    def scan_list(name,count=2):
        index=0
        while True:
            data_list=conn.lrange(name,index,count+index-1)
            if not data_list:
                return
            index+=count
            for item in data_list:
                yield item
    print(conn.lrange('test',0,100))
    for item in scan_list('test',5):
        print('---')
        print(item)

    其他操作:

    blpop key timeout #lpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
    brpop key timeout #rpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
    
    #要实现栈的功能
    lpush+lpop
    #实现队列功能
    lpush+rpop
    #固定大小的列表
    lpush+ltrim
    #消息队列
    lpush+brpop

    Set操作:(不允许重复的列表)

    # sadd(name,values) 添加元素,可以添加一个或多个
    conn.sadd('s1', 111, 222)
    
    # scard(name) 获取name对应的集合中元素个数
    conn.scard('s1')
    # >:2
    conn.sadd('s2', 111, 333,444,555,666,777)
    '''
    smembers(name) 获取集合中所有元素
    sismember(name,value) 检测value是否是name集合的元素
    '''
    conn.smembers('s1')
    # >:{'111', '222'}
    
    conn.sismember('s1', '111')
    # >: True
    
    '''
    sdiff(keys,*args) 差集
    sdiffstore(dest,keys,*args),将差集存在一个新的集合中
       参数 dest: 是新集合的name
    '''
    conn.sdiff('s1', 's2')  # 在集合s1中但是不在集合s2中的元素
    # >: {'222'}
    conn.sdiffstore('s3','s1','s2')
    # >: s3={'222'}
    
    '''
    sinter(keys,*args) 交集
    sinterstore(dest,keys,*args) 将交集结果存在dest中
    '''
    conn.sinter('s1', 's2')
    conn.sinterstore('s3', 's1', 's2')
    # >: {'111'}
    '''
    并集:
    sunion(keys,*args)
    sunionstore(dest,keys,*args) 并集结果存于dest这个新集合中
    '''
    conn.sunion('s1','s2')
    conn.sunionstore('s3','s1','s2')
    
    '''
    smove(src,dst,value):将src集合中的valuse移动到dst集合中
    spop(name): 从集合的右侧移除一个成员并将其返回
    '''
    conn.smove('s1','s2','222')
    
    # srandmember(name,numbers):从集合随机获取 numbers个元素
    conn.srandmember('s2', 2)
    # >:['111', '333']
    
    # srem(name,values) 删除集合中某些值
    conn.srem('s2',111,333)
    
    '''
    通哈希类型的hscan一样
    sscan(name,cursor=0,match=None,count=None):
          cursor: 游标(基于游标分批获取数据)
          match: 匹配指定key,默认None 表示所有的key
          count: 每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    '''
    # eg:
    # 第一次:cursor1, data1 = r.sscan('xx', cursor=0, match=None, count=None)
    # 第二次:cursor2, data1 = r.sscan('xx', cursor=cursor1, match=None, count=None)
    # ...
    # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
    conn.sscan('s2', cursor=0, match=None, count=2)
    # >: (2, ['555', '111'])
    
    '''
    # 利用yield封装sscan创建生成器,实现分批去redis中获取数据
    sscan_iter(name,match=None,count=None):
    # 参数:
        # match,匹配指定key,默认None 表示所有的key
        # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    '''
    for item in conn.sscan_iter('s2',match=None,count=None):
        print(item)
    # >:555
    # >:111
    # >: 333
    # ...

    有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。(结构:(name, value,score))

    '''
    有序集合添加元素
    zadd(self, name, mapping, nx=False, xx=False, ch=False, incr=False):
    参数:
        mapping: 字典
        nx:如果设置为True,则只有name不存在时,set操作才执行,如果name存在,则修改不了,执行没有效果
        xx: 如果设置为True,则只有name存在是,set操作才执行,name值存在才能修改,不存在,不会设置新值
        ch: 如果为True,返回值返回的是修改的元素数。修改的元素包括添加的新元素和分数更改的元素。false则默认返回0
        incr:设置为True时,当集合元素不存在时,则没有影响,若集合元素(比如:n1)存在,则会把原来的元素对应的value值,加新的value值。
    '''
    conn.zadd('zs1',{'n1':1,'n2':2,'n3':3,'n4':5,'n5':4})
    conn.zadd('zs1',{'n1':6},incr=True)
    # >: n1=7
    '''
    name有序集合元素的数量
    zcard(name)
    '''
    conn.zcard('zs1')
    # >: 5
    '''
     获取name对应的有序集合分数(value) 在[min,max]之间的个数
     zcount(name,min,max):
    '''
    conn.zcount('zs1',2,5)
    # >: 4
    '''
    有序集合结构:(name,value,score)
     zincrby(name, amount, value):
    '''
    conn.zincrby('zs1',amount=4,value='n1')
    # >: n1 对应的score 从2 变成6
    '''
    conn.zrange(name, start, end, desc=False, withscores=False,score_cast_func=float)
     参数:
        name,redis的name
        start,有序集合索引起始位置(非分数)
        end,有序集合索引结束位置(非分数)
        desc,排序规则,默认按照分数从小到大排序
        withscores,是否获取元素的分数,默认只获取元素的值
        score_cast_func,对分数进行数据转换的函数
    # 更多:
        # 从大到小排序
        # zrevrange(name, start, end, withscores=False, score_cast_func=float)
     
        # 按照分数范围获取name对应的有序集合的元素
        # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
        # 从大到小排序
        # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
    '''
    conn.zrange('zs1',1,5)
    # >: ['n3', 'n5', 'n4', 'n1']
    conn.zrange('zs1',1,5,withscores=True)
    # >: [('n3', 3.0), ('n5', 4.0), ('n4', 5.0), ('n1', 6.0)]
    
    '''
     获取name有序集合中的value的排行位置(本质就是位置,是从0开始计)
     zrank(name,value)
     更多:
        # zrevrank(name, value),从大到小排序
    '''
    conn.zrank('zs1','n1')
    # >: 4
    conn.zrevrank('zs1','n1')
    # >: 0
    
    '''
     conn.zrangebylex(name, min, max, start=None, num=None)
     当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 
     (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中,
     元素的值介于 min 和 max 之间的成员
     对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 
     返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
    # 参数:
        # name,redis的name
        # min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间
        # min,右区间(值)
        # start,对结果进行分片处理,索引位置
        # num,对结果进行分片处理,索引后面的num个元素
    # 如:
        # ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga
        # r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']
    # 更多:
        # 从大到小排序
        # zrevrangebylex(name, max, min, start=None, num=None)
    '''
    conn.zrangebylex('zs1','[n1','+')
    # >: ['n2', 'n3', 'n5', 'n4', 'n1']
    
    '''
    zrem(name,values) 删除name集合中values的值
    zremrangebyrank(name,min,max): 根据value范围删除
    zremrangebyscore(name,min,max): 根据分数范围删除
    zremrangebylex(name, min, max):根据值返回删除
    '''
    conn.zrem('zs1',['n5','n4'])
    conn.zremrangebyrank('zs1',2,3)
    conn.zremrangebyscore('zs1',7,9)
    '''
    获取两个有序集合的交集,存在一个新的集合,如遇到相同值不同分数,则按照aggregate进行操作
    zinterstore(dest,keys,aggregate=None)
    获取两个有序集合的并集,存在一个新的集合,如遇到相同值不同分数,则按照aggregate进行操作
    zunionstore(dest, keys, aggregate=None)
     aggregate的值为:  SUM  MIN  MAX
    '''
    # zs1={n1:1,n2:2,..} zs2={n1:4,b1:1,..}
    conn.zinterstore('zs3',('zs1','zs2'),aggregate='MAX')
    # >: zs3={n1:4}
    '''
    跟字符串的相似
    zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
    zscan_iter(name, match=None, count=None,score_cast_func=float)
        score_casst_func:是用来处理分数的
    '''

    其他操作

    #delete(*names) #删除redis中的任意数据类型
    conn.delete('k4','k5')
    exists(name) #判断name是否存在
    '''
    keys(pattern='*') 获取redis的name
    # 更多:
        pattern = * 匹配数据库中所有 key 。
        pattern = h?llo 匹配 hello , hallo 和 hxllo 等。
        pattern = h*llo 匹配 hllo 和 heeeeello 等。
        pattern = h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 
    '''
    conn.keys(pattern='*')
    # ['s1', 'zs1', 'zs2', 's3', 's2', 'hash1', 'k1', 'k3', 'hash2', 'name', 'k2', 'zs3', 'l1']
    
    expire(name ,time) # 为某个redis的某个name设置超时时间
    rename(src, dst) # 对redis的name(src)重命名为dst
    move(name, db)  # 将redis的某个值移动到指定的db下
    randomkey() # 随机获取一个redis的name(不删除)
    type(name) # 获取name对应值的类型
    scan(cursor=0, match=None, count=None)
    scan_iter(match=None, count=None) # 同字符串操作,用于增量迭代获取key

    redis的总结:

    '''
    列表:实现timeLine功能,时间轴,微博关注的人,按时间轴排列,在列表中放入关注人的微博的即可
    集合:抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除
         点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中
         标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3
    ​     给标签添加用户,关注该标签的人有哪些
         共同好友:集合间的操作
    ​    sadd:可以做标签相关
    ​    spop/srandmember:可以做随机数相关
    ​    sadd/sinter:社交相关
    有序集合:做排行榜
    '''

    六、 管道

    管道来实现一次请求执行多条命令,减少多个命令造成的网络时延。

    注意:管道是没有原子性的,同时,也不是命令越多越好

    # -*-coding:utf-8 -*-
    import redis
    
    pool=redis.ConnectionPool(host='127.0.0.1',port=6379,password=12345)
    r=redis.Redis(connection_pool=pool)
    
    #transaction=True 开启事务,注意,该事务没有回滚
    pip=r.pipeline(transaction=True)
    pip.multi() #
    pip.set('k4','111')
    pip.set('k5','5555')
    pip.execute() #执行

    1次pipeline(n条命令)=1次网络时间+n次命令时间

    '''
    pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭;
    如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作新建Client链接,让pipeline和其他正常操作分离在2个client中。
    不过pipeline事实上所能容忍的操作个数,和socket-output缓冲区大小/返回结果的数据尺寸都有很大的关系;
    同时也意味着每个redis-server同时所能支撑的pipeline链接的个数,也是有限的,这将受限于server的物理内存或网络接口的缓冲能力
    '''

    七、事务

    事务是一个单独的隔离操作,将事务中的命令进行序列化、按顺序去执行。本质:就是命令入队,按顺序执行。

    注意:redis单条命令式是保存原子性,但是redis事务是不保证原子性

    multi:开启事务

    exec:执行事务

    discard:清空事务队列,并放弃执行事务

    127.0.0.1:6379> multi  #开启事务
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> exec  #执行事务
    1) OK
    2) OK
    127.0.0.1:6379> multi #开启事务
    OK
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> discard  # 取消事务
    OK

    八、Django中使用redis

    1、方法一(直接使用):

     先封装一个redis_pool.py文件

    import redis
    POOl=redis.ConnectionPool(host='127.0.0.1',port=6379,password='12345',max_connections=1000)

    再在视图函数views中写代码

    import redis
    
    from luffy.utils.redis_pool import POOl
    def index(request):
        conn=redis.Redis(connection_pool=POOl)
        conn.hset('kkk','age',11)
        ...

    2、方法二(使用django-redis模块)

    (1)pip install django-redis

    (2)setting里配置:

    # redis配置
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/2",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100}
                "PASSWORD": "123",
            }
        }
    }

    (3)视图函数:

    from django_redis import get_redis_connection
    conn = get_redis_connection('default')
    print(conn.hgetall('xxx'))
  • 相关阅读:
    团队计划
    python数据处理学习
    ShellExecute函数
    WinAPI WinMain函数
    I2C相关知识学习
    JavaScript多元运算符
    彻底理解js中的&&和||
    彻底理解线程同步与同步代码块synchronized
    JVM内存区域划分Eden Space、Survivor Space、Tenured Gen,Perm Gen解释
    JS参数使用带参数的方法
  • 原文地址:https://www.cnblogs.com/nq31/p/14087899.html
Copyright © 2020-2023  润新知