## 为什么使用Redis?
用户使用memcached,只能用APPEND命令将数据添加到已有字符串的末尾,但随后如何删除这些元素?memcached采用的办法是通过黑名单(blacklist)来隐藏列表里面的元素,从而避免对元素执行读取、更新、写入等操作。相反地,Redis的LIST和SET允许用户直接添加或者删除元素。
数据库一个常见用法是存储长期的报告数据,并将这些报告数据用作固定时间范围内的聚合数据。收集聚合数据常见做法是:先将各个行插入一个报告表里面,之后再通过扫描这些行来收集聚合数据,并根据收集到的聚合数据来更新聚合表中已有的那些行。(使用插入行的方式存储,原因在于对大部分数据库来说,插入行操作执行速度非常快,但对表里面行更新却是速度非常慢的操作,除了会引起依次随机读,还可能会引起一次随机写)而在Redis里面,用户可以直接使用原子的INCR命令及其变种来计算聚合数据,并且因为Redis将数据存储在内存里面,而且发送给Redis的命令请求并不需要经过典型的查询分析器(parser)或者查询优化器(optimizer)进行处理,所以对Redis存储的数据执行随机写的速度总是非常迅速的。
Redis相对于memcached来说,不仅可以使代码变得更简短、更易懂、更易维护,而且还可以使代码的运行速度更快(因为用户不需要通过读取数据库来更新数据)。
memcached也可以将数据存储在内存里面,但使用Redis存储聚合数据有以下3个好处:首先,使用Redis可以将彼此相关的聚合数据放在同一个结构里面,这样访问聚合数据就会变得更为容易;其次,使用Redis可以将聚合数据放到有序集合里面,构建出一个实时的排行榜;最后,Redis的聚合数据可以是整数或者浮点数,而memcached的聚合数据只能是整数。
Redis数据结构
STRING
set hello world //将hello的值设置为world
OK
get hello
"world" //键的值仍然是world
del hello //删除这个键值对
(integer) 1
get hello //因为这个键的值已经不存在了,所以尝试获取键的值将得到一个nil,Python客户端将这个nil转换成None。
conn = redis.Redis()
conn.get('key')
conn.incr('key')
1
conn.incr('key',15)
16
conn.decr('key',5)
11
conn.get('key')
'11'
conn.set('key','13')
True
conn.incr('key')
14
LIST
Redis列表可执行的操作和很多编程语言里面的列表操作非常相似:LPUSH命令和RPUSH命令分别用于将元素推入列表的左端(left end)和右端(right end);LPOP命令和RPOP命令分别用于从列表的左端和右端弹出元素;LINDEX命令用于获取列表在给定位置上的一个元素;LRANGE命令用于获取列表在给定范围上的所有元素。
rpush list-key item
(integer) 1
rpush list-key item2
(integer) 2
rpush list-key item
(integer) 3
lrange list-key 0 -1
1) "item"
2) "item2"
3) "item"
lindex list-key 1
"item2"
lpop list-key
"item"
lrange list-key 0 -1
1) "item2"
2) "item"
conn.rpush('list-key','last')
1L
conn.lpush('list-key','first')
2L
conn.rpush('list-key','new last')
3L
conn.lrange('list-key',0,-1)
['first','last','new last']
conn.lpop('list-key')
'first'
conn.lpop('list-key')
'last'
conn.lrange('list-key',0,-1)
['new last']
conn.rpush('list-key','a','b','c')
4L
conn.lrange('list-key',0,-1)
['new last','a','b','c']
conn.ltrim('list-key',2,-1) //可以从列表的左端、右端或者左右两端删减任意数量的元素
True
conn.lrange('list-key',0,-1)
['b','c']
上例中使用到了LTRIM命令,组合使用LTRIM和LRANGE可以构建出一个在功能上类似于LPOP或RPOP,但是却能够一次返回并弹出多个元素的操作。
conn.rpush('list','item1')
1
conn.rpush('list','item2')
2
conn.rpush('list2','item3')
1
conn.brpoplpush('list2','list',1) //将一个元素从一个列表移动到另一个列表,并返回被移动的元素
'item3'
conn.brpoplpush('list2','list',1) //当列表不包含任何元素时,阻塞弹出操作会在给定的时限内等待可弹出的元素出现,并在时限到达后返回None
conn.lrange('list',0,-1)
['item3','item1','item2']
conn.brpoplpush('list','list2',1) //弹出“list2”最右端的元素,并将被弹出的元素推入“list”的左端
'item2'
//BLPOP命令会从左到右地检查传入的列表,并对最先遇到的非空列表执行弹出操作。
conn.blpop(['list','list2'],1)
('list','item3')
conn.blpop(['list','list2'],1)
('list','item1')
conn.blpop(['list','list2'],1)
('list2','item2')
conn.blpop(['list','list2'],1)
SET
Redis的集合和列表都可以存储多个字符串,他们之间的不同之处在于,列表可以存储多个相同的字符串,而集合则通过使用散列表来保证自己存储的每个字符串都是各不相同的。
因为Redis的集合使用无序方式存储元素,所以用户不能像使用列表那样,将元素推入集合的某一端,或者从集合的某一端弹出元素。不过用户可以使用SADD命令将元素添加到集合,或者使用SREM命令从集合里面移除元素。另外还可以通过SISMEMBER命令快速检查一个元素是否已经存在于集合中,或者使用SMEMBERS命令获取集合包含的所有元素(如果集合包含的元素非常多,那么SMEMBERS命令的执行速度可能会很慢,所以谨慎使用这个命令)。
//在尝试将一个元素添加到集合的时候,命令返回1表示这个元素被成功地添加到了集合里面,而返回0则表示这个元素以及存在于集合中。
sadd set-key item
(integer) 1
sadd set-key item2
(integer) 1
sadd set-key item3
(integer) 1
sadd set-key item
(integer) 0
//获取集合包含的所有元素将得到一个由元素组成的序列,Python客户端会将这个序列转换成Python集合
semebers set-key
1) "item"
2) "item2"
3) "item3"
//检查一个元素是否存在集合中,Python客户端会返回一个布尔值表示检查结果。
sismember set-key item4
(integer) 0
sismember set-key item
(integer) 1
//在使用命令移除集合中的元素时,命令会返回被移除元素的数量。
srem set-key item2
(integer) 1
srem set-key item2
(integer) 0
散列
Redis的散列可以存储多个键值对之间的映射。和字符串一样,散列存储的值既可以是字符串又可以是数字值,并且用户同样可以对散列存储的数字值执行自增操作或者自减操作。
hset hash-key sub-key1 value1
(integer) 1
hset hash-key sub-key2 value2
(integer) 1
hset hash-key sub-key1 value1
(integer) 0
hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"
//在删除键值对的时候,命令会返回一个值来表示给定的键在移除之前是否存在于散列里面
有序集合
有序集合和散列一样,都用于存储键值对:有集合的键被称为成员,每个成员都是各不相同;而有序集合的值则被称为分值,分值必须为浮点数。