Redis提供了5种数据结构,但除此以外,Redis还提供了注入慢查询分析,Redis Shell、Pipeline、事务、与Lua脚本、Bitmaps、HyperLogLog、PubSub、GEO等附加功能,这些功能能够在某些场景发挥很重要的做用.数据库
Redis
客户端执行一条命令分为如下四个步骤:网络
1.发送命令
2.命令排队
3.命令执行
4.返回结果数据结构
其中,第一步+第四步称为Round Trip Time
(RTT
,往返时间).并发
Redis
提供了批量操做命令(例如mget
,mset
等),有效的节约RTT
.但大部分命令是不支持批量操做的,例如要执行n
次hgetall
命令,并无mhgetall
存在,须要消耗n
次RTT
.Redis
的客户端和服务端可能不是在不一样的机器上.例如客户端在北京,Redis
服务端在上海,两地直线距离为1300千米,那么1次RTT
时间=1300×2/(300000×2/3)=13毫秒
(光在真空中传输速度为每秒30万千米,这里假设光纤的速度为光速的2/3),那么客户端在1秒内大约只能执行80次左右的命令,这个和Redis
的高并发高吞吐背道而驰.分布式
Pipeline
(流水线)机制能改善上面这类问题,它能将一组Redis
命令进行组装,经过一次RTT
传输给Redis
,再将这组Redis
命令按照顺序执行并装填结果返回给客户端.图1.1中未使用Pipeline
执行了n次命令,整个过程须要n个RTT
.高并发
Pipeline
并非什么新的技术和机制,不少技术上都使用过.并且RTT
在不一样网络环境下会有不一样,例如同机房和同机器会比较快,跨机房跨地区会比较慢.Redis
命令真正执行的时间一般在微秒级别,因此才会有Redis
性能瓶颈是网络这样的说法.性能
可使用Pipeline
模拟出批量操做的效果,可是在使用时须要质疑它与原生批量命令的区别,具体包含几点:优化
Pipeline
是非原子性的.key
,Pipeline
支持多个命令.Redis
服务端支持实现的,而Pipeline
须要服务端与客户端的共同实现.Pipeline
虽然好用,可是每次Pipeline
组装的命令个数不能没有节制,不然一次组装Pipeline
数据量过大,一方面会增长客户端的等待时机,另外一方面会形成必定的网络阻塞,能够将一次包含大量命令的Pipeline
拆分红屡次较小的Pipeline
来完成.网站
Pipeline
只能操做一个Redis
实例,但即便在分布式Redis
场景中,也能够做为批量操做的重要优化方法.spa
为了保证多条命令组合的原子性,Redis
提供了简单的事务以及集成Lua
脚原本解决这个问题.
熟悉关系型数据库的开发者应该对事务比较了解,简单地说,事务表示一组动做,要么所有成功,要不所有不成功.例如在在电商网站中用户购买商品A那么须要将商品A的库存-1,并建立一个订单.这两个操做要么远不执行成功,要么所有执行不成功,不然会出现数据不一致的状况.
Redis
提供了简单的功能,将一组须要一块儿执行的命令放到multi
和exec
两个命令之间.multi
命令表明事务的开始,exec
命令表明事务结束,他们之间的命令是原子顺序执行的.
例如上述的用户购买商品问题:
127.0.0.1:6379> multi OK 127.0.0.1:6379> hincrby commodity:a:detail stock -1 QUEUE 127.0.0.1:6379> rpush user:1:orders {"commodity":'a',..} QUEUE
能够看到数据操做命令返回的结果是QUEUE
,表明命令并无真正执行,而是暂时保存在Redis
中.若是此时另外一个客户端执行llen user:1:orders
返回结果为0.
127.0.0.1:6379> llen user:1:orders (integer) 0
只有当exec
执行后,用户购买商品的行为才算完成,以下两个结果对应hincrby
和rpush
命令.
127.0.0.1:6379> exec 1) (integer) 4 # 商品原库存为5 2) (integer) 1 127.0.0.1:6379> llen user:1:orders (integer) 1
若是要中止事务的执行,可使用discard
命令替代exec
命令便可.
127.0.0.1:6379> discard OK 127.0.0.1:6379> llen user:1:orders (integer) 0
若是事务中的命令出现错误,Redis
的处理机制也不尽相同.
1.命令错误
例以下面操做错将set
写成了sett
,属于语法错误,会形成整个事务没法执行,key
和counter
的值未发生变化:
127.0.0.1:6379> mget key counter 1) "hello" 2) "100" 127.0.0.1:6379> multi OK 127.0.0.1:6379> sett key world (error) ERR unknown command 'sett' 127.0.0.1:6379> incr counter QUEUE 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> mget key counter 1) "hello" 2) "100"
2.运行时错误
例如用户购买商品,误把rpush
写成了zadd
127.0.0.1:6379> multi OK 127.0.0.1:6379> hincrby commodity:a:detail stock -1 QUEUED 127.0.0.1:6379> zadd user:1:orders {"commodity":'a',..} QUEUED 127.0.0.1:6379> exec 1) (integer) 1 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value. 127.0.0.1:6379> hget commodity:a:detail stack (integer) 3
能够看到Redis
并不支持回滚功能,hincrby commodity:a:detail stock -1
命令已经执行成功,开发者须要本身修改这类问题.