redis数据库的使用

一、安装

  • 下载-----打开redis官方网站,推荐下载稳定版本(stable)

    wget https://download.redis.io/releases/redis-6.2.1.tar.gz
  • 解压

    tar -zxvf redis-6.2.1.tar.gz
  • 改名并移动到/usr/local目录下(推荐)

    sudo mv redis-6.2.1 redis
    sudo mv ./redis /usr/local/
  • 编译安装

    cd /usr/local/redis
    make && sudo make install

二、配置

  /usr/local/redis目录下,文件redis.conf为配置文件

  • 需要远程访问,可将此行注释:
    bind 127.0.0.1
  • 默认端口:6379,可以修改
    port 6379
  • 是否以守护进程运行
    daemonize no|yes

    如果以守护进程运行,则不会堵塞命令行,否则当前终端被阻塞,推荐设为yes

  • 设置密码
    注释掉:requirepass foobared
    #设置密码
    requirepass 需要设置的密码
  • 数据文件名
    dbfilename dump.rdb

    数据库中添加键值后,会发现在当前运行的目录下创建了一个文件:dump.rdb,这个文件用于将数据持久化存储

  • 数据文件存储路径
    dir ./ #推荐修改为 dir /var/lib/redis/

三、运行

  • 修改配置文件存放目录
    sudo mkdir /etc/redis
    sudo cp /usr/local/redis/redis.conf /etc/redis/redis.conf

    一般配置文件都存放在/etc/目录下

  • 配置文件启动(方式一)
    sudo redis-server /etc/redis/redis.conf

    直接运行redis-server会阻塞当前终端

  • 后台启动服务(方式二)
    redis-server & #执行命令后,按两下回车键
  • 关闭服务
    ps -aux | grep redis
    sudo kill -9 进程号(pid)
  • 启动redis客户端
    redis-cli 或者  redis-cli -a 密码

 四、数据库以及键的命令

  • 数据库命令

    • 切换数据库
      select 数据库名 #0-15共16个数据库,默认数据库为0
    • 清空当前数据库数据
      flushdb
    • 清空所有数据库数据
      flushall
  • 键命令

    • 查找键
      keys pattern 
      keys * #查找所有
      keys n* #查找以n开头的键
    • 判断键是否存在
      exists key [key ...]

      不存在就返回0,如果判断多个,则返回存在的个数,比如判断3个键,只有2个存在,则返回2

    • 查看键对应的值类型
      type key
    • 删除键及对应的value
      del key [key ...]
    • 设置过期时间,以秒为单位
      expire key seconds

      创建时没有设置过期时间则一直存在,直到使用del移除

    • 查看有效时间,以秒为单位
      ttl key 

五、数据操作

redis是key-value的数据,所以每个数据都是一个键值对

键的类型以及元素的类型均为字符串

值的类型可分为以下5种:

    1. 字符串string
    2. 哈希hash
    3. 列表list
    4. 集合set
    5. 有序集合zset

string

string是redis最基本的类型,是二进制安全的,即可以为任何数据,比如数字、图片、序列化对象等,最大能存储512MB数据

设置:

  • 设置键值
    set key value
  • 设置键值及过期时间,以秒为单位
    setex key seconds value
  • 设置多个键值
    mset key value [key value ...]

获取:

  • 根据键获取值,如果不存在此键则返回nil
    get key
  • 根据多个键获取多个值
    mget key [key ...]
  • 获取值长度
    strlen key

运算:(值必须是数字)

  • 键对应的值加1
    incr key
  • 值加整数
    incrby key increment
  • 值减1
    decr key
  • 值减整数
    decrby key decrement

其他:

  • 追加值
    append key value

    例如原有的键对应值是"hello",append key "world"后,值变成"helloworld"

hash

用于存储对象,对象的格式为键值对

设置:

  • 设置单个属性
    hset key field value

    注:hset现在也能设置多个属性

  • 设置多个属性
    hmset key field value [field value ...]

