Redis事务操作笔记

/ 0评 / 0

一、五大数据类型及应用场景回顾

类型 特点 使用场景
string 简单key-value类型,value可为字符串和数字 常规计数(微博数, 粉丝数等功能)
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象 存储部分可能需要变更的数据(比如用户信息)
list 有序可重复列表 消息队列等
set 无序不可重复列表 存储并计算关系(如微博,关注人或粉丝存放在集合,可通过交集、并集、差集等操作实现如共同关注、共同喜好等功能)
sorted set 每个元素带有分值的集合 各种排行榜

二、事务(ACID特性:原子、一致、隔离、持久性)

  1. 特点

    • 单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断。
    • 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制。
  2. 事务命令

    • MULTI # 开启事务 mysql begin
    • 命令1 # 执行命令
    • 命令2 ... ...
    • EXEC # 提交到数据库执行 mysql commit
    • DISCARD # 取消事务 mysql 'rollback'
  3. 使用步骤

    # 开启事务
    127.0.0.1:6379> MULTI
    OK
    # 命令1入队列
    127.0.0.1:6379> INCR n1
    QUEUED
    # 命令2入队列
    127.0.0.1:6379> INCR n2
    QUEUED
    # 提交到数据库执行
    127.0.0.1:6379> EXEC
    1) (integer) 1
    2) (integer) 1
    
  4. 事务错误处理

    • 命令语法错误,命令入队失败,直接自动discard退出这个事务。
    • 命令语法错误[rdis可自动检查出来,最终取消事务操作] * 这个在命令在执行调用之前会发生错误。例如,这个命令可能有语法错误(错误的参数数量,错误的命令名)

      • 处理方案:语法错误则自动执行discard
    • 命令语法没错,但类型操作有误,则事务执行调用之后失败,无法进行事务回滚。

    • 类型操作有误[rdis无法检查出来,会造成部分执行成功,部分执行失败]

      • 我们执行了一个由于错误的value的key操作(例如对着String类型的value施行了List命令操作)
      • 处理方案:发生在EXEC之后的是没有特殊方式去处理的:即使某些命令在事务中失败,其他命令都将会被执行。

三. pipeline流水线

  1. 定义:批量执行redis命令,减少通信io(客户端技术)
  2. 示例

    import redis
    # 创建连接池并连接到redis
    pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
    r = redis.Redis(connection_pool=pool)
    ​
    pipe = r.pipeline()
    pipe.set('fans',50)
    pipe.incr('fans')
    pipe.incrby('fans',100)
    pipe.execute()
    
  3. 池化技术(pool)-典型使用场景:数据库连接以及多线程操作等。频繁的连接关闭数据库,消耗CPU资源。可采取连接操作完成后,不关闭数据库,放回连接池中,下一个用户需要操作时,无需创建连接,直接从连接池中获取连接。避免频繁连接、关闭数据库带来的资源消耗。

    # 创建连接池并连接到redis
    pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
    r = redis.Redis(connection_pool=pool)
    ​
    def withpipeline(r):
    p = r.pipeline()
    for i in range(1000):
        key = 'test1' + str(i)
        value = i+1
        p.set(key, value)
    p.execute()
    ​
    def withoutpipeline(r):
    for i in range(1000):
        key = 'test2' + str(i)
        value = i+1
        r.set(key, value)
    
  4. python 操作redis事务

    with r.pipeline(transaction=true) as pipe
    pipe.multi()
    pipe.incr("books")
    pipe.incr("books")
    values = pipe.execute()
    
  5. watch乐观锁

    • 作用: 事务过程中,可对指定key进行监听,命令提交时,若被监听key对应的值未被修改时,事务方可提交成功,否则失败。
        > watch books
        OK
        > multi
        OK
        > incr books
        QUEUED
        > exec  # 事务执行失败
        (nil)
    
        watch之后,再开一个终端进入redis
        > incr books  # 修改book值
        (integer) 1
    
  6. python操作watch

    import redis
    import time
    
    r = redis.Redis(password='123456')
    
    def double_account(user_id):
        key = 'account_%s' % user_id
        with r.pipeline(transaction=True) as pipe:
            while True:
                try:
                    pipe.watch(key)
                    # 获取账户余额
                    value = int(r.get(key))
                    # 余额翻倍
                    value *= 2
    
                    print('----sleep is start----')
                    time.sleep(10)
                    print('-----sleep is over------')
    
                    pipe.multi()
                    pipe.set(key, value)
                    pipe.execute()
                    # 余额翻倍后的值
                    print('new value is %s' % value)
                    break
                except redis.WatchError:
                    print('value changed,本次操作取消')
                    continue
        return int(r.get(key))
    
    if __name__ == '__main__':
        print(double_account('tedu'))
    

