如何正确的使用Redis.docx
《如何正确的使用Redis.docx》由会员分享,可在线阅读,更多相关《如何正确的使用Redis.docx(27页珍藏版)》请在冰豆网上搜索。
如何正确的使用Redis
如何正确地使用Redis
1.Redis特性介绍
1.1Redis概述
Redis是一款依据BSD开源协议发行的高性能,使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value存储系统(cacheandstore),并提供多种语言的API。
它起步较晚,发展迅速,目前已被许多大型机构采用。
它通常被称为数据结构服务器,因为值(value)可以是字符串(String),哈希(hashes),列表(list),集合(sets)和有序集合(sortedsets)等类型。
1.性能极高–Redis能支持超过100K每秒的读写频率。
2.丰富的数据类型–Redis支持二进制案例的Strings,Lists,Hashes,Sets及SortedSets数据类型操作。
3.原子–Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行,我们能非常方便得实现事务。
4.Redis支持publish/subscribe,通知,key过期等重要特性。
5.Redis内存中的数据支持实时持久化,非常安全。
6.新版本Redis支持集群。
Redis在最近发布了3.0.6稳定版,我把最新的3.0.6稳定版的配置翻译了一遍,获益匪浅,基本上对这个版本的Redis的很多细节清晰了很多,下面针对一些重要配置介绍下Redis的相关特性。
1.2Redis配置
花了三天时间,把REDIS3.0.6英文版大部分都翻译过来了,地址:
http:
//q.fireflyclub.org/?
/article/19
1.3内存快照
某个时间点Redis服务器内存中的内容我们称之为快照,Redis会自动快照保存到磁盘,调用BGSAVE能手动触发快照保存,保存快照的动作是后台进程完成的,保存快照期间其他客户端仍然和可以读写REDIS服务器。
后台保存快照到磁盘会占用大量内存。
如果调用SAVE命令保存内存中的数据到磁盘,将阻塞客户端请求,直到保存完毕。
调用SHUTDOWN命令,Redis服务器会先调用SAVE,所有数据持久化到磁盘之后才会真正退出。
在Redis的配置文件中可以配置内存数据持久化的触发条件
#save<时间><变更次数>
#两个条件同时满足,发生一次落地本地磁盘动作,下面三个配置是或的关系
#如果不想落地内存中的数据,直接注释掉下面三个配置即可
#如果配置成save"",之前落地的数据都可能被删除
save9001
save30010
save6010000
1.4主从同步
用slaveof的配置来设置本机的Redis作为从实例,实时从主实例读取数据,成为其镜像。
主要是高可用场景需要本功能。
以下是Redis主从同步的一些特性:
1.同一个Master可以同步多个Slaves;
2.Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
因此我们可以将Redis的Replication架构视为图结构;
3.MasterServer是以非阻塞的方式为Slaves提供服务。
所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求;
4.SlaveServer同样是以非阻塞的方式完成数据同步。
在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据;
5.为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。
即便如此,系统的伸缩性还是得到了很大的提高;
6.Master可以将数据快照保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作,减轻Master的压力;
7.Redis的数据同步是异步进行的,你可以配置主实例在和从实例断掉连接的时候停止接受客户端发过来的写请求,从而保证数据的一致性。
8.同步是自动完成的,不需要人工干预。
主从之间的网络短暂的断开后,从再次连上主之后,会自动从上次断开的时候同步数据。
9.当新的从实例连入主实例,或者从实例断开连接时间比较长,再连入主实例的时候,为了保证数据一致,主实例会将全量数据同步给从实例。
这种主要有两种模式:
文件模式:
主实例创建一个任务去写DB文件到磁盘,文件创建完毕后,主实例增量方式读取文件中的数据传输给从实例;使用Disk-backed模式同步数据有个好处就是,这个文件一旦生成,多个slave实例过来全量同步,都可以重用这一个文件;
网络模式:
主实例创建一个任务把全量数据直接写入从实例的socket连接上,数据不落地。
;使用Diskless模式同步全量数据,一旦一个全量同步行为开始了,其他slave实例的同步请求过来时,只能先排到队列里面去等下一次全量同步开始。
使用diskless的数据同步时,master实例会等一会(时间可配),看看这段时间内是否有多个slave实例同时请求全量同步,好凑齐了一块给所有实例传输数据。
在网络带宽充裕的情况下,diskless的同步避免了磁盘io,性能会好很多。
10.Redis提供一个叫RedisSentinel的监控程序做主从监控和主从切换工作。
他主要有以下功能:
监控(Monitoring):
RedisSentinel实时监控主服务器和从服务器运行状态。
提醒(Notification):
当被监控的某个Redis服务器出现问题时,RedisSentinel可以向系统管理员发送通知,也可以通过API向其他程序发送通知。
自动故障转移(Automaticfailover):
当一个主服务器不能正常工作时,RedisSentinel可以将一个从服务器升级为主服务器,并对其他从服务器进行配置,让它们使用新的主服务器。
当应用程序连接到Redis服务器时,RedisSentinel会告之新的主服务器地址和端口。
RedisSentinel是一个分布式系统,你可以在架构中运行多个Sentinel进程,这些进程通过相互通讯来判断一个主服务器是否断线,以及是否应该执行故障转移。
在配置RedisSentinel时,至少需要有1个Master和1个Slave。
当Master失效后,RedisSentinel会报出失效警告,并通过自动故障转移将Slave提升为Master,并提供读写服务;当失效的Master恢复后,Redis
Sentinel会自动识别,将Master自动转换为Slave并完成数据同步。
1.5安全
Redis可以在配置内设置访问密码,如果设置,那么在运行任何命令前,必须先输入密码。
如果你的内网环境有不信任的主机在运行,那么你需要设置这个密码。
如果你的内网是安全的,那么不建议设置本密码。
Redis允许客户端每秒尝试15万次密码匹配,如果你密码不够强,很容易被破解。
Redis还有一招挺绝的,就是修改高级别的命令的指令名。
比如把CONFIG修改成b840fc02d524045429941cc15f59e41cb7be6c52,基本上就可以避免外部人员调用本命令了。
Redis也可以屏蔽一些命令名,使用rename-command命令将命令的指令名设置为空字符串即可。
需要注意的是,这些对命令名的修改也会同步到AOF文件中,或者传输给从实例中,引起其他问题。
1.6内存限制及LRU自动清理Key
Redis可以使用maxmemory来配置内存的使用上限,一旦Redis使用的内存达到设置的上限,那么会出现两种情况:
1.按照LRU算法自动清理过期Key来释放内存;
2.拒绝所有客户端发上来的写请求;
Redis有个配置叫maxmemory-policy,这个配置决定了Redis内存触限后的处理策略:
1.volatile-lru:
根据LRU算法删除过期Key
2.allkeys-lru:
根据LRU算法删除所有Key
3.volatile-random:
随机删除过期数据
4.allkeys-random:
随机删除任意数据
5.volatile-ttl:
根据最近过期时间来删除(辅以TTL)
6.noeviction:
不删除任何数据,拒绝客户端写请求
如果Reids在当前策略下找不到可以删除的key,那么Redis会拒绝所有客户端的写请求。
写请求的命令包括:
setsetnxsetexappendincrdecrrpushlpushrpushxlpushxlinsertlsetrpoplpushsaddsintersinterstoresunionsunionstoresdiffsdiffstorezaddzincrbyzunionstorezinterstorehsethsetnxhmsethincrbyincrbydecrbygetsetmsetmsetnxexecsort
本配置项默认值为:
maxmemory-policynoeviction
1.7AOF模式持久化
1.7.1AOF持久化细节
默认情况下Redis会异步落地内存快照数据到磁盘,这种模式对于很多场景是够用的。
但这种模式有个缺点就是对于突发情况,比如突然停电,落地的文件数据会丢失几分钟数据,极端情况丢数据这事对于普通应用程序可能可以接收,但对于类似银行这种机构是苟能容忍的。
因此Redis提供一种更可靠的模式来保证数据的安全,AOF是一种可选的更安全的持久化模式,能很好地解决上面说的数据丢失的问题。
默认配置下,AOF模式在意外故障发生时最多丢失一秒钟的数据。
AOF和内存快照两种持久化模式能同时启动,不会互相影响。
如果AOF模式生效了,那么Redis启动的时候会首先载入AOF文件来保证数据的可靠性。
AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令,有比较好的可读性。
AOF日志也不是完全按客户端的请求来生成日志的,比如命令INCRBYFLOAT在记AOF日志时就被记成一条SET记录,因为浮点数操作可能在不同的系统上会不同,所以为了避免同一份日志在不同的系统上生成不同的数据集,所以这里只将操作后的结果通过SET来记录。
每一条写命令都生成一条日志,所以AOF文件会很大。
Redis在落地AOF文件的时候,有三种模式
1.appendfsyncalways:
每次有客户端发送写操作,都需要落地到磁盘,性能最差,但最安全。
2.appendfsynceverysec:
顾名思义,每秒写一次,均衡模式。
3.appendfsyncno:
操作系统在需要的时候才落地数据到磁盘,性能最好,但可能有数据丢失风险。
对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
Redis实用的默认模式是everysec,这是一种均衡的模式。
在AOF同步文件同步模式设置为always或者everysec的时候,会有一个后台线程去做这个事,同时产生大量磁盘IO。
这些IO操作经常会阻塞后台内存快照落地线程和AOF日志重写线程,甚至导致整个Redis被阻塞,目前没有很好的解决方案。
为了缓解这个问题,Redis增加了AOF阻塞机制,生成AOF文件之前会先检查BGSAVE或者BGREWRITEAOF是否在运行,如果是,那么就先阻止AOF操作。
这就意味这在BGSAVE或者BGREWRITEAOF时,Redis不会去写AOF,可能会因此丢掉30秒以内的数据。
如果你因为AOF写入产生延迟问题,可以将AOF阻塞机制的相关配置no-appendfsync-on-rewrite设置为yes。
该配置设置为no为最安全,最不可能丢失数据的方式.
1.7.2AOF重写
在AOF文件增长到足够大超过配置的百分比的时候,Redis提供AOF重写功能,AOF重写会聚合Key的所有操作,目的是让一个KEY只有一条记录留在AOF文件中,从而大大缩小AOF文件的尺寸。
AOF重写是重新生成一份AOF文件,新的AOF文件中一条记录的操作只会有一次,而不像一份老文件那样,可能记录了对同一个值的多次操作。
其生成过程和RDB类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。
在写入新文件的过程中,所有的写操作日志还是会写到原来老的AOF文件中,同时还会记录在内存缓冲区中。
当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。
然后调用原子性的rename命令用新的AOF文件取代老的AOF文件。
重写后,AOF文件变成一个非常小的全量文件
命令:
BGREWRITEAOF,我们应该经常调用这个命令来来重写
Redis会自动重写AOF,当然你也可以配置它不自动AOF重写。
Redis数据结构详解
Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值。
也就是说,你不必仅仅把字符串当作键所指向的值。
下列这些数据类型都可作为值类型。
二进制安全的字符串string
二进制安全的字符串列表listofstring
二进制安全的字符串集合setofstring,换言之:
它是一组无重复未排序的element。
可以把它看成JAVA中的HashSet。
有序集合sortedsetofstring,类似于集合set,但其中每个元素都和一个浮点数score(评分)关联。
element根据score排序。
可以把它看成JAVA的HashMap–其key等于element,value等于score,但元素总是按score的顺序排列,无需额外的排序操作。
1.8Key
Rediskey值是二进制安全的,这意味着可以用任何二进制序列作为key值,比如”foo”的简单字符串到一个JPEG文件的内容都可以。
空字符串也是有效key值。
关于key的几条规则:
太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。
太短的键值通常也不是好主意,如果你要用”u:
1000:
pwd”来代替”user:
1000:
password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于keyobject和valueobject本身来说很小。
当然,没人阻止您一定要用更短的键值节省一丁点儿空间。
最好坚持一种模式。
例如:
”object-type:
id:
field”就是个不错的注意,像这样”user:
1000:
password”。
1.9Strings
这是最简单Redis类型。
如果你只用这种类型,Redis就像一个可以持久化的memcached服务器(注:
memcache的数据仅保存在内存中,服务器重启后,数据将丢失)。
我们来玩一下字符串类型。
1.9.1操作
$redis-clisetmykey"mybinarysafevalue"
OK
$redis-cligetmykey
mybinarysafevalue
正如你所见到的,通常用SETcommand和GETcommand来设置和获取字符串值。
值可以是任何种类的字符串(包括二进制数据),例如你可以在一个键下保存一副jpeg图片。
值的长度不能超过1GB。
虽然字符串是Redis的基本值类型,但你仍然能通过它完成一些有趣的操作。
例如:
原子递增:
$redis-clisetcounter100
OK$redis-cliincrcounter
(integer)101
$redis-cliincrcounter
(integer)102
$redis-cliincrbycounter10
(integer)112
INCR命令将字符串值解析成整型,将其加一,最后将结果保存为新的字符串值,类似的命令有INCRBY,DECRandDECRBY。
实际上他们在内部就是同一个命令,只是看上去有点不同。
INCR是原子操作意味着什么呢?
就是说即使多个客户端对同一个key发出INCR命令,也决不会导致竞争的情况。
例如如下情况永远不可能发生:
客户端1和客户端2同时读出“10”,他们俩都对其加到11,然后将新值设置为11。
最终的值一定是12,read-increment-set操作完成时,其他客户端不会在同一时间执行任何命令。
对字符串,另一个的令人感兴趣的操作是GETSET命令,顾名思义:
他为key设置新值并且返回原值。
这有什么用处呢?
例如:
你的系统每当有新用户访问时就用INCR命令操作一个Rediskey。
你希望每小时对这个信息重置一次。
你就可以GETSET这个key并给其赋值0并读取原值。
1.9.2使用场景
UserId的生成,我们直接使用Redis的Strings数据结构,主要用到了INCR的原子性和Redis的全局性两个特点。
内容变更不频繁的对象,直接用protobuff序列化之后,以字符串的形式写入Redis,需要的时候把字符串从Redis中取出,然后反序列化成对象后使用。
比如用户基本信息的缓存我们就用这种形式存储。
这种做法的好处是:
存储、获取的时候代码很简单,不容易出错;
对于这种对象,如果对象内容发生变更,一般的处理都是在变更时直接删除Redis中的对象对应的字符串,下次需要使用该对象的时候生成字符串写入Redis。
1.10Lists
1.10.1操作
一般意义上讲,列表就是有序元素的序列:
10,20,1,2,3就是一个列表。
但用数组实现的List和用LinkedList实现的List,在属性方面大不相同。
Redislists基于LinkedLists实现。
这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是非常小。
用LPUSH命令在十个元素的list头部添加新元素,和在千万元素list头部添加新元素的速度相同。
那么,坏消息是什么?
在数组实现的list中利用索引访问元素的速度极快,而同样的操作在linkedlist实现的list上没有那么快。
RedisLists用linkedlist实现的原因是:
对于数据库系统来说,至关重要的特性是:
能非常快的在很大的列表上添加元素。
另一个重要因素是,正如你将要看到的:
Redislists能在非常短时间取得常数长度。
LPUSH命令可向list的左边(头部)添加一个新元素,而RPUSH命令可向list的右边(尾部)添加一个新元素。
最后LRANGE命令可从list中取出一定范围的元素
$redis-clirpushmessages"Hellohowareyou?
"
OK
$redis-clirpushmessages"Finethanks.I‘mhavingfunwithRedis"
OK
$redis-clirpushmessages"IshouldlookintothisNOSQLthingASAP"
OK
$redis-clilrangemessages02
1.Hellohowareyou?
2.Finethanks.I‘mhavingfunwithRedis
3.IshouldlookintothisNOSQLthingASAP
注意LRANGE带有两个索引,一定范围的第一个和最后一个元素。
这两个索引都可以为负来告知Redis从尾部开始计数,因此-1表示最后一个元素,-2表示list中的倒数第二个元素,以此类推。
1.10.2使用场景
List最大的优点就是你可以每次都以原先添加的顺序访问数据。
对于只需要顺序批量读取,不需要按照特定值检索的数据,我们使用Lists数据结构。
比如IM系统中的未读消息,就可以存在Lists中,每次读出来后就可以删除。
Blog系统里面的Feed列表,也可以存储在List中,使用LRANGE可以实现分页。
每个Feed的评论也可以单独存储一个List,写入读取都很方便。
一些网站的一些访问量比较大的内容,比如推荐文章,热门用户等内容都比较适合使用Lists结构来存储。
1.11Hashes
1.11.1操作
Redis拥有一个键值对的数据结果,类似Java中的HashMap
>hmsetuser:
1000usernameantirezbirthyear1977verified1
OK
>hgetuser:
1000username
"antirez"
>hgetuser:
1000birthyear
"1977"
>hgetalluser:
1000
1)"username"
2)"antirez"
3)"birthyear"
4)"1977"
5)"verified"
6)"1"
Hahes数据结构用来存储对象非常方便,基本上你想存储多少个字段到对象中都可以(除非超过内存限制)。
HMSET可以设置多个键值对到Hashes对象中去,HGET只能获取一个键值对出来。
HMGET可以获取多个键值对出来。
>hmgetuser:
1000usernamebirthyearno-such-field
1)"antirez"
2)"1977"
3)(nil)
HINCRBY之类的命令可以针对Hashes对象中的某一个键值对进行计算操作:
>hincrbyuser:
1000birthyear10
(integer)1987
>hincrbyuser:
1000birthyear10
(integer)1997
需要提醒大家的是,比较小的Hashes对象(拥有的元素少)被专门优化过,会得到一个非常好的性能。
1.11.2使用场景
对于需要经常变更的对象,我们使用Hashes结构来存储。
好处显而易见,第一你可以给一个对象存储任意多的字段,第二访问很方便,不用频繁序列化和反序列化。
在IM系统中,用户在线状态就推荐用Hashes结果来存储。
keepalive的时候会非常高效,IM的keepalive的请求量是非常大的。
对于一些列配置类的数据,也比较适合用Hashes来缓存。
比如会员的相关配置就可以都存在一个Hashes结构中,取起来很方便。
1.12Sets
1.12.1操作
Redis集合是未排序的集合,其元素是二进制安全的字符串。
SADD命令可以向集合添加一个新元素。
和sets相关的操作也有许多,比如检测某个元素是否存在,以及实现交集,并集,差集等等。
一例胜千言:
$redis-clisaddmyset1
(integer)1
$redis-clisaddmyset2
(integer)1
$redis-clisaddmyset3
(integer)1
$redis-clismembersmyset
1.3
2.1
3.2
我向集合中添加了三个元素,并让Redis返回所有元素。
如你所见它们是无序的。
现在让我们检查某个元素是否存在:
$redis-clisismembermyset3
(integer)1
$redis-clisismembermyset30
(integer)0
“3″是这个集合的成员,而“30”不是。
集合特别适合表现对象之间的关系。
例