Redis简介Redis完全开源免费,遵守BSD协议。它是一个高性能的键值数据库。Redis和其他key-value缓存产品有以下三个特点:Redis支持数据持久化,将内存中的数据保存在磁盘中,重启时可以再次加载使用。Redis不仅支持简单的key-value类型数据,还提供list、set、zset、hash等数据结构的存储。Redis支持数据备份,即主从模式的数据备份。Redis优势高性能——Redis读取速度为110000次/s,写入速度为81000次/s。丰富的数据类型——Redis支持二进制情况下的字符串、列表、哈希、集合和有序集合数据类型操作。原子性——Redis中的所有操作都是原子的,这意味着它们要么成功要么完全失败。单个操作是原子的。多操作也支持事务,即原子性,由MULTI和EXEC指令包裹。其他特性——Redis还支持发布/订阅通知、密钥过期等特性。Redis数据类型Redis支持5种数据类型:string(字符串)、hash(散列)、list(列表)、set(集合)、zset(sortedset:有序集合)stringstring是redis最基本的数据类型。一个键对应一个值。字符串是二进制安全的。也就是说redis的字符串可以包含任何数据。例如jpg图像或序列化对象。string类型是redis最基本的数据类型,string类型的值最多可以存储512MB。理解:string就像java中的map,一个key对应一个value127.0.0.1:6379>sethelloworldOK127.0.0.1:6379>gethello"world"hashRedishash是一组键值对(key-value).Redishash是字符串类型的键值映射表,hash特别适合存储对象。理解:hash可以看作是一个key-value的集合。你也可以把它看成是多个字符串对应的哈希。与string的区别:string是一个key-value键值对,而hash是多个key-value键值对。//hash-key可以看作是一组键值对的名称。这里在其中添加了sub-key1:value1、sub-key2:value2、sub-key3:value3三个键值对127.0.0.1:6379>hsethash-keysub-key1value1(integer)1127.0.0.1:6379>hsethash-keysub-key2value2(integer)1127.0.0.1:6379>hsethash-keysub-key3value3(integer)1//获取hash-key中的hash所有键值对127.0.0.1:6379>hgetallhash-key1)"sub-key1"2)"value1"3)"sub-key2"4)"value2"5)"sub-key3"6)"value3"//删除hash-key的hash中的sub-key2键值对127.0.0.1:6379>hdelhash-keysub-key2(整数)1127.0.0.1:6379>hgethash-keysub-key2(无)127.0.0.1:6379>hgethash-keysub-key1"value1"127.0.0.1:6379>hgetallhash-key1)"sub-key1"2)"value1"3)"sub-key3"4)"value3"listRedis列表是一个简单的字符串列表,按插入顺序排序。我们可以在列表的左侧或右侧添加元素。127.0.0.1:6379>rpushlist-keyv1(整数)1127.0.0.1:6379>rpushlist-keyv2(整数)2127.0.0.1:6379>rpushlist-keyv1(整数)3127.0.0.1:6379>lrangelist-key0-11)"v1"2)"v2"3)"v1"127.0.0.1:6379>lindexlist-key1"v2"127.0.0.1:6379>lpoplist(nil)127.0.0.1:6379>lpoplist-key"v1"127.0.0.1:6379>lrangelist-key0-11)"v2"2)"v1"我们可以看出list是一个简单的字符串集合,和Java中的list没有太大区别。不同的是这里的列表存储的是字符串。列表中的元素是可迭代的。setredis的集合是字符串类型的无序集合。集合是通过哈希表实现的,所以增删查查的复杂度为O(1)127.0.0.1:6379>saddk1v1(integer)1127.0.0.1:6379>saddk1v2(integer)1127.0.0.1:6379>saddk1v3(整数)1127.0.0.1:6379>saddk1v1(整数)0127.0.0.1:6379>smembersk11)"v3"2)"v2"3)"v1"127.0.0.1:6379>127.0.0.1:6379>sismemberk1k4(整数set和java中的set还是有点区别的,redis的set是一个key对应多个string类型的value,也是string类型的集合,但是和redis的list不同的是,string集合中的集合元素不能重复,而列表可以。double类型的score,redis使用scores对set的成员从小到大进行排序,zset的元素是唯一的,但是scores可以重复。127.0.0.1:6379>zaddzset-key728member1(整数)1127.0.0.1:6379>zaddzset-key982member0(整数)1127.0.0.1:6379>zaddzset-key982member0(整数)0127.0.0.1:6379"mewithsmber0"zrangezset-1)ith"728"3)"member0"4)"982"127.0.0.1:6379>zrangebyscorezset-key0800withscores1)"member1"2)"728"127.0.0.1:6379>zremzset-keymember1(整数)1127.0.0.1:6379>zremzset-keymember1(integer)0127.0.0.1:6379>zrangezset-key0-1withscores1)"member0"2)"982"zset按照分数大小排序,发布订阅一般不使用Redis进行消息发布订阅。Redis发布-订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。一个Redis客户端可以订阅任意数量的频道。如下图所示频道channel1和订阅这个频道的三个client-client2,client5,client1的关系:学Redis就够了,当有新消息时nt通过PUBLISH命令发送到通道channel1,消息会发送给订阅它的三个客户端:学Redis就够了。以下示例演示了发布和订阅的工作方式。在我们的例子中,我们创建了一个名为redisChat:127.0.0.1:6379>SUBsCRIBEredisChatReadingmessages...(pressCtrl-Ctoquit)1)"subscribe"2)"redisChat"的订阅频道,现在,我们先重启一个redis客户端,然后发布两个同频道redisChat上的消息,订阅者可以收到消息。127.0.0.1:6379>PUBLISHredisChat"sendmessage"(integer)1127.0.0.1:6379>PUBLISHredisChat"helloworld"(integer)1#订阅者客户端显示如下1)"message"2)"redisChat"3)"sendmessage"1)"message"2)"redisChat"3)"helloworld"transactionRedis事务可以同时执行多个命令,服务器在执行命令的过程中不会执行来自其他客户端的命令请求。事务中的多个命令是一次发送到服务器,而不是一条一条发送。这种方法称为流水线,可以减少客户端和服务器之间的网络通信次数,提高性能。Redis中最简单的事务实现是使用MULTI和EXEC命令来包装事务操作。批量操作在发送EXEC命令之前排队。收到EXEC命令后,进入事务执行,事务中任意命令执行失败,其他命令依然执行。也就是说,Redis事务不保证原子性。在交易执行过程中,其他客户端提交的命令请求不会被插入到交易执行命令序列中。一个事务从开始到执行会经历以下三个阶段:启动事务。该命令已排队。执行交易。例子下面是一个事务的例子,用MULTI启动一个事务,然后将多个命令入队到事务中,最后通过EXEC命令触发事务,同时执行事务中的所有命令:redis127.0.0.1:6379>MULTIOKredis127.0.0.1:6379>SETbook-name"MasteringC++in21days"QUEUEDredis127.0.0.1:6379>GETbook-nameQUEUEDredis127.0.0.1:6379>SADDtag"C++""Programming""MasteringSeries"QUEUEDredis127.0.0。1:6379>SMEMBERStagQUEUEDredis127.0.0.1:6379>EXEC1)OK2)"MasteringC++in21days"3)(integer)34)1)"MasteringSeries"2)"C++"3)"Programming"单个Redis命令的执行是Atomic的,但是Redis并没有在事务中加入任何维护原子性的机制,所以Redis事务的执行不是原子的。事务可以理解为封装好的批量执行脚本,但是批量指令不是原子操作,中间某条指令失败不会导致前面指令的回滚,也不会导致后面的指令不执行。这是官方网站上关于交易的redis文档的描述:重要的是要注意,即使一个命令失败,队列中的所有其他命令都会被处理——Redis不会停止命令的处理。例如:redis127.0.0.1:7000>multiOKredis127.0.0.1:7000>setaaaaQUEUEDredis127.0.0.1:7000>setbbbbQUEUEDredis127.0.0.1:7000>setccccQUEUEDredis127.0.0.1:7000>exec1)OK2)OK3)OK如果在setbbbb处失败,seta成功,不会回滚,setc继续执行。Redis事务命令下表列出了redis事务的相关命令:序号命令及说明:1.DISCARD取消事务,放弃事务块中所有命令的执行。2.EXEC执行事务块内的所有命令。3.MULTI标志着一个交易块的开始。4、UNWATCH通过WATCH命令取消对所有按键的监听。5.WATCHkey[key...]监控一个(或多个)键。如果在事务执行之前键(或多个键)被其他命令更改,则事务将被中断。持久性Redis是一个内存数据库。为了保证掉电后数据不丢失,需要将内存中的数据持久化到硬盘中。RDB持久化将某个时间点的所有数据存储在硬盘上。可以将快照复制到其他服务器以创建具有相同数据的服务器副本。如果系统出现故障,最后拍摄的快照中的数据将丢失。如果数据量很大,保存快照的时间会很长。AOF持久化在AOF文件末尾添加写命令(appendonlyfile)。使用AOF持久化需要设置同步选项来保证写命令同步到磁盘文件。这是因为写入文件不会立即将内容同步到磁盘,而是先将其存储在缓冲区中,然后由操作系统决定何时同步到磁盘。OptionSynchronizationfrequencyalwaysSynchronizeeverywritecommandEyerysec每秒同步一次No让操作系统决定何时同步。always选项会严重降低服务器的性能。everysec选项比较合适,可以保证系统崩溃时只有一秒左右的数据丢失,Redis每秒执行一次同步,对服务器几乎没有影响。no选项不会给服务器性能带来太大的提升,而且会增加系统崩溃时丢失的数据量。随着服务器写请求的增加,AOF文件会越来越大。Redis提供了重写AOF的特性,可以去除AOF文件中多余的写命令。复制通过使用slaveofhostport命令使一个服务器成为另一个服务器的从属服务器。一个slave只能有一个master,不支持master-master复制。连接过程主服务器创建一个快照文件,即RDB文件,发送给从服务器,并使用缓冲区记录发送过程中执行的写命令。快照文件发送完毕后,开始向从服务器发送存储在缓冲区中的写命令。从服务器丢弃所有旧数据,加载主服务器发送的快照文件,然后从服务器开始接受主服务器的写命令。主服务器每次执行写命令时,都会向从服务器发送相同的写命令。主从链随着负载不断上升,主服务器无法快速更新所有的从服务器,或者重新连接、重新同步从服务器会导致系统过载。为了解决这个问题,可以创建一个中间层来卸载主服务器的复制工作。中间层的服务器是顶层服务器的从服务器和底层服务器的主服务器。哨兵(Sentinel)可以监控集群中的服务器,并在主服务器下线时自动从从服务器中选举出新的主服务器。Sharding分片是一种将数据分成多个部分的方法,可以将数据存储在多台机器上。该方法在解决某些问题时可以实现线性性能提升。假设有4个Redis实例R0,R1,R2,R3,有很多代表用户user:1,user:2,...的key,有不同的方式来选择一个指定的key存放在哪个实例中,最简单的是rangesharding,比如0到1000的userid存储在实例R0中,1001到2000的用户id存储在实例R1中,以此类推。然而,这需要维护一个映射范围表,维护成本很高。另一个是哈希分片。使用CRC32哈希函数将密钥转换为数字,然后对实例数取模以了解存储了哪个实例。根据分片的位置,可以分为三种分片方式:客户端分片:客户端使用一致性哈希等算法来决定应该分发到哪个节点。代理分片:将客户端的请求发送给代理,代理转发给正确的节点。服务器分片:RedisCluster。