四、数据持久化

  1. 持久化定义:将数据从掉电易失的内存放到永久存储的设备上
  2. 为什么需要持久化:所有数据都在内存,必须得持久化。
  3. RDB模式(默认开启)
    • 保存真实的数据
    • 将服务器包含的所有数据库数据以二进制文件的形式保存到硬盘里面
    • 默认文件名 :/var/lib/redis/dump.rdb
  4. 创建RDB文件的两种方式

    • 方式一:redis终端中使用SAVE或者BGSAVE命令
    127.0.0.1:6379> SAVE
    OK
    # 特点
    1、执行SAVE命令过程中,redis服务器将被阻塞,无法处理客户端发送的命令请求,在SAVE命令执行完毕后,服务器才会重新开始处理客户端发送的命令请求
    2、如果RDB文件已经存在,那么服务器将自动使用新的RDB文件代替旧的RDB文件
    # 工作中定时持久化保存一个文件
    ​
    127.0.0.1:6379> BGSAVE
    Background saving started
    # 执行过程如下
    1、客户端 发送 BGSAVE 给服务器
    2、服务器马上返回 Background saving started 给客户端
    3、服务器 fork() 子进程做这件事情
    4、服务器继续提供服务
    5、子进程创建完RDB文件后再告知Redis服务器
    ​
    # 配置文件相关
    /etc/redis/redis.conf
    263行: dir /var/lib/redis # 表示rdb文件存放路径
    253行: dbfilename dump.rdb  # 文件名
    ​
    # 两个命令比较
    SAVE比BGSAVE快,因为需要创建子进程,消耗额外的内存
    ​
    # 补充:可以通过查看日志文件来查看redis都做了哪些操作
    # 日志文件:配置文件中搜索 logfile
    logfile /var/log/redis/redis-server.log
    
  5. 方式二:设置配置文件条件满足时自动保存(最常用)

    # redis配置文件默认
    218行: save 900 1
    219行: save 300 10
        表示如果距离上一次创建RDB文件已经过去了300秒,并且服务器的所有数据库总共已经发生了不少于10次修改,那么自动执行BGSAVE命令
    220行: save 60 10000
      1、只要三个条件中的任意一个被满足时,服务器就会自动执行BGSAVE
      2、每次创建RDB文件之后,服务器为实现自动持久化而设置的时间计数器和次数计数器就会被清零,并重新开始计数,所以多个保存条件的效果不会叠加
    
    # 该配置项也可以在命令行执行 [不推荐] 
    redis>save 60 10000
    
  6. RDB缺点

    • 创建RDB文件需要将服务器所有的数据库的数据都保存起来,这是一个非常消耗资源和时间的操作,所以服务器需要隔一段时间才创建一个新的RDB文件,也就是说,创建RDB文件不能执行的过于频繁,否则会严重影响服务器的性能。
    • 可能丢失数据。

