多语言展示
当前在线:1139今日阅读:84今日分享:32

Redis高级使用特性

Redis高级使用特性
方法/步骤
1

主从复制特点:(1)、master可以拥有多个slave(2)、多个slave可以连接同一个master外,还可以连接到其他slave(3)、主从复制不会阻塞master,在同步数据时,master可以据需处理client请求(4)、提高系统的伸缩性。Redis主从复制过程:当配置好slave后,slave与master建立连接,然后发送sync命令。无论是第一次连接还是重新连接,master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存。后台进程完成写文件后,master就发送文件给slave,slave将文件保存到硬盘上,再加载到内存中,接着master就会把缓存的命令转发给slave,后续master将受到的写命令发送给slave。如果master同时收到多个slave发来的同步连接命令,master只会启动一个进程来写数据库镜像,然后发送给所有的slave.

3

事务控制redis 对事务的支持目前还比较简单。redis 只能保证一个 client 发起的事务中的命令可以连 续的执行,而中间不会插入其他 client 的命令。 由于 redis 是单线程来处理所有 client 的请 求的所以做到这点是很容易的。一般情况下 redis 在接受到一个 client 发来的命令后会立即 处理并 返回处理结果,但是当一个 client 在一个连接中发出 multi 命令有,这个连接会进入 一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接 受到 exec 命令后,redis 会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到 一起返回给 client.然后此连接就 结束事务上下文。简单事务控制下面可以看一个例子 redis 127.0.0.1:6379> get age '33' redis 127.0.0.1:6379> multiOKredis 127.0.0.1:6379> set age 10QUEUED redis 127.0.0.1:6379> set age 20QUEUED redis 127.0.0.1:6379> exec1) OK 2) OK redis 127.0.0.1:6379> get age '20'redis 127.0.0.1:6379>从这个例子我们可以看到 2 个 set age 命令发出后并没执行而是被放到了队列中。调用 exec 后 2 个命令才被连续的执行,最后返回的是两条命令执行后的结果。如何取消一个事务我们可以调用 discard 命令来取消一个事务,让事务回滚。接着上面例子redis 127.0.0.1:6379> get age '20' redis 127.0.0.1:6379> multi OK redis 127.0.0.1:6379> set age 30QUEUED redis 127.0.0.1:6379> set age 40 QUEUEDredis 127.0.0.1:6379> discardOK redis 127.0.0.1:6379> get age '20' redis 127.0.0.1:6379>可以发现这次 2 个 set age 命令都没被执行。discard 命令其实就是清空事务的命令队列并退 出事务上下文,也就是我们常说的事务回滚。

4

乐观锁复杂事务控制乐观锁:大多数是基于数据版本(version)的记录机制实现的。何谓数据版本?即为数据增 加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个 “version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加 1。 此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版 本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。 乐观锁实例:假设数据库中帐户信息表中有一个 version 字段,当前值为 1;而当前帐户余 额字段(balance)为$100。下面我们将用时序表的方式来为大家演示乐观锁的实现原理:

5

这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果来覆盖操作员 A 的操作结果 的可能。 即然乐观锁比悲观锁要好很多,redis 是否也支持呢?答案是支持, redis 从 2.1.0 开始就支持乐观锁了,可以显式的使用 watch 对某个 key 进行加锁,避免悲观锁带来的一系列问题。 Redis 乐观锁实例:假设有一个 age 的 key,我们开 2 个 session 来对 age 进行赋值操作,我 们来看一下结果如何。

6

从以上实例可以看到在 第一步,Session 1 还没有来得及对 age 的值进行修改 第二步,Session 2 已经将 age 的值设为 30 第三步,Session 1 希望将 age 的值设为 20,但结果一执行返回是 nil,说明执行失败,之后 我们再取一下 age 的值是 30,这是由于 Session 1 中对 age 加了乐观锁导致的。 watch 命令会监视给定的 key,当 exec 时候如果监视的 key 从调用 watch 后发生过变化,则整 个事务会失败。也可以调用 watch 多次监视多个 key.这 样就可以对指定的 key 加乐观锁了。 注意 watch 的 key 是对整个连接有效的,事务也一样。如果连接断开,监视和事务都会被自 动清除。当然了 exec,discard,unwatch 命令都会清除连接中的所有监视。 redis 的事务实现是如此简单,当然会存在一些问题。第一个问题是 redis 只能保证事务的每 个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,比如使用的命令 类型不匹配。下面将以一个实例的例子来说明这个问题:

