Redis是一种内存缓存服务器,也叫内存数据库,它的功能比Memcached更强大。 Redis是一个高性能的 key-value 对存储系统。它支持存储的value类型很多,包括string(字符串)、list(链表)、hash(哈希)、set(无序集合)、zset(有序集合)。它把整个数据库全加载到内存当中进行操作,通过异步操作定期把内存数据库中的数据flush到硬盘上保存。因为是纯内存操作,所以Redis的性能非常出色,每秒处理10万次以上的读写操作。
Redis缓存服务器的优点:
支持丰富的数据类型
支持两种数据持久化方式:Snapshotting(快照)和 Append Only file(追加到日志记录文件)
支持主从复制
支持海量数据的高效率存储和访问
关于linux环境下Redis的安装与配置,可参考: http://blog.csdn.net/lamp_yang_3533/article/details/52518706
1. Redis的key和value Redis本质上是key-value对的内存数据库,key(键)使用字符串存储,但是key中不能出现空格或者换行符 \n,原因是空格和换行符都是Redis的特殊字符,但只限于key。value可以使用任何字符。 Redis以换行符 \n 作为命令结束符,所以在key中不能存在 \n,否则就会报错。此外,Redis以空格作为命令和参数的分隔符,所以在key中也不能存在空格。尽量使用较短的key,因为较短的key可以节省内存和带宽。
Redis存储的value支持多种数据类型,包括string(字符串)、list(链表)、hash(哈希)、set(无序集合)、zset(有序集合)。下面简单介绍一些这些数据类型的特点:
string类型:string数据类型是二进制安全的,可以把图片、css文件、视频文件等保存在string中,为了提供网站的运行速度,可以用string类型缓存一些静态文件,如:图片、css文件等。string类型支持增量操作,可用作统计计算,如统计网站访问次数。
list类型:list数据类型指key对应的value是一个双向链表结构,所以list类型提供链表支持的所有操作。list类型在互联网应用中非常有用,例如存放微博中“我的关注列表”或者论坛中所有的回帖ID。另外,使用list还可以实现消息队列功能,减轻数据库的压力。消息队列类似于现实生活中的排队,每次有消息到达时就把消息放进队列尾部,取出消息时就从队列头部取出。要用list实现消息队列,先用rpush命令把消息放进队列尾部,然后使用lpop命令把消息从队列头部取出。
set类型:set数据类型是一种无序集合。优点是快速查找元素是否存在,用于记录一些不能重复的数据。例如:网站中注册的用户名,如果要注册的用户名已经存在于集合中,就拒绝此用户注册。 set类型通常用于记录做过某些事情。例如:在投票系统中,每个用户一天只能投票一次,那么可以使用set类型来记录某个用户的投票情况,只需要以日期作为key,将用户ID作为集合中的元素即可。要查看某个用户今天是否投过票,只需以今天的日期作为key去集合中查询用户ID是否存在。
zset类型:即sorted set类型。zset类型和set类型很相似,都是string类型元素的集合,不同的是zset类型属于有序集合,它通过一个double类型的整数score对集合中的元素进行排序。zset通过SkipList(跳跃表)和HashTable组合完成。SkipList负责排序,而HashTable负责保存数据。
set类型能做的事情zset也可以做,而且zset还可以完成一些set不能做的事情,例如使用zset构建一个具有优先级的队列,这也是list类型不能实现的。 zset类型在Web应用中非常有用。例如,排行榜应用中按“顶贴”次数排序,方法是:将排序的值设置成zset的score值,将具体数据设置成相应的value,用户每次按“顶贴”按钮时,只需执行zadd命令修改score的值。
hash类型:hash类型是每个key对应一个HashTable,hash类型适合存储对象,例如用户信息对象,把用户ID作为key,可以把用户信息保存到hash类型中。新建一个hash类型对象时,为了节省内存,Redis使用zipmap存储数据。这个zipmap并不是真正的HashTable,但是相比普通HashTable,zipmap节省不少内存。如果field或value的大小超出一定限制,Redis在内部自动将zipmap替换成正常的HashTable存储。修改配置文件的hash_max_zipmap_entries和hash_max_zipmap_value选项,可设置这两个限制。
2. Redis排序命令详解 Redis支持对list、set、zset类型进行排序,sort命令完整格式如下: SORT key[By pattern][LIMIT start count][GET pattern][ASC | DESC][ALPHA][STORE dstkey]下面详细说明sort命令个选项:(1)SORT key 这是最简单的情况,不设置任何选项就是对集合元素进行简单排序,并返回排序后的结果,例如: lpush mylist 2 lpush mylist 1 lpush mylist 3 sort mylist // 输出: "1"、"2"、"3"
(2)[ASC | DESC][ALPHA] sort命令默认排序方式是升序排序(ASC),也可以降序排序或者按照字母顺序排序。降序排序在sort命令后加上DESC选项,如果想按字母顺序排序,加上ALPHA选项。ALPHA和DESC可以同时使用,例如: lpush namelist Jack lpush namelist Tom lpush namelist Lily sort namelist alpha sort namelist alpha desc(3)[BY pattern] sort命令可以按照集合元素自身的值排序,还可以按照指定模式(pattern)将集合元素内容组合成新key,并按新key对应的内容排序,完成后返回排好序的集合元素。下面使用第一个例子中的mylist集合作为排序对象: set name1 102 set name2 103 set name3 101 sort mylist by name* // 输出:"3"、"1"、"2" 模式 name* 代表使用mylist 集合中元素的值填充 * ,按照name1、name2、name3这三个key对应的值排序,返回的是排序后mylist集合中的元素。
4. [LIMIT start count] 上面的例子返回的结果都是排序后集合的全部元素,使用limit选项能够限定返回结果的数量。例如: sort mylist limit 0 2 // 输出:"1"、"2" start下标从0开始,0表示第一个元素,count表示获取的元素的个数。
5. [STORE dstkey] 经常对集合按照固定模式排序,把排序结果缓存起来可以较少CPU的开销。使用STORE选项将排序结果保存到指定的key中。例如: sort mylist by name*store cachelist type cachelist lrange cachelist 0 -1 // 输出:"3"、"1"、"2"
3. 事务处理 redis对事务的支持目前还比较简单。它只能保证一个client(客户端)发起的事务中的命令可以连续的执行,中间不会插入其他的client的命令。当一个client在一个连接中发出 multi 命令时,这个连接会进入一个事务,后续的命令不会立即执行,而是先放到一个队列中。当执行 exec 命令时,redis才会顺序执行队列中的所有命令,之后退出事务;当执行 discard 命令时,redis会废弃事务的命令队列并退出事务。
一般情况下,Redis接收到一个客户端连接发来的命令后,会立刻执行并返回结果。但是,当客户端连接发出multi命令时,此连接便进入一个事务上下文,Redis把此连接发来的命令存入一个队列中。当此连接发出exec命令时,Redis便开始按顺序执行队列中的所有命令,并将事务中所有命令的结果打包一起返回给客户端,即提交事务后退出事务。如: multi set num 1 incr num exec 从这个例子可以看出,set num 1 和 incr num 命令发出以后并没有立刻执行,而是存放到事务的命令队列中。当调用 exec 命令时,这两个命令才开始连续执行,最后返回这两个命令执行后的结果。可调用 discard 命令取消事务,例如: multi set count 100 incr count discard get count 从这个例子可以看出,set count 100 和 incr count 命令都没有执行,discard 命令的作用是清空事务的命令队列并退出事务上下文。
注意:Redis只能保证事务中的每个命令能够连续执行,但是如果事务中有命令执行失败,Redis无法进行回滚操作。也就是说事务中的命令要么全部执行,要么全部取消,我们无法从事务执行过程中的失败处进行回滚。
4. 持久化 Redis是基于内存的数据库,内存数据库有个严重的弊端:突然宕机或者断电时,内存中的数据就会丢失。为了解决这个问题,redi提供了两种持久化的方式:
snapshotting(内存快照),默认方式
append-only file(日志追加,缩写为aof) Redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到硬盘来保证持久化。
(1)内存快照快照是默认的持久化方式。这种方式是将redis保存在内存中的数据以快照的方式写入二进制文件,默认的文件名 为dump.rdb。可以通过修改配置文件,来设置自动快照。
vi /usr/local/redis/etc/redis.conf save 900 1 # 每900秒,数据更改1次,就发起快照保存 save 300 10 # 每300秒,数据更改10次,则发起快照保存 save 60 10000 # 每60秒,数据更改10000,则发起快照保存
上面设置了多个内存快照保存方案,只要其中一个条件成立,Redis都会进行一次内存快照操作。
Redis每隔一段时间进行一次内存快照操作,客户端使用save或者bgsave命令,告诉Redis需要做一次内存快照操作。save命令在主线程中保存内存快照,Redis使用单线程处理所有请求,执行save命令可能阻塞其他客户端请求,从而导致不能快速响应请求,所以建议不要使用save命令。另外要注意,内存快照每次都把内存数据完整地写入硬盘,而不是只写入增量数据。所以如果数据量很大,写入操作比较频繁,就会严重影响性能。
由于快照方式是在一定间隔时间执行一次快照保存,所以如果redis出现问题,就会丢失最后一次快照后的所有修改。
(2)日志追加 日志追加(aof)方式比快照方式有更好的持久化性,如果启用了aof,Redis会将每一个收到的写命令通过write函数追加到文件appendonly.aof中,当Redis重启时,它会执行该文件中的所有命令,这样就可以在内存中重建整个redis数据库的内容。
另外,操作系统内核的 I/O 接口可能存在缓存,所以日志追加方式不可能立即写入文件,这样就有可能丢失部分数据。幸运的是,Redis提供了解决方法,通过修改配置文件,告诉Redis应该在什么时候使用fsync函数强制操作系统把缓存中的写命令写入磁盘中的日志文件。有以下三种方法: appendonly yes #启用aof持久化方式 # appendfsync always #每次收到写命令就立即写入磁盘,性能最差,持久化最好 appendfsync everysec #每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中 # appendfsync no #是否写入磁盘完全依赖操作系统,性能最好,持久化没保证
日志追加方式有效地降低了数据丢失的风险,同时也带来另一个问题,即持久化文件(appendonly.aof)不断膨胀。例如调用 incr nums 命令100次,文件就会保存100条该命令,其实99条都是多余的,因为要恢复数据只需要set nums 100。
为了压缩日志文件,Redis提供了bgrewriteaof命令。当Redis收到此命令时,就使用类似于内存快照的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的日志文件。
内存快照和日志追加,各有优缺点,选择哪种持久化方式需要自己衡量。也可以把这两种持久化方式都关闭,实现自己的持久化方式,如使用Berkeley DB或者Tokyo Cabinet。
5. 主从复制 主从复制(也叫主从同步)可以防止主机坏掉导致的网站不能正常运作的问题。Redis支持主从复制,而且配置也很简单。redis的主从复制可以让多个从服务器(slave server)拥有和主服务器(master server)相同的数据库副本。
Redis主从复制的特点:
一个master可以拥有多个slave
多个slave除了可以连接同一个master外,还可以连接其他的slave
不会阻塞master,在slave同步数据时,master可以继续处理客户端的请求
提高了系统的伸缩性,比如多个slave专门用于客户端的读操作
可在master服务器上禁止数据持久化,而只在slave服务器上进行数据持久化操作
Redis主从复制的原理: 主从复制设置很简单,设置好slave服务器后,slave自动和master建立连接,发送SYNC命令。无论是第一次同步建立的连接还是连接断开后重新建立的连接,master都会启动一个后台进程,将内存数据以快照方式写入文件中,同时master主进程开始收集新的写命令并且缓存起来。master后台进程完成内存快照操作后,把数据文件发给slave,slave将文件保存到磁盘上,然后将数据加载到内存中。接着master把缓存的命令发给slave,后续master收到的写命令都通过开始建立的连接发送给slave。当master与slave断开连接,slave自动重新建立连接。如果master同时收到多个slave发来的同步请求,其只启动一个进程写数据库镜像,然后发送给所有的slave。
Redis主从复制的过程,分为两个阶段,第一个阶段如下:(1)slave服务器主动连接到master服务器。(2)slave服务器发送SYNC命令到master服务器请求同步数据。(3)master服务器备份数据库到rdb文件。(4)master服务器将该rdb文件传输给slave服务器。(5)slave服务器清空数据库数据,把rdb文件数据导入数据库中。
完成第一阶段,接下来master服务器把用户所有更改数据的操作(写操作),通过命令的形式转发给slave服务器,slave服务器只需执行master服务器发送过来的命令就可以实现后续的同步效果。
相对mysql的主从复制来说,Redis的主从复制配置很简单,只需在slave服务器的配置文件中,添加下面的配置项: slaveof 192.168.1.115 6379 #指定master(主服务器)的ip和端口 masterauth 密码 #如果主服务器设置了安全密码,还要加上这行代码进行授权
配置完成后,重启redis从服务器,就已经通过主从复制实现了数据的同步。
6. 虚拟内存 Redis的数据是保存在内存中的,可能出现物理内存不足的情况。物理内存不足时,Redis使用什么方法解决问题呢?答案是使用“虚拟内存”(VM)。 redis的虚拟内存与操作系统的虚拟内存不是一回事,但思路和目的是相同的。就是暂时把不经常访问的value从内存交换到磁盘中保存,同时,Redis会把value对应的key都放在内存中,当用户访问这些很少访问的数据时,Redis才会把key对应的value从磁盘导入到内存中。从而,腾出宝贵的内存空间用于其他需要经常访问的数据。
对于redis这样的内存数据库,内存总是不够用的。除了可以将数据分割到多个redis server,另外一个提高数据库容量的办法就是使用虚拟内存把那些不经常访问的数据交换到磁盘上。
要想使用虚拟内存,需要在配置文件中开启相关的配置项,如下: vm-enabled yes # 开启vm功能 (默认是没有使用虚拟内存的) vm-swap-file /tmp/redis.swap # 交换出来的value保存的文件路径 vm-max-memory 268435456 # redis使用的最大内存,超过该上限后,Redis开始交换value到磁盘文件 vm-page-size 32 # 每个页面的大小为32字节 vm-pages 134217728 # 最多使用多少个页面,即swap文件最多包含多少页面 vm-max-threads 4 # 用于执行value对象换入换出的工作线程数量。0 表示不使用工作线程
Redis的虚拟内存只把value交换到磁盘中,而key依然存储在内存中,目的是让开启虚拟内存的Redis和完全使用内存的Redis性能基本保持一致。如果由于太多key造成的内存不足的问题,Redis的虚拟内存并不能解决。
vm-max-threads 表示用于执行交换任务的工作线程的数量,建议不要将其设置为0。因为如果设置为0,交换过程就会在主线程进行,从而阻塞其他用户。但也不是设置越大越好,因为太多的工作线程导致操作系统使用更多时间来切换线程,从而降低了效率。推荐将 vm-max-threads 设置为服务器的CPU核心数。