Redis是一个开源支持网络基于内存、可持久化日志型、key-value键值对数据库。使用ANSI C编写。并提供多种语言的API

它是远程字典server(remote dictionary server)的缩写。[redis网络资源汇总][redis命令參考]

特性

速度快

Redis使用标准C编写实现,并且将全部数据载入到内存中,所以速度很快。

官方提供的数据表明,在一个普通的Linux机器上。Redis读写速度分别达到81000/s110000/s

持久化

因为全部数据保持在内存中,所以对数据的更新将异步地保存到磁盘上,Redis提供了一些策略来保存数据,比方依据时间或更新次数。

数据结构

能够将Redis看做“数据结构server”。眼下,Redis支持5种数据结构。

自己主动操作

Redis对不同数据类型的操作是自己主动的。因此设置或添加key值。从一个集合中添加或删除一个元素都能安全的操作。

支持多种语言

Redis支持多种语言,诸如Ruby, Python, Twisted Python, PHP, Erlang, Tcl, Perl, Lua, Java, Scala, Clojure等。

主-从复制

Redis支持简单而高速的主-从复制。

官方提供了一个数据。Slave21秒即完毕了对Amazon站点10G key set的复制。

Sharding

非常easy将数据分布到多个Redis实例中。但这主要看该语言是否支持。眼下支持Sharding功能的语言仅仅有PHPRubyScala

性能

当数据依赖不再须要,Redis这样的基于内存的性质。与在运行一个事务时将每一个变化都写入硬盘的数据库系统相比就显得运行效率很高。写与读操作速度没有明显区别。

数据类型

Redis的外围由一个键、值映射的字典构成。与其它非关系型数据库主要不同在于:Redis值的类型不仅限于字符串,还支持这些抽象数据类型:string,list,set,zset,hash.值的类型决定了值本身支持的操作。Redis支持不同无序、有序的列表。无序、有序的集合间的交集、并集等高级server端原子操作。

string字符串

stringredis最主要的类型,并且string类型是二进制安全的。意思是redisstring能够包括不论什么数据。比方jpg图片或者序列化的对象。

从内部实现来看事实上string能够看作byte数组,最大上限是1G字节。string类型的值也可视为integer,从而能够让“incr”命令族操作,这样的情况下,该integer的值限制在64位有符号数

listsetzset中包括的独立的元素类型都是string类型

list双向链表

redislist类型事实上就是一个每一个子元素都是string类型的双向链表所以[lr]push[lr]pop命令的算法时间复杂度都是O(1),另外list会记录链表的长度,所以llen操作也是O(1).能够通过push,pop操作从链表的头部或者尾部加入删除元素。

这使得list能够用作栈,也能够用作队列。

list的最大长度是2^32-1个元素,约等于4亿个。

set无序不反复集合

set就是redis string的无序集合,不同意有反复元素set的操作有交集、并集、差集等

set的最大元素数是2^32-1,约等于4亿。

zset有序不反复集合

zsetset的一个升级版本号。set的基础上添加了一个顺序属性,这一属性在加入改动元素时能够指定,每次指定后zset会自己主动安照指定值又一次调整顺序。能够理解为一张表。一列存value。一列存顺序。

操作中的key理解为zset的名字。

zset的最大元素数是2^32-1,约等于4亿。

对于已经有序的zset,仍然能够使用sort命令。通过指定asc|desc參数对其进行排序。

hast表

键、值都为字符串的哈希表(hash)。redis Hash类型对数据域和值提供了映射,这一结构非常方便表示对象。

Hash中能够仅仅保存有限的几个“域”,而不是将全部的“域”作为key。这能够节省内存

同步

Redis支持主从同步。

数据能够从主server向随意数量的从server上同步。从server能够是关联其它从server的主server。

这使得Redis可运行单层树复制。从盘能够有意无意的对数据进行写操作。因为全然实现了公布/订阅机制,使得从数据库在不论什么地方同步树时。可订阅一个频道并接收主server完整的消息公布记录。

同步对读取操作的可扩展性和数据冗余非常有帮助。 

主从复制

Redis的复制功能是全然建立在基于内存快照的持久化策略基础上的。也就是说不管你的持久化策略选择的是什么,仅仅要用到了Redis的复制功能,就一定会有内存快照发生,那么首先要注意你的系统内存容量规划——物理内存使用量不要超过3/5

配置slaveserver非常easy。仅仅须要在配置文件里增加例如以下配置

slaveof 192.168.1.1 6379  #指定masterip和port

 

以下是关于redis主从复制的一些特点

  • lmaster能够有多个slave
  • 除了多个slave连到同样的master外。slave也能够连接其它slave形成图状结构
  • 主从复制不会堵塞master。也就是说当一个或多个slavemaster进行初次同步数据时。master能够继续处理client发来的请求。相反slave在初次同步数据时则会堵塞不能处理client的请求。

  • 主从复制能够用来提高系统的可伸缩性,我们能够用多个slave 专门用于client的读请求。比方sort操作能够使用slave来处理。也能够用来做简单的数据冗余
  • 能够在master禁用数据持久化,仅仅须要凝视掉master 配置文件里的全部save配置,然后仅仅在slave上配置数据持久化。

 

主从复制的过程

  1. 当设置好slaveserver后,slave会建立和master的连接。然后发送sync命令。
  2. 不管是第一次同步建立的连接还是连接断开后的又一次连接,master都会启动一个后台进程。将数据库快照保存到文件里,同一时候master主进程会開始收集新的写命令并缓存起来。
  3. 后台进程完毕写文件后,master就发送文件给slaveslave将文件保存到磁盘上,然后载入到内存恢复数据库快照到slave上。
  4. 接着master就会把缓存的命令转发给slave。并且兴许master收到的写命令都会通过開始建立的连接发送给slave
  5. masterslave的同步数据的命令和从client发送的命令使用同样的协议格式。

    masterslave的连接断开时slave能够自己主动又一次建立连接。假设master同一时候收到多个slave发来的同步连接命令,仅仅会使用启动一个进程来写数据库镜像,然后发送给全部slave 