五、AOF(AppendOnlyFile) 方案

  1. 存储的是命令,而不是真实数据
  2. 默认不开启
    • 开启方式(修改配置文件)
    ****
    1. /etc/redis/redis.conf
      672行: appendonly yes # 把 no 改为 yes
      676行: appendfilename "appendonly.aof"
    2. 重启服务
      sudo /etc/init.d/redis-server restart
    
  3. AOF持久化原理及优点
    1. 原理:每当有修改数据库的命令被执行时,因为AOF文件里面存储了服务器执行过的所有数据库修改的命令,所以给定一个AOF文件,服务器只要重新执行一遍AOF文件里面包含的所有命令,就可以达到还原数据库的目的。
    2. 优点:用户可以根据自己的需要对AOF持久化进行调整,让Redis在遭遇意外停机时不丢失任何数据,或者只丢失一秒钟的数据,这比RDB持久化丢失的数据要少的多。
  4. 特殊说明
    • 因为: 虽然服务器执行一个修改数据库的命令,就会把执行的命令写入到AOF文件,但这并不意味着AOF文件持久化不会丢失任何数据,在目前常见的操作系统中,执行系统调用write函数,将一些内容写入到某个文件里面时,为了提高效率,系统通常不会直接将内容写入硬盘里面,而是将内容放入一个内存缓存区(buffer)里面,等到缓冲区被填满时才将存储在缓冲区里面的内容真正写入到硬盘里。
    • 所以:
      1. AOF持久化:当一条命令真正的被写入到硬盘里面时,这条命令才不会因为停机而意外丢失。
      2. AOF持久化在遭遇停机时丢失命令的数量,取决于命令被写入到硬盘的时间
      3. 越早将命令写入到硬盘,发生意外停机时丢失的数据就越少,反之亦然。
  5. 策略-配置文件

    # 打开配置文件:/etc/redis/redis.conf,找到相关策略如下
    1、701行: alwarys
       服务器每写入一条命令,就将缓冲区里面的命令写入到硬盘里面,服务器就算意外停机,也不会丢失任何已经成功执行的命令数据
    2、702行: everysec(# 默认)
       服务器每一秒将缓冲区里面的命令写入到硬盘里面,这种模式下,服务器即使遭遇意外停机,最多只丢失1秒的数据
    3、703行: no
       服务器不主动将命令写入硬盘,由操作系统决定何时将缓冲区里面的命令写入到硬盘里面,丢失命令数量不确定
    ​
    # 运行速度比较
    always:速度慢
    everysec和no都很快,默认值为everysec
    
  6. AOF重写

    • 为了让AOF文件的大小控制在合理范围,避免胡乱增长,redis提供了AOF重写功能,通过这个功能,服务器可以产生一个新的AOF文件。
      • 新的AOF文件记录的数据库数据和原由的AOF文件记录的数据库数据完全一样。
      • 新的AOF文件会使用尽可能少的命令来记录数据库数据,因此新的AOF文件的提及通常会小很多。
      • AOF重写期间,服务器不会被阻塞,可以正常处理客户端发送的命令请求。
  7. AOF重写触发

    1、客户端向服务器发送BGREWRITEAOF命令
    127.0.0.1:6379> BGREWRITEAOF
    Background append only file rewriting started
    ​
    2、修改配置文件让服务器自动执行BGREWRITEAOF命令
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    # 解释
    1、只有当AOF文件的增量大于100%时才进行重写,也就是大一倍的时候才触发
        # 第一次重写新增:64M
        # 第二次重写新增:128M
        # 第三次重写新增:256M(新增128M)
    

六、RDB和AOF持久化对比

RDB持久化 AOF持久化
全量备份,一次保存整个数据库 增量备份,一次保存一个修改数据库的命令
保存的间隔较长 保存间隔默认为一秒钟
数据还原速度快 数据还原速度一般,冗余命令多
执行SAVE命令阻塞服务器,手动或自动BDSAVE不会阻塞服务器 平时还是进行AOF重写,都不会阻塞服务器

说明:用redis用来存储真正数据,每一条都不能丢失,都要用always,有的做缓存,有的保存真数据,我可以开多个redis服务,不同业务使用不同的持久化,新浪每个服务器上有4个redis服务,整个业务中有上千个redis服务,分不同的业务,每个持久化的级别都是不一样的。

七、数据恢复(无需手动操作)

既有dump.rdb,又有appendonly.aof,恢复时找谁?先找appendonly.aof。

八、常用配置

  1. 配置文件常用设置总结

    # 设置密码
    1、requirepass password
    # 开启远程连接
    2、bind 127.0.0.1 ::1 注释掉
    3、protected-mode no  把默认的 yes 改为 no
    # rdb持久化-默认配置
    4、dbfilename 'dump.rdb'
    5、dir /var/lib/redis
    # rdb持久化-自动触发(条件)
    6、save 900 1
    7、save 300 10 
    8、save 60  10000
    # aof持久化开启
    9、appendonly yes
    10、appendfilename 'appendonly.aof'
    # aof持久化策略
    11、appendfsync always
    12、appendfsync everysec # 默认
    13、appendfsync no
    # aof重写触发
    14、auto-aof-rewrite-percentage 100
    15、auto-aof-rewrite-min-size 64mb
    # 设置为从服务器
    16、salveof <master-ip> <master-port>
    
  2. Redis相关文件存放路径

    1、配置文件: /etc/redis/redis.conf
    2、备份文件: /var/lib/redis/*.rdb|*.aof
    3、日志文件: /var/log/redis/redis-server.log
    4、启动文件: /etc/init.d/redis-server
    # /etc/下存放配置文件
    # /etc/init.d/下存放服务启动文件
    

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注