Redis简介Redis完全开源免费,遵守BSD协议,是一个高性能的key-value数据库。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-值)聚集。Redishash是字符串类型的键值映射表,hash特别适合存储对象。理解:hash可以看作是一个key-value的集合。你也可以把它看成是多个字符串对应的哈希。与string的区别:string是一个key-value键值对,而hash是多个key-value键值对。//hash-key可以看作是一组键值对的名称。这里添加了三个键值对127.0.0.1sub-key1:value1,sub-key2:value2,sub-key3:value3: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"//删除sub-key2中的键值对hash-key127.0.0.1:6379>hdelhash-keysub-key2(integer)1127.0.0.1:6379>hgethash-keysub-key2(nil)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>rpush列表键v1(整数)1127.0.0.1:6379>rpush列表键v2(整数)2127.0.0.1:6379>rpush列表键v1(整数)3127.0.0.1:6379>lrange列表-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是一个简单的字符串集合,和list区别不大在Java中,不同的是这里的列表存储的是字符串。列表中的元素是可迭代的。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(整数)0127.0.0.1:6379>sismemberk1v1(整数)1127.0.0.1:6379>sremk1v2(整数)1127.0.0.1:6379>sremk1v2(整数)0127。0.1:6379>smembersk11)"v3"2)"v1"redis中的set和java中的set有些不同。redis的集合是一个key对应多个string类型的value,也是string类型的集合。但是和redis的list不同的是,set中的string集合元素不能重复,而list可以。zsetrediszset和set一样,是一个字符串类型元素的集合,集合中的元素不能重复。不同之处在于zset的每个元素都将与一个双精度类型的分数相关联。Redis使用分数将集合的成员从小到大排序。zset的元素是唯一的,但是分数是可以重复的。127.0.0.1:6379>zaddzset-key728member1(整数)1127.0.0.1:6379>zaddzset-key982member0(整数)1127.0.0.1:6379>zaddzset-key982member0(整数)0127.0.09.1:63>zrangezset-key0-1withscores1)"member1"2)"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(整数)0127.0.0.1:6379>zrangezset-key0-1withscores1)"member0"2)"982"zset根据分数的大小排序。发布订阅一般不使用Redis进行消息发布订阅。Redis发布-订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis客户端可以订阅任意数量的频道。下图展示了频道channel1和订阅该频道的三个客户端——client2、client5和client1的关系:当有新消息通过PUBLISH命令发送到频道channel1时,这条消息会被发送到订阅的频道中三个客户端:示例下面的示例演示了发布-订阅的工作原理。在我们的示例中,我们创建了一个名为redisChat的订阅频道:127.0.0.1:6379>SUBsCRIBEredisChatReadingmessages...(按Ctrl-C退出)1)“订阅”2)“redisChat”现在,让我们重新打开频道Redis客户端,然后在同一个频道redisChat上发布两条消息,订阅者就可以收到消息了。127.0.0.1:6379>PUBLISHredisChat"sendmessage"(整数)1127.0.0.1:6379>PUBLISHredisChat"helloworld"(整数)1订阅者客户端显示如下1)"message"2)"redisChat"3)"sendmessage"1)"message"2)"redisChat"3)"helloworld"发布订阅常用命令,自己查看事务Redis事务可以一次执行多条命令,服务端不会执行其他客户端的命令在命令执行期间询问。事务中的多个命令是一次发送到服务器,而不是一条一条发送。这种方法称为流水线,可以减少客户端和服务器之间的网络通信次数,提高性能。Redis中最简单的事务实现是使用MULTI和EXEC命令来包装事务操作。批量操作在发送EXEC命令之前排队。收到EXEC命令后,进入事务执行,事务中任意命令执行失败,其他命令依然执行。也就是说,Redis事务不保证原子性。在交易执行过程中,其他客户端提交的命令请求不会被插入到交易执行命令序列中。一个事务从开始到执行会经历以下三个阶段:开始事务。该命令已排队。执行交易。例子下面是一个事务的例子,用MULTI启动一个事务,然后将多个命令入队到事务中,最后通过EXEC命令触发事务,同时执行事务中的所有命令:redis127.0.0.1:6379>MULTIOKredis127.0.0.1:6379>SETbook-name"21天精通C++"QUEUEDredis127.0.0.1:6379>GETbook-nameQUEUEDredis127.0.0.1:6379>SADDtag"C++""Programming""MasteringSeries"QUEUEDredis0.127。0.1:6379>SMEMBERStagQUEUEDredis127.0.0.1:6379>EXEC1)OK2)《21天精通C++》3)(integer)34)1)《精通系列》2)《C++》3)单个Redis的《编程》command的执行是原子的,但是Redis并没有在事务中加入任何维护原子性的机制,所以Redis事务的执行不是原子的。事务可以理解为封装好的批量执行脚本,但是批量指令不是原子操作,中间某条指令失败不会导致前面指令的回滚,也不会导致后面的指令不执行。这是官方网站上关于交易的redis文档的描述:重要的是要注意,即使一个命令失败,队列中的所有其他命令都会被处理——Redis不会停止命令的处理。例如:redis127.0.0.1:7000>multiOKredis127.0.0.1:7000>setaaaQUEUEDredis127.0.0.1:7000>setbbbbQUEUEDredis127.0.0.1:7000>setccccQUEUEDredis127.0.0.1:7000>OK3)OK2)OK2)iffailedatsetbbbbseta成功,不会回滚,setc继续执行。Redis事务命令下表列出了redis事务的相关命令:序号命令及说明:1DISCARD取消事务,放弃执行事务块中的所有命令。2EXEC执行事务块内的所有命令。3MULTI标记一个事务块的开始。4UNWATCH取消WATCH命令对所有按键的监听。5WATCHkey[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。