Redis复制流程在SlaveMaster端分别是一套状态机流转。涉及的状态信息是:

Slave 端:

  • REDIS_REPL_NONE
  • REDIS_REPL_CONNECT
  • REDIS_REPL_CONNECTED 

Master端:

  • REDIS_REPL_WAIT_BGSAVE_START
  • REDIS_REPL_WAIT_BGSAVE_END
  • REDIS_REPL_SEND_BULK
  • REDIS_REPL_ONLINE

整个状态机流程步骤例如以下:

 

  1. Slave端在配置文件里加入了slave of指令,于是Slave启动时读取配置文件,初始状态为REDIS_REPL_CONNECT。 
  2. Slave端在定时任务serverCron(Redis内部的定时器触发事件)中连接Master,发送sync命令。然后等待master发送回其内存快照文件。 
  3. Master端收到sync命令简单推断是否有正在进行的内存快照子进程,没有则马上開始内存快照。有则等待其结束。当快照完毕后会将该文件发送给Slave端。 
  4. Slave端接收Master发来的内存快照文件,保存到本地,待接收完毕后,清空内存表。又一次读取Master发来的内存快照文件,重建整个内存表数据结构,并终于状态置位为 REDIS_REPL_CONNECTED状态,Slave状态机流转完毕。

     

  5. Master端在发送快照文件过程中。接收的不论什么会改变数据集的命令都会临时先保存在Slave网络连接的发送缓存队列里(list数据结构),待快照完毕后,依次发给Slave,之后收到的命令同样处理,并将状态置位为 REDIS_REPL_ONLINE

     

 

Redis复制机制的缺陷:Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave。也就是没有象MySQL那样有复制位置的概念,即无增量复制。这会给整个集群搭建带来许多的问题

持久化

定时快照方式(snapshot)

快照是默认的持久化方式。这样的方式是就是将内存中数据以快照的方式写入到二进制文件里,默认的文件名称为dump.rdb。能够通过配置设置自己主动做快照持久化的方式。能够配置redisn秒内假设超过mkey被改动就自己主动做快照。以下是默认的快照保存配置

  • save 900 1  #900秒内假设超过1key被改动,则发起快照保存
  • save 300 10 #300秒内容如超过10key被改动。则发起快照保存
  • save 60 10000

client 也能够使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,因为redis是用一个主线程来处理全部 client的请求,这样的方式会堵塞全部client请求,所以不推荐使用。每次快照持久化都是将内存数据完整写入到磁盘一次,并非增量的仅仅同步脏数据。假设数据量大的话,并且写操作比較多,必定会引起大量的磁盘io操作,可能会严重影响性能。

另外因为快照方式是在一定间隔时间做一次的,所以假设redis意外down掉的话,就会丢失最后一次快照后的全部改动。

假设应用要求不能丢失不论什么改动的话,能够採用aof持久化方式

保存步骤例如以下

  1. redis调用fork,如今有子进程和父进程。
  2. 父进程继续处理client请求。子进程负责将内存内容写入到暂时文件。因为os的写时复制机制(copy on write)父子进程会共享同样的物理页面。当父进程处理写请求时os会为父进程要改动的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
  3. 当子进程将快照写入暂时文件完成后,用暂时文件替换原来的快照文件,然后子进程退出。

缺点:是定时快照仅仅是代表一段时间内的内存映像。所以系统重新启动会丢失上次快照与重新启动之间全部的数据。

 

基于语句追加方式(aof)

aof 比快照方式有更好的持久化性。是因为在使用aof持久化方式时,redis会将每个收到的写命令都通过write函数追加到文件里(默认是 appendonly.aof)

redis重新启动时会通过又一次运行文件里保存的写命令来在内存中重建整个数据库的内容。当然因为os会在内核中缓存 write做的改动,所以可能不是马上写到磁盘上。

这样aof方式的持久化也还是有可能会丢失部分改动。只是我们能够通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。

有三种方式例如以下(默认是:每秒fsync一次)

  • appendonly yes              //启用aof持久化方式
  • # appendfsync always    //每次收到写命令就马上写盘,最慢。但保证全然的持久化,不推荐使用
  • appendfsync everysec    //每秒钟强制写入磁盘一次。推荐
  • # appendfsync no    //全然依赖os,性能最好,持久化没保证

持久化文件会变的越来越大。比如我们调用incr test命令100次,文件里必须保存所有的100条命令,事实上有99条都是多余的。由于要恢复数据库的状态事实上文件里保存一条set test 100就够了。

为了压缩aof的持久化文件。

redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到暂时文件里,最后替换原来的文件。 

保存步骤例如以下

  1. redis调用fork ,如今有父子两个进程
  2. 子进程依据内存中的数据库快照,往暂时文件里写入重建数据库状态的命令
  3. 父进程继续处理client请求。除了把写命令写入到原来的aof文件里。同一时候把收到的写命令缓存起来。这样就能保证假设子进程重写失败的话并不会出问题。
  4. 当子进程把快照内容以命令方式写入暂时文件里后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到暂时文件。

  5. 如今父进程能够使用暂时文件替换老的aof文件,并重命名。后面收到的写命令也開始往新的aof文件里追加。

缺点:log文件体积过大时系统重新启动恢复数据很慢,几十G的数据可能要几小时才干载入完。每条命令都要写log。读写性能会有所下降

posted on 2017-04-24 15:58  yutingliuyl  阅读(244)  评论(0编辑  收藏  举报