一、redis简介
redis是个远程内存数据库。
redis通过复制、持久化、客户端分片,可方便地将redis扩展词一个能保护数百万GB数据,每秒处理上百万次请求的系统。
redis是一个高性能的非关系型数据库,他可以存储key和5类不同类型的value之间的映射,可以将存储在内存的key/value对,持久化到磁盘,然后用复制来扩展读性能;也可以使用客户端分片来扩展写性能。
1.1、redis与memcached对比
redis与memcached性能相差无几,但redis可自动以两种不同的方式将数据写入磁盘,且redis除了存储普通的字符串key之外,还支持其他4中数据结构,而memcached只能存储普通的字符串key。
1.2、redis的持久化特性的作用
1.2.1、redis作为内存缓存,当服务器重启后,数据丢了怎么办呢?
答:redis拥有两种不同的持久化方法:
1)时间点转储(point-in-time-dump):在指定时间内把指定数量的写操作落盘。
2)将所有修改了数据库的命令都写入一个只追加(append-only)的文件里,用户可以根据数据的重要程度,将只追加写入,设置为从不同步、每秒同步一次,或者写入一个命令就同步一次。
1.2.2、redis的读性能扩展怎样扩展?
redis受限于数据存到内存,只一台redis无法满足读取的并发量,为了扩展读性能,redis实现了主从复制。
1.3、redis数据结构简介
redis可以提供key与5中不同的数据结构类型之间的映射,分别为:
- string
- list
- set
- hash
- zset(有序集合)
有一部分redis命令对这5种数据结构是通用的,如del, type, rename等;但也有一部分redis命令只能对特定的一种或两种数据结构使用。
1.3.1、redis中的字符串
字符串有的命令:get, set, del
也可对字符串存储的数值进行自增、自减。
1.3.2、redis中的list
list可以有序地存储字符串。
lpush, rpush命令分别表示把元素放入list的左端和右端;
lpop, rpop命令分别表示从队列的左端和右端弹出元素;
lindex用于获取list在指定位置上的单个元素
lrange获取指定范围内的所有元素
1.3.3、redis的set
list可以存储重复的字符串,但set中的元素值是不能重复的。
set存储无序且不能重复的元素。
sadd:添加元素
srem:删除元素
sismember:查找一个元素是否存在于set中
smembers:获取set中包含的所有元素
sinter:交集计算
sunion:并集计算
sdiff:差集计算
1.3.4、Redis的hash
hash存储key/value映射,hash的value可以是字符串,也可以是数字;且可以对数字值进行自增或自减操作。
1.3.5、Redis的有序集合zset
zset 与hash一样,都是key/value对。
zset 的key被称为member; value被称为score,score必须为浮点数。
zset是redis中唯一既可以根据member访问元素(跟hash一样);也可以根据score及score的排列顺序来访问元素的结构。
与其他结构一样,用户可以对zset进行添加、删除、获取等操作。
zadd:将一个带score的member添加到zset里
zrange:根据元素在zset中所处的位置,从zset中获取多个元素
zrangebyscore:获取zset在给定分支范围内的members
zrem:如果元素在zset里,则删除。
使用示例:
1.4、Redis的发布/订阅
redis也支持sub/pub。
1.5、Redis的排序
sort命令:可以根据字符串, list, set, zset, hash这五种key里面存储的数据,对list, set, zset进行排序。
1.6、Redis事务
redis基本事务用到 multi 和 exec 命令,这种事务可以让客户端在不被其他客户端打断的情况下执行多个命令。即被 multi 和 exec 包围的所有命令,必须一个接一个地执行,直到所偶遇命令全部执行完,然后redis才会处理其他客户端命令。
当redis从一个客户端收到 multi 命令时,redis会将这个客户端之后发送的所有命令都放到一个队列里,直到这个客户端发送 exec 命令为止,然后redis就在不会被打断的情况下,一个接一个地执行存储在队列里的命令。
1.7、Redis的key的过期时间
expire key-name seconds : 单位是秒
二、Redis持久化
2.1、Redis持久化简介
redis支持2种持久化方式:
- snapshotting:快照的方式:将存在于某一时刻的所有数据都写入磁盘。
- append-only file (AOF):只追加文件:在执行写命令时,将被执行的写命令复制到磁盘。
这两种方式可以单独使用,也可以同时使用。
将内存中的数据持久化到磁盘的目的是,为了将数据存储起来以后使用,或为了防止系统故障而将数据备份到远程位置。
2.1.1、snapshotting方式
对于snapshotting方式,快照将被写入dbfilename选项指定的路径上面。如果在新的快照文件创建完毕之前,redis服务崩溃了,那么redis将丢失最近一次创建快照之后写入的所有数据。
创建快照的方法有下面几种:
1、客户端通过向redis发送bgsave命令来创建一个快照。对于支持bgsave命令的平台来说(基本上所有平台都支持,除了windows平台),redis会调用fork来创建一个子进程,然后这个子进程负责将快照写入磁盘,而父进程则继续处理命令请求。
2、客户端也可以铜鼓乡redis发送save命令来创建一个快照。接到save命令的redis服务器在快照创建完毕之前将不再响应任何其他命令。save命令兵不常用,通常只会在没有足够内存去执行bgsave命令时,又或者即使等待持久化操作执行完毕也无所谓时,才会用save命令。
3、如果用户设置了save配置选项,比如save 60 10000, 那么从redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,redis会自动触发bgsave命令。如果用户设置了多个save配置选项,那么当任意一个save配置选项所设置的条件被满足时,redis就会触发一次bgsave命令。
4、当redis通过shutdown命令接收到关闭服务器的请求时,或者接收到标准term信号时,就执行一个save命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在save命令执行完毕之后关闭服务器。
5、当一个redis服务器连接另一个redis服务器,并向对方发送sync命令来开始一次复制操作的时候,如果主服务器目前没有在执行bgsave操作,或者主服务器兵非刚刚执行完bgsave操作,那么主服务器就会执行bgsave命令。
总结:使用快照持久化,仅适用于那些即使丢失一部分数据,也不会造成问题的应用程序,而不能接受这种数据损失的应用程序可使用AOF方式。
适合用snapping的场景:
- 个人开发
- 日志存储
- 大数据存储
2.2、AOF持久化
2.2.1、AOF持久化简介
AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发送的变化。因此,redis只要从头到尾重新执行一次AOF文件保护的所有写命令,就可以恢复AOF文件所记录的数据集。
AOF持久化可以通过设置下面的appendonly tes配置选项来打开:
文件同步的过程:
在想磁盘文件写入时,会发生2件事:
1)当调用file.write()方法对文件进行写入时,写入的内容会被先存储到缓冲区
2)然后操作系统会再将来的某个时刻将缓冲区的内容写入磁盘,而数据只有在被写入磁盘之后,才算是真正地保存到了磁盘里。用户可以通过调用file.flush()方法来请求操作系统尽快地将缓冲区存储的数据写入磁盘,但具体何时执行写入操作,仍由操作系统决定。此外,用户也可以命令操作系统将文件sync到磁盘,sync操作会一直阻塞,直到指定的文件被写入到磁盘为止。
当同步操作执行完毕之后,即使操作系统故障,也不会对被同步的文件造成任何影响。
虽然redis AOF持久化提供了是那种不同的选项,但AOF持久化也有缺陷,就是AOF文件的体积大小。
2.2.2、重写/压缩AOF文件
AOF及可以将丢失数据的窗口降低到1秒,有可以在极短的时间内完成定期的持久化操作,有什么理由我们不使用AOF呢?
答:因为redis会不断把被执行的写命令记录到AOF文件里,AOF文件会越来越大,甚至会磁盘存不下。另外,在redis重启后,会重新执行AOF文件中记录的写命令,如果AOF文件非常大,那么还原操作会执行时间非常长。
为了解决AOF文件大的问题,用户可以向redis发送 BGRewrtieAOF 命令, 该命令会通过移除AOF文件中的冗余命令来重新AOF文件,使AOF文件的体积变得尽可能小。
BGRewrtieAOF的工作原理和bgsave非常类似:redis先创建子检测,然后由子进程负责对AOF文件进行重写。
总结:物物理是AOF持久化,还是快照持久化,将数据持久化到磁盘上后,还需要对持久化文件进行备份,最好是备份到不同的服务器,避免数据丢失。
2.3、复制
2.3.1、复制简介
复制(replication)是让其他服务器拥有一个不断更新的数据副本,从而使得拥有副本的服务器可以处理客户端发送的读请求。
关系型数据库通常会使用一个主服务器(master)向多个从服务器(slave)发送更新,并使用从服务器来处理所有的读请求。redis也采用同样的方式,作为扩展性能的手段。
2.3.2、Redis复制的启动过程
从服务器连接主服务器时的步骤:
redis在复制期间,也会尽可能地处理接受到的命令请求。但是,如果主从服务器之间的网络带宽不足,或者主服务器没有足够的内存来创建子进程和创建记录写命令的缓冲区,那么redis处理命令的请求效率将会受到影响。因此,尽管这不是必须的,但在实际中最好还是让主服务器只使用50%60%的内存,留下30%45%的内存用于执行bgsave命令和创建记录写命令的缓冲区。
设置从服务器的步骤:
- 既可以通过配置选项slaveof host port来将一个redis服务器设置为从服务器,
- 也可以通过向运行中的redis发送slaveof命令来将其设置为从服务器。
从服务器在进行同步时,会先请客自己的所有数据。
警告:redis不支持主主复制(master-master replication):因为redis允许用户在服务器启动之后使用slaveof命令来设置从服务器选项。
2.3.3、主从链
有些人发现,创建多个从服务器,可能会造成网络不可用:当复制需要通过互联网进行,或者需要再不同数据中心之间进行时,尤为如此。redis的主服务器和从服务器并没有什么不同的地方,所以从服务器也可以有自己的从服务器,从而形成了主从链(master/slave chaining)
从服务器对从服务器进行复制,与 从服务器对主服务器进行复制的区别在于,如果从服务器X拥有从服务器Y,那么当从服务器X在执行上表中的步骤4时,它将端口与从服务器Y的连接,从而导致从服务器Y需要重新连接,并重新同步(resync)。
2.3.4、检验磁盘写入
为了检验主服务器是否已经将写数据发送到了从服务器,用户需要再向主服务器写入真正数据之后,在想主服务器写入一个唯一的虚构值(unique dummy value),然后通过检查虚构值是否存在于从服务器,来判断写数据是否已经到达从服务器。
2.4、处理系统故障
如果我们要把redis作为存储数据的唯一手段,就必须确保redis不丢数据。怎样保证极端情况下redis不丢数据呢?
2.4.1、验证快照文件和AOF文件
无论快照持久化还是AOF持久化,都提供了遇到系统故障时进行数据恢复的工具。redis提供了两个命令行程序redis-check-aof和redis-check-dump,他们可以在系统故障发生后,检查AOF文件和快照文件的状态,并在有需要的情况下,对文件进行修复。下面是这2个命令的基本用法:
如果用户在运行redis-check-aof时,给定额–fix参数,那么程序将对AOF文件进行修复:他会扫描给定的AOF文件,寻找不正确或不完整的命令,当发现第一个出错命令的时候,程序会删除出错的命令,以及位于出错命令之后的所有命令,只保留哪些位于出错命令之前的正确命令。在大多数情况下,被删除的都是AOF文件末尾的不完整的写命令。
遗憾的是,切没有办法修复出错的快照文件。尽快发现了快照文件首个出现错误的地方是有可能的,但因为快照文件本身经过了压缩,而出现在快照文件中的错误有可能会导致快照文件的剩余部分无法被读取。因此,用户最好为重要的快照文件保留多个备份,并在进行数据恢复时,通过计算快照文件的SHA1散列值和SHA256散列值来对内容进行验证。
在了解了如何验证持久化文件是否完好无损,并在有需要时对其进行修复后,我们接下来要考虑如何更好出现故障的redis服务器。
2.4.2、更换故障主服务器
redis服务器总有因为某些故障而停止运行的时候,如硬盘驱动器出错、内存出错或电量耗尽等,这时需要对故障服务器进行更换。
假设A、B两台服务器都运行着Redis,A的Redis为主,B的Redis为从。那么当A由于某种故障断开了网络连接,因此用户决定将同样安装了redis的机器C作为主服务器。
更换服务器的步骤:首先向集群B发送save命令,让他创建一个新的快照文件,接着讲这个快照文件发送给机器C,并在机器C上启动redis。最后,让机器B成为C的从服务器。
3、redis分布式锁
redis使用watch命令来代替对数据进行加锁,然后执行操作,租后释放锁。
redis使用atch命令来代替对数据进行加锁,因为watch只会在数据被其他客户端抢先修改了的情况下通知执行了这个命令的客户端,二不会阻止其他客户端对数据进行修改,所以这个命令被称为“乐观锁”。
分布式锁也有类似“先获取锁,再执行操作,最后是否锁”的动作,但这种锁既不是给同一个进程中的多个线程使用,也不是给同一个进程中的多个线程使用,也不是给同一台机器上的多个进程使用,而是由不同机器上的不同redis客户端进行获取和释放的。
三、Redis优化
3.1、Redis怎样降低内存占用
降低redis内存占用,有助于减少创建快照和快照加载所需的时间;提升载入AOF文件和重写AOF文件的效率;缩短从服务器进行同步所需的时间;并让redis可存储更多的数据。
节省内存的方案:
- 使用redis的短数据结构
- 使用分片技术:将体积大的结构分成多个体积小的结构
- 将固定长度的数据,打包到字符串key里面
3.1.1、短结构
redis为list, set, hash, zset提供了一组配置选项,这些配置选项可以让redis以更节约空间的方式存储长短较短的数据:
- ziplist: 在List,Hash, ZSet等长度较短,或体积较小的时候,可以使用压缩列表(ziplist)的紧凑存储方式来存储。说明:ziplist是list, zset, hash这三种对象的非结构化表示:redis在通常情况下,用双链表表示list, 用hash表表示hash,用hash表+skiplist来表示zset的做法不同,ziplist会以序列化的方式存储数据,但使用时也需要反序列化。
- intset:体积较小的set也有自己的紧凑表示,如果整数的所有元素都可用十进制数表示,且可用有符号整数表示,且元素个数足够少的话,可以用intset表示。intset称为整数集合。
3.1.2、分片
分片(Sharding):把可存储数据的区域划分为多个小的区域,然后基于某些规则,根据key把数据存储到对应的区域。