7

从这个例子中可以看到,age 由于是个数字,那么它可以有自增运算,但是 name 是个字符 串,无法对其进行自增运算,所以会报错,如果按传统关系型数据库的思路来讲,整个事务 都会回滚,但是我们看到 redis 却是将可以执行的命令提交了,所以这个现象对于习惯于关 系型数据库操作的朋友来说是很别扭的,这一点也是 redis 今天需要改进的地方。4 持久化机制 redis 是一个支持持久化的内存数据库,也就是说 redis 需要经常将内存中的数据同步到磁盘 来保证持久化。redis 支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另 一种是 Append-only file(缩写 aof)的方式。下面分别介绍: 4.4.1 snapshotting 方式 快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中, 默认的文件名为 dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置 redis 在 n 秒内如果超过 m 个 key 被修改就自动做快照,下面是默认的快照保存配置 save 900 1 #900 秒内如果超过 1 个 key 被修改,则发起快照保存 save 300 10 #300 秒内容如超过 10 个 key 被修改,则发起快照保存 save 60 10000 下面介绍详细的快照保存过程: 1.redis 调用 fork,现在有了子进程和父进程。 2. 父进程继续处理 client 请求,子进程负责将内存内容写入到临时文件。由于 os 的实时复 制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时 os 会为父 进程要修改的页面创建副本,而不是写共享的页面。所以子进程地址空间内的数据是 fork时刻整个数据库的一个快照。 3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。 client 也可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。save 操作是在主线程 中保存快照的,由于 redis 是用一个主线程来处理所有 client 的请求,这种方式会阻塞所有 client 请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整 写入到磁盘一次,并不是增量的只同步变更数据。如果数据量大的话,而且写操作比较多, 必然会引起大量的磁盘 io 操作,可能会严重影响性能。 下面将演示各种场景的数据库持久化情况 redis 127.0.0.1:6379> set name HongWan OK redis 127.0.0.1:6379> get name 'HongWan' redis 127.0.0.1:6379> shutdown redis 127.0.0.1:6379> quit 我们先设置了一个 name 的键值对,然后正常关闭了数据库实例,数据是否被保存到磁盘了 呢?我们来看一下服务器端是否有消息被记录下来了: [6563] 09 Aug 18:58:58 * The server is now ready to accept connections on port 6379 [6563] 09 Aug 18:58:58 - 0 clients connected (0 slaves), 539540 bytes in use [6563] 09 Aug 18:59:02 - Accepted 127.0.0.1:58005 [6563] 09 Aug 18:59:03 - 1 clients connected (0 slaves), 547368 bytes in use [6563] 09 Aug 18:59:08 - 1 clients connected (0 slaves), 547424 bytes in use [6563] 09 Aug 18:59:12 # User requested shutdown... [6563] 09 Aug 18:59:12 * Saving the final RDB snapshot before exiting. [6563] 09 Aug 18:59:12 * DB saved on disk [6563] 09 Aug 18:59:12 # Redis is now ready to exit, bye bye... [root@localhost redis-2.2.12]# 从日志可以看出,数据库做了一个存盘的操作,将内存的数据写入磁盘了。正常的话,磁盘 上会产生一个 dump 文件,用于保存数据库快照,我们来验证一下: [root@localhost redis-2.2.12]# ll 总计 188 -rw-rw-r-- 1 root root 9602 2011-07-22 00-RELEASENOTES -rw-rw-r-- 1 root root 55 2011-07-22 BUGS -rw-rw-r-- 1 root root 84050 2011-07-22 Changelog drwxrwxr-x 2 root root 4096 2011-07-22 client-libraries -rw-rw-r-- 1 root root 671 2011-07-22 CONTRIBUTING -rw-rw-r-- 1 root root 1487 2011-07-22 COPYING drwxrwxr-x 4 root root 4096 2011-07-22 deps drwxrwxr-x 2 root root 4096 2011-07-22 design-documents drwxrwxr-x 2 root root 12288 2011-07-22 doc -rw-r--r-- 1 root root 26 08-09 18:59 dump.rdb -rw-rw-r-- 1 root root 652 2011-07-22 INSTALL -rw-rw-r-- 1 root root 337 2011-07-22 Makefile -rw-rw-r-- 1 root root 1954 2011-07-22 README -rw-rw-r-- 1 root root 19067 08-09 18:48 redis.conf drwxrwxr-x 2 root root 4096 08-05 19:12 src drwxrwxr-x 7 root root 4096 2011-07-22 tests -rw-rw-r-- 1 root root 158 2011-07-22 TODO drwxrwxr-x 2 root root 4096 2011-07-22 utils [root@localhost redis-2.2.12]# 硬盘上已经产生了一个数据库快照了。这时侯我们再将 redis 启动,看键值还是否真的持久 化到硬盘了。 redis 127.0.0.1:6379> keys * 1) 'name' redis 127.0.0.1:6379> get name 'HongWan' redis 127.0.0.1:6379> 数据被完全持久化到硬盘了。硬盘上已经产生了一个数据库快照了。这时侯我们再将 redis 启动,看键值还是否真的持久 化到硬盘了。 redis 127.0.0.1:6379> keys * 1) 'name' redis 127.0.0.1:6379> get name 'HongWan' redis 127.0.0.1:6379> 数据被完全持久化到硬盘了。 4.4.2 aof 方式 另外由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外 down 掉的话,就会丢 失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用 aof 持久化 方式。下面介绍 Append-only file: aof 比快照方式有更好的持久化性,是由于在使用 aof 持久化方式时,redis 会将每一个收到 的写命令都通过 write 函数追加到文件中(默认是 appendonly.aof)。当 redis 重启时会通过重 新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于 os 会在内核中缓 存 write 做的修改,所以可能不是立即写到磁盘上。这样 aof 方式的持久化也还是有可能会 丢失部分修改。不过我们可以通过配置文件告诉 redis 我们想要通过 fsync 函数强制 os 写入 到磁盘的时机。有三种方式如下(默认是:每秒 fsync 一次) appendonly yes //启用 aof 持久化方式 # appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化 appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中 # appendfsync no //完全依赖 os,性能最好,持久化没保证 接下来我们以实例说明用法: redis 127.0.0.1:6379> set name HongWan OK redis 127.0.0.1:6379> set age 20 OK redis 127.0.0.1:6379> keys * 1) 'age'我们先设置 2 个键值对,然后我们看一下系统中有没有产生 appendonly.aof 文件 [root@localhost redis-2.2.12]# ll 总计 184 -rw-rw-r-- 1 root root 9602 2011-07-22 00-RELEASENOTES -rw-r--r-- 1 root root 0 08-09 19:37 appendonly.aof -rw-rw-r-- 1 root root 55 2011-07-22 BUGS -rw-rw-r-- 1 root root 84050 2011-07-22 Changelog drwxrwxr-x 2 root root 4096 2011-07-22 client-libraries -rw-rw-r-- 1 root root 671 2011-07-22 CONTRIBUTING -rw-rw-r-- 1 root root 1487 2011-07-22 COPYING drwxrwxr-x 4 root root 4096 2011-07-22 deps drwxrwxr-x 2 root root 4096 2011-07-22 design-documents drwxrwxr-x 2 root root 12288 2011-07-22 doc -rw-rw-r-- 1 root root 652 2011-07-22 INSTALL -rw-rw-r-- 1 root root 337 2011-07-22 Makefile -rw-rw-r-- 1 root root 1954 2011-07-22 README -rw-rw-r-- 1 root root 19071 08-09 19:24 redis.conf drwxrwxr-x 2 root root 4096 08-05 19:12 src drwxrwxr-x 7 root root 4096 2011-07-22 tests -rw-rw-r-- 1 root root 158 2011-07-22 TODO drwxrwxr-x 2 root root 4096 2011-07-22 utils [root@localhost redis-2.2.12]# 结果证明产生了,接着我们将 redis 再次启动后来看一下数据是否还在 [root@localhost redis-2.2.12]# src/redis-cli redis 127.0.0.1:6379> keys * 1) 'age' 2) 'name' redis 127.0.0.1:6379> 数据还存在系统中,说明系统是在启动时执行了一下从磁盘到内存的 load 数据的过程。 aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test 命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。因为要恢复数 据库的状态其实文件中保存一条 set test 100 就够了。为了压缩 aof 的持久化文件。redis 提 供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令 的方式保存到临时文件中,最后替换原来的文件。具体过程如下 1、redis 调用 fork ,现在有父子两个进程 2、子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令

推荐信息