• Hello Redis


    引言:实际开发中我们不可能每次都从数据库中查数据,因为一旦访问量大了数据库很容易就崩溃,因此需要将一些数据存到缓存数据库中,这样用户访问时先去缓存数据库中找没找到再查询数据库。从而避免了数据库

    Redis是啥

    它是当下最受欢迎的NoSQL数据库之一,它是一个内存数据库(缓存数据库),非关系型的,对于数据的读写速度快。

    Redis的优势

    (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
    (2) 支持丰富数据类型,支持string,list,set,sorted set,hash
    (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
    (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

    和Memcached比较

    Memcached只支持一种数据类型字符串

    Memcached不支持持久化(不支持存到硬盘)

    单线程、单进程,不存在并发访问的问题(新版本开始有并发访问了)

    单线程为什么这么快

    • 数据在内存中
    • 运用了io多路复用技术
    • 没有进程线程间的切换

    redis适合的场景

    1 排行榜
    2 网站访问量,文章访问量
    3 缓存数据库(用的最多,就是做缓存)
    4 发布订阅
    5 去重
    6 分布式(blpop)
    

    Python操作redis普通连接

    from redis import Redis
    conn = Redis(host='127.0.0.1', port=6379)
    conn.set('name', 'ggb')
    print(conn.get('name'))
    

    Python操作redis之连接池

    # 将池建成单例模式(使用导模块的方式)
    redis_pool.py
    import redis
    POOL = redis.ConnectionPool(host='127.0.0.1', port=6379)
    
    from redis_pool import POOL
    from redis import Redis
    # decode_responses默认为False,默认返回bytes类型
    conn = Redis(connection_pool=POOL, decode_responses=True)
    conn.set('age', '18')
    print(conn.get('age'))
    

    redis中的数据类型

    字符串(string)

    • set(name, value, ex=None, px=None, nx=False, xx=False)

      在Redis中设置值,默认,不存在则创建,存在则修改
      参数:
           ex,过期时间(秒)
           px,过期时间(毫秒)
           nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
           xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
      
    • setnx(name, value)

      设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改
      
    • setex(name, value, time)

      # 设置值
      # 参数:
          # time,过期时间(数字秒 或 timedelta对象)
      
    • psetex(name, time_ms, value)

      # 设置值
      # 参数:
          # time_ms,过期时间(数字毫秒 或 timedelta对象
      
    • mset(*args, **kwargs)

      批量设置值
          mset(k1='v1', k2='v2')
          mset({'k1': 'v1', 'k2': 'v2'})
      
    • get(name)

      获取值
      
    • mget(keys, *args)

      批量获取
          mget('k1', 'k2')
          mget(['k3', 'k4'])
      
    • getset(name, value)

      设置新值并获取原来的值
      
    • getrange(key, start, end)

      # 获取子序列(根据字节获取,非字符)
      # 参数:
          # name,Redis 的 name
          # start,起始位置(字节)
          # end,结束位置(字节)
      
    • setrange(name, offset, value)

      # 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
      # 参数:
          # offset,字符串的索引,字节(一个汉字三个字节)
          # value,要设置的值
      
    • setbit(name, offset, value)

      # 对name对应值的二进制表示的位进行操作
       
      # 参数:
          # name,redis的name
          # offset,位的索引(将值变换成二进制后再进行索引)
          # value,值只能是 1 或 0
       
      # 注:如果在Redis中有一个对应: n1 = "foo",
              那么字符串foo的二进制表示为:01100110 01101111 01101111
          所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
              那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
      
    • getbit(name, offset)

      # 获取name对应的值的二进制表示中的某位的值 (0或1)
      
    • bitcount(key, start=None, end=None)

      # 获取name对应的值的二进制表示中 1 的个数
      # 参数:
          # key,Redis的name
          # start,位起始位置
          # end,位结束位置
      
    • bitop(operation, dest, *keys)

      # 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
      # 参数:
          # operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
          # dest, 新的Redis的name
          # *keys,要查找的Redis的name
      # 如:
          bitop("AND", 'new_name', 'n1', 'n2', 'n3')
          # 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
      
    • strlen(name)

      # 返回name对应值的字节长度(一个汉字3个字节)
      
    • incr(self, name, amount=1)

      # 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
      # 参数:
          # name,Redis的name
          # amount,自增数(必须是整数,可以是负数)
      # 注:同incrby
      
    • incrbyfloat(self, name, amount=1.0)

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

      # 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
      # 参数:
          # name,Redis的name
          # amount,自减数(整数)
      
    • append(key, value)

      # 在redis name对应的值后面追加内容
      # 参数:
          key, redis的name
          value, 要追加的字符串
      

    链表(list)

    • lpush(name,values)

      # 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
      # 如:
          # r.lpush('oo', 11,22,33)
          # 保存顺序为: 33,22,11
      # 扩展:
          # rpush(name, values) 表示从右向左操作
      
    • lpushx(name,value)

      # 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
      # 更多:
          # rpushx(name, value) 表示从右向左操作
      
    • llen(name)

      # name对应的list元素的个数
      
    • linsert(name, where, refvalue, value))

      # 在name对应的列表的某一个值前或后插入一个新值
      # 参数:
          # name,redis的name
          # where,BEFORE或AFTER(小写也可以)
          # refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
          # value,要插入的数据
      
    • lset(name, index, value)

      # 对name对应的list中的某一个索引位置重新赋值
       
      # 参数:
          # name,redis的name
          # index,list的索引位置
          # value,要设置的值
      
    • lrem(name, count, value)

      # 在name对应的list中删除指定的值
      # 参数:
          # name,redis的name
          # value,要删除的值
          # count,count=0,删除列表中所有的指定值;
                 # count>0,从前往后删除列表中的个数
                 # count<0,从后往前删除列表中的个数
      
    • lpop(name)

      # 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
       
      # 更多:
          # rpop(name) 表示从右向左操作
      
    • lindex(name, index)

      在name对应的列表中根据索引获取列表元素
      
    • lrange(name, start, end)

      # 在name对应的列表分片获取数据
      # 参数:
          # name,redis的name
          # start,索引的起始位置
          # end,索引结束位置  
      print(re.lrange('aa',0,re.llen('aa')))
      
    • ltrim(name, start, end)

      # 在name对应的列表中移除没有在start-end索引之间的值
      # 参数:
          # name,redis的name
          # start,索引的起始位置
          # end,索引结束位置(大于列表长度,则代表不移除任何)
      
    • rpoplpush(src, dst)

      # 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
      # 参数:
          # src,要取数据的列表的name
          # dst,要添加数据的列表的name
      
    • blpop(keys, timeout)

      # 将多个列表排列,按照从左到右去pop对应列表的元素
      # 参数:
          # keys,redis的name的集合
          # timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
      # 更多:
          # r.brpop(keys, timeout),从右向左获取数据
      爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
      
    • brpoplpush(src, dst, timeout=0)

      # 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
       
      # 参数:
          # src,取出并要移除元素的列表对应的name
          # dst,要插入元素的列表对应的name
          # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
      
    • 自定义增量迭代(生成器的使用场景)

      # 由于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)
      

    哈希类型(hash)

    • hset(name, key, value)

      # name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
       
      # 参数:
          # name,redis的name
          # key,name对应的hash中的key
          # value,name对应的hash中的value(只能是字符串类型)
      # 注:
          # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
      
    • hmset(name, mapping)

      # 在name对应的hash中批量设置键值对
      # 参数:
          # name,redis的name
          # mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
      # 如:
          # r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
      
    • hget(name,key)

      # 在name对应的hash中获取根据key获取value
      
    • hmget(name, keys, *args)

      # 在name对应的hash中获取多个key的值
      # 参数:
          # name,reids对应的name
          # keys,要获取key集合,如:['k1', 'k2', 'k3']
          # *args,要获取的key,如:k1,k2,k3
      # 如:
          # r.mget('xx', ['k1', 'k2'])
          # 或
          # print r.hmget('xx', 'k1', 'k2')
      
    • hgetall(name)

      # 获取name对应hash的所有键值
      print(re.hgetall('xxx').get(b'name'))
      
    • hlen(name)

      # 获取name对应的hash中键值对的个数
      
    • hkeys(name)

      # 获取name对应的hash中所有的key的值
      
    • hvals(name)

      # 获取name对应的hash中所有的value的值
      
    • hexists(name, key)

      # 检查name对应的hash是否存在当前传入的key
      
    • hdel(name,*keys)

      # 将name对应的hash中指定key的键值对删除
      print(re.hdel('xxx','sex','name'))
      
    • hincrby(name, key, amount=1)

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

      # 自增name对应的hash中的指定key的值,不存在则创建key=amount
      # 参数:
          # name,redis中的name
          # key, hash对应的key
          # amount,自增数(浮点数)
      # 自增name对应的hash中的指定key的值,不存在则创建key=amount
      
    • hscan(name, cursor=0, match=None, count=None)

      # 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
      # 参数:
          # 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时,表示数据已经通过分片获取完毕
      
    • hscan_iter(name, match=None, count=None)

      # 利用yield封装hscan创建生成器,实现分批去redis中获取数据
      # 参数:
          # match,匹配指定key,默认None 表示所有的key
          # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
      # 如:
          # for item in r.hscan_iter('xx'):
          #     print item
      

    集合(set)

    """
    sadd key member1 member2 ...
    sdiff key1 key2 ...
    sdiffstore newkey key1 key2 ...
    sinter key1 key2 ...
    sunion key1 key2 ...
    smembers key
    spop key
    """
    

    有序集合(zset)

    """
    zadd key grade1 member1 grade2 member2 ...
    zincrby key grade member
    zrange key start end
    zrevrange key start end
    """
    

    其他操作

    • delete(*names)

      # 根据删除redis中的任意数据类型
      conn.delete(*['age', 'name']) # 0|1
      
    • exists(name)

      # 检测redis的name是否存在
      conn.exists('name') # 0|1
      
    • keys(pattern='*')

      # 根据模型获取redis的name
      # 更多:
          # KEYS * 匹配数据库中所有 key 。
          # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
          # KEYS h*llo 匹配 hllo 和 heeeeello 等。
          # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 
      
    • expire(name ,time)

      # 为某个redis的某个name设置超时时间
      
    • rename(src, dst)

      # 对redis的name重命名为
      
    • 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中没有真正意义上的回滚操作,要想实现事务操作需要使用pipline管道

    import redis
     
    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
     
    r = redis.Redis(connection_pool=pool)
     
    # pipe = r.pipeline(transaction=False)
    pipe = r.pipeline(transaction=True)
    pipe.multi()
    pipe.set('name', 'alex')
    pipe.set('role', 'sb')
     
    pipe.execute()  # 这句话,才真正的去执行将上面的操作同步到redis中
    

    Django中使用redis

    方式一

    utils文件夹下,建立redis_pool.py

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

    视图函数中使用:

    import redis
    from django.shortcuts import render,HttpResponse
    from utils.redis_pool import POOL
    
    def index(request):
        conn = redis.Redis(connection_pool=POOL)
        conn.hset('kkk','age',18)
    
        return HttpResponse('设置成功')
    def order(request):
        conn = redis.Redis(connection_pool=POOL)
        conn.hget('kkk','age')
    
        return HttpResponse('获取成功')
    

    方式二:

    安装django-redis模块

    pip3 install django-redis

    setting里配置:

    # 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",
            }
        }
    }
    

    视图函数:

    # 1、直接使用cache进行存取,会自动存入redis中
    from django.core.cache import cache
    cache.set(key, value, timeout)
    cache.get(key)
    # 2、也可以使用下面的进心存取
    from django_redis import get_redis_connection
    conn = get_redis_connection('default')
    print(conn.hgetall('xxx'))
    
  • 相关阅读:
    数据库基本操作
    守护线程
    线程使用的场景
    创建多线程
    用正则表达式去截取网页里文字的方法。参数为读取的网页源代码
    文章生成器,Split方法截取字符串。从硬盘读取文件,和向硬盘存储文件参考代码
    winform 阶段学习总结
    Windowform 窗体关联数据库存储,读取图片,参考代码
    windows form窗体应用程序,建一个记事本参考代码,重点是打开,保存,另存为
    js实现相册翻页,滚动,切换,轮播功能
  • 原文地址:https://www.cnblogs.com/guanxiying/p/13376012.html
Copyright © 2020-2023  润新知