获取:

  • 获取单个属性的值
    hget key field
  • 获取多个属性的值
    hmget key field [field ...]
  • 获取所有属性以及值
    hgetall key
  • 获取所有属性
    hkeys key
  • 获取所有值
    hvals key 
  • 获取属性个数
    hlen key
  • 获取某个属性值的长度
    hstrlen key field

其他:

  • 判断某个属性是否存在
    hexists key field
  • 删除属性和值
    hdel key field [field ...]

list

列表的元素类型为string,在列表的头部或者尾部添加元素,按照插入顺序排序

插入:

  • 头部插入数据
    lpush key element [element ...]

    如果插入多个数据,后插入的数据会在前面,比如lpush list1 'a' 'b' 'c',结果list1中的数据顺序是'c' ,'b', 'a'

  • 头部插入数据(键不存在,则不执行命令)
    lpushx key element [element ...]
  • 尾部插入数据
    rpush key element [element ...]
  • 尾部插入数据(键不存在,则不执行命令)
    rpushx key element [element ...]
  • 在指定元素的前面或者后面插入某个元素
    linsert key BEFORE|AFTER pivot element

修改:

  • 修改指定索引的元素值,索引从0开始,也可以使用负数,-1表示最后一个
    lset key index element

获取:

  • 移除并获取第一个元素
    lpop key
  • 移除并获取最后一个元素
     rpop key
    
  • 获取指定索引对应的元素
    lindex key index
  • 获取指定索引范围内的元素(包含stop索引元素
    lrange key start stop
  • 获取列表长度
    llen key  

删除

  • 根据参数count的值,移除列表中与参数element相等的元素。
    lrem key count element

    count=0,移除列表中所有相等的元素;count>0,从前向后搜索并移除count个相等的元素;count<0,从后向前搜索并移除|count|个相等的元素。

其他:

  • 裁剪列表
    ltrim key start stop

    裁剪后,成为原列表的一个子集

  • 移动某一个列表尾部数据到另一个列表头部(也可以是同一个列表)
    rpoplpush source destination

set

无序集合,元素为string类型,具有唯一性,不重复

添加:

  • 添加1个或多个元素,并返回成功添加的元素个数
    sadd key member [member ...]

获取:

  • 随机返回指定个数的元素,不指定个数,默认返回一条数据,指定个数,则返回数组形式
    srandmember key [count]
  • 返回集合所有元素
    smembers key
  • 返回集合元素个数
    scard key

移除

  • 删除并返回指定个数的元素
    spop key [count]

    count不指定,默认移除1个,指定1个,则返回的是数组

  • 删除指定的元素,并返回被删除的元素个数
    srem key member [member ...]

    有则删除,没有则不做任何操作

其他:

  • 移动一个集合中的元素到另一个集合中,操作成功则返回1,否则返回0
    smove source destination member
  • 交集
    sinter key [key ...]
    

    集合共有的部分

  • 差集
    sdiff key [key ...]

    注意先后顺序,前面集合所独有的部分

  • 合集
    sunion key [key ...]
  • 判断元素是否在集合中
    sismember key member

zset

有序集合(sorted set),元素类型为string,具有唯一性,不重复,每个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序,score值可以相同。

设置:

  • 添加
    zadd key score member [score member ...]

    如果元素已经存在,则表示更新操作,更新该元素score值

获取:

  • 升序获取指定元素排名(从0开始)
    zrank key value
  • 降序获取指定元素排名(从0开始)
    zrevrank key value
  • 升序获取指定索引范围内的元素
    zrange key min max
  • 降序获取指定索引范围内的元素
    zrevrange key min max
  • 升序获取指定分值范围内的元素
    zrangebyscore key min max
  • 降序获取指定分值范围内的元素
    zrevrangebyscore key max min WITHSCORES  【会带有分值】
    zrevrangebyscore key max min   【不带分值】
  • 对指定元素分值进行增减操作
    zincrby key amount value

    amount为正数,增加操作,负数为减少操作

  • 删除指定的元素
    zrem key value
  • 升序删除指定索引范围内的元素
    zremrangebyrank key min max
  • 升序删除指定分值范围内的元素
    zremrangebyscore key min max
  • 删除并获取分值最大的指定个数元素
    zpopmax key count

    返回值带上分数,比如[(value1, score1),  (value2, score2)...]

  • 删除并获取分值最小的指定个数元素
    zpopmin key count
  • 获取集合元素个数
    zcard key
  • 统计权重值在某一范围内的元素个数
    zcount key min max
  • 获取某一个元素的权重值
    zscore key member

六、发布订阅

客户端发到频道的消息,将会被推送到所有订阅此频道的客户端。

客户端不需要主动去获取消息,只需要订阅频道,这个频道的内容就会被推送过来。

  • 发布

    publish channel message
  • 订阅

     subscribe channel [channel ...]
  • 取消订阅

    unsubscribe [channel [channel ...]]

    如果不写参数,表示取消订阅所有频道

七、主从配置

一个主服务器可以拥有多个从服务器,一个从服务器又可以拥有多个从服务器,如此下去,形成了强大的多级服务器集群架构。

将ip为192.168.1.10的机器作为主服务器,将ip为192.168.1.11的机器作为从服务器,进行演示。

  • 主服务器配置

    bind 192.168.1.10
    
    # requirepass abc123  可选,设置redis密码

    修改配置文件(/etc/redis/redis.conf文件)

  • 从服务器配置

    bind 192.168.1.11
    slaveof 192.168.1.10 6379
    
    # masterauth abc123    可选,指定Master节点的密码,仅在Master节点设置了requirepass时设置

    注意:在slaveof后面写主机ip,再写端口,而且端口必须写

  • 主服务器上写数据

    set hello world
  • 从服务器上读取数据

    get hello 
  • 查看主从节点

    info Replication

    在客户端命令行输入上述命令,就会显示当前节点角色具体信息

  • 主服务器挂掉如何解决?

    • 手动方案

      #  一、选某个从节点执行命令,使其成为主节点
      slaveof no one
      
      #  二、其它节点设为该新主节点的从(包括恢复后的原主节点)
      slaveof ip port
    • 哨兵模式

      • 安装redis-sentinel
        sudo apt install redis-sentinel
      • 配置文件sentinel.conf
        # ------1、相同部分-------
        port 26379 # 端口
        # 该哨兵监听的主节点(哨兵会基于主节点找到各从节点):其中mymaster为监控对象起的服务器名称,2:判定主节点客观下线所需要的哨兵数量
        # 当主节点挂了,哨兵会自动找到下一个主节点,所以ip地址写第一个主节点即可
        sentinel monitor mymaster 47.94.174.89 6379 2
        
        # 如果master有密码,则需要添加该配置(redis集群需要配置相同的密码,哨兵会自动监听连接到主节点的从节点)
        sentinel auth-pass mymaster 密码
        
        # ------2、不同部分------
        # 以1主2从3哨兵模式演示:
            # a 节点指定为 47.94.174.89
            # b 节点指定为 47.93.39.238
            # c 节点指定为 47.93.235.147
        sentinel announce-ip <当前服务器公网ip>

        注意点:
          1、各redis集群密码需要相同
                2、各redis集群服务器redis.conf中需要指定:masterauth 主节点密码
                3、哨兵集群是基于Redis的订阅/发布模式建立
                4、具体哪个从点会成为主节点,会按照规则:(优先级、偏移量、runid)
        哨兵模式缺点:
                复制延时问题

      • 启动
        redis-sentinel sentinel.conf

        对于3个redis集群,则需要在3个服务器上分别启动哨兵

八、与python交互

  • 安装redis包

    sudo pip3 install redis
  • 引入redis模块

    import redis
  • 创建数据库连接对象

    try:
        r = redis.StrictRedis(host='ip地址',port=6379)
    except Exception as e:
        print(e)
    
    # 不需要调用r.close()方法来关闭连接对象,因为内部__del__()方法中已实现了self.close()
  • 哨兵模式操作

    from redis.sentinel import Sentinel
    
    #生成哨兵连接
    sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
    
    #初始化master连接
    master = sentinel.master_for('mymaster', socket_timeout=0.1, db=0)
    slave = sentinel.slave_for('mymaster',socket_timeout=0.1, db=0)
    
    #使用redis相关命令
    master.set('mymaster', 'yes')
    print(slave.get('mymaster'))
  • 连接池操作

    • 方式一:不用自己实现连接池(内部自动生成)-- 更高效简便

      import redis
      
      # 内部已经自动维护了一个连接池
      con = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DATABASE, password=REDIS_PASSWORD,decode_responses=True)
      
      def test():
          for i in range(50):
              con.get('hello')
      
      # 5个线程操作
      t_list = []
      for i in range(5):
          t = threading.Thread(target=test)
          t.start()
          t_list.append(t)
      for t in t_list:
          t.join()
      
      # 连接池中的可用连接数
      print(len(con.connection_pool._available_connections)) # 5
      
      # con.close()  不需要执行该步操作,因为con.__del__()方法中已经实现了
    • 方式二:自己手动实现连接池

      import redis
      redis_pool = redis.ConnectionPool(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DATABASE, password=REDIS_PASSWORD, decode_responses=True)
      
      def test():
          for i in range(50):
              con = redis.StrictRedis(connection_pool=redis_pool)
              con.get('hello')
      
      # 5个线程操作
      t_list = []
      for i in range(5):
          t = threading.Thread(target=test)
          t.start()
          t_list.append(t)
      for t in t_list:
          t.join()
      
      # 连接池中的可用连接数
      print(len(redis_pool._available_connections)) # 5

       

  • 数据库操作

    • 方式一:根据数据类型,调用相应方法

      #string类型演示
      r.set(key,value)
      r.get(key) #注意返回的是bytes类型,没有则返回None,redis中存储的数值也都是字符串类型
      
      #hash类型
      r.hset(key,field,value) #设置单个属性和值,可以执行多次
      r.hmset(key,{field1:value1,...}) #设置多个属性和值
      r.hget(key,field) #获取单个属性的值
      r.hmget(key,field1,...) #获取多个属性的值,返回的是属性的值构成的列表,bytes类型 r.hgetall(key) #获取所有属性和值,dict类型,其中的键和值都是bytes类型,例如{b'name':b'laowang',b'age':b'28'} r.expire(key,seconds) #设置过期时间 r.hdel(key,field1,...) #删除属性和值 r.delete(key) #删除键,注意是delete而不是del
    • 方式二:利用pipeline缓冲多条命令,然后一次性执行,减少服务器-客户端之间TCP数据库包,从而提高效率

      # 仅使用管道(不开启事务)
      with r.pipeline(transaction=False) as pipe:
          pipe.set('key1','value1')
          pipe.set('key2','value2')
          pipe.set('key3','value3')
          data = pipe.execute() # 返回的是一个有序列表,元素是上面每条命令执行的返回值[True,True,True]
          print(data)
      
      # 开启事务(原子性操作,要么所有命令全部执行,要么全部不执行)
      with r.pipeline() as pipe:   # transaction参数默认为True
          pipe.multi() # 开启事务
          pipe.set('key1','value1')
          pipe.set('key2','value2')
          pipe.set('key3','value3')
          data = pipe.execute() # 返回的是一个有序列表,同上
          print(data)
posted @ 2021-03-25 22:30  eliwang  阅读(957)  评论(0编辑  收藏  举报