首页 » Redis » Redis学习04之 List列表

Redis学习04之 List列表

前两节学习了String和Hash, Hash可以把关连性的字段组合到一起用一个KEY, key值多同样会耗费内存和CPU, 在这点上Hash要优于String,  当然String在字符操作上如追加、部分值更新、Key 过期上更加灵活, 都是为特定的场景制定,这里学习另一个数据结构LIST列表,List顾名思义可以认为左右延伸的队列,一种有序存放的数据结构。

常用的操作有

推入

LPUSH:将元素推入到列表左端
RPUSH:将元素推入到列表右端
LPUSHX、RPUSHX:只对已存在的列表执行推入操作

移除

LPOP:弹出列表最左端的元素
RPOP:弹出列表最右端的元素
RPOPLPUSH:将右端弹出的元素推入到左端

其它

LSET 更新列表指定值

LTRIM 截断指定位置的值

LLEN 列表长度

测试命令

[root@ANBOB.COM ~]# redis-cli
127.0.0.1:6379> lpush Order u1
(integer) 1
127.0.0.1:6379> lpush Order u2
(integer) 2
127.0.0.1:6379> lpush Order u3
(integer) 3

127.0.0.1:6379> lrange Order 0 -1
1) "u3"
2) "u2"
3) "u1"
127.0.0.1:6379> rpush Order u5
(integer) 4
127.0.0.1:6379> lrange Order 0 -1
1) "u3"
2) "u2"
3) "u1"
4) "u5"
127.0.0.1:6379> lindex Order 0
"u3"
127.0.0.1:6379> lindex Order 1
"u2"
127.0.0.1:6379> rpush Order 9
(integer) 5
127.0.0.1:6379> rpush Order u1
(integer) 6
127.0.0.1:6379> rpush Order u9
(integer) 7
127.0.0.1:6379> lrange Order 0 -1
1) "u3"
2) "u2"
3) "u1"
4) "u5"
5) "9"
6) "u1"
7) "u9"
127.0.0.1:6379> rpushx Order u9
(integer) 8
127.0.0.1:6379> rpushx Order u10
(integer) 9
127.0.0.1:6379> lrange Order 0 -1
1) "u3"
2) "u2"
3) "u1"
4) "u5"
5) "9"
6) "u1"
7) "u9"
8) "u9"
9) "u10"
127.0.0.1:6379> rpushx Order u10
(integer) 10
127.0.0.1:6379> lrange Order 0 -1
 1) "u3"
 2) "u2"
 3) "u1"
 4) "u5"
 5) "9"
 6) "u1"
 7) "u9"
 8) "u9"
 9) "u10"
10) "u10"
127.0.0.1:6379> rpushx Order u11
(integer) 11
127.0.0.1:6379> lrange Order 0 -1
 1) "u3"
 2) "u2"
 3) "u1"
 4) "u5"
 5) "9"
 6) "u1"
 7) "u9"
 8) "u9"
 9) "u10"
10) "u10"
11) "u11"
127.0.0.1:6379> rpushx Order1 u11
(integer) 0
127.0.0.1:6379> lrange Order1 0 -1
(empty array)
127.0.0.1:6379> lpush Order u100 u200 u300
(integer) 14
127.0.0.1:6379> lrange Order1 0 -1
(empty array)
127.0.0.1:6379> lrange Order 0 -1
 1) "u300"
 2) "u200"
 3) "u100"
 4) "u3"
 5) "u2"
 6) "u1"
 7) "u5"
 8) "9"
 9) "u1"
10) "u9"
11) "u9"
12) "u10"
13) "u10"
14) "u11"
127.0.0.1:6379> lpop u300
(nil)
127.0.0.1:6379> lpop Order
"u300"
127.0.0.1:6379> lrange Order 0 -1
 1) "u200"
 2) "u100"
 3) "u3"
 4) "u2"
 5) "u1"
 6) "u5"
 7) "9"
 8) "u1"
 9) "u9"
10) "u9"
11) "u10"
12) "u10"
13) "u11"
127.0.0.1:6379> lpop Order
"u200"
127.0.0.1:6379> lpop Order
"u100"
127.0.0.1:6379> lrange Order 0 -1
 1) "u3"
 2) "u2"
 3) "u1"
 4) "u5"
 5) "9"
 6) "u1"
 7) "u9"
 8) "u9"
 9) "u10"
10) "u10"
11) "u11"
127.0.0.1:6379> rpop Order
"u11"
127.0.0.1:6379> rpop Order
"u10"
127.0.0.1:6379> rpoplpush Order Last
"u10"
127.0.0.1:6379> rpoplpush Order Last
"u9"
127.0.0.1:6379> rpoplpush Order Last
"u9"
127.0.0.1:6379> lrange Order 0 -1
1) "u3"
2) "u2"
3) "u1"
4) "u5"
5) "9"
6) "u1"
127.0.0.1:6379> lrange Last 0 -1
1) "u9"
2) "u9"
3) "u10"
127.0.0.1:6379> llen Order
(integer) 6
127.0.0.1:6379> llen last
(integer) 0
127.0.0.1:6379> llen LAST
(integer) 0
127.0.0.1:6379> llen Last
(integer) 3

应用场景

FIFO 队列,如下发短信,或秒杀活动


>>> import redis
>>> import json
>>>
>>> client = redis.Redis()
>>>
>>> for i in range(100):
...     print('telnum {0:011d} add completed!'.format(i))
...     tel={'telnum':str(i).zfill(011)}
...     client.rpush('phone_queue',json.dumps(tel))
...
telnum 00000000000 add completed!
1L
telnum 00000000001 add completed!
2L
telnum 00000000002 add completed!
3L
telnum 00000000003 add completed!
4L
...

模拟下发短信

 sendsms.py

import redis
import json
client = redis.Redis()

def send_sms(tel):
    print('----{}----'.format(tel))
    return 1
next_phone_info = ''
while True:
    phone_bytes = client.lpop('phone_queue')
    if not phone_bytes:
        print('All message send completed!')
        break
    phone_info = json.loads(phone_bytes)
    print phone_info
    print type(phone_info)
    retry = phone_info.get('retry', 0)
    telnum = phone_info['telnum']
    rest = send_sms(telnum)
    if rest:
        print('The telnum {} send completed...'.format(telnum))
        continue

    if retry >= 3:
        print('The telnum {} send failed. try 3 times'.format(telnum))
        continue

    next_phone_info = {'telnum':telnum, 'retry': retry+1}
client.rpush('phone_queue', json.dumps(next_phone_info))
 
[root@ANBOB.COM sendsms]# python sendsms.py
----000000000----
The telnum 000000000 send completed...
----000000001----
The telnum 000000001 send completed...
----000000002----
The telnum 000000002 send completed...
----000000003----
The telnum 000000003 send completed...
----000000004----
The telnum 000000004 send completed...
...

由于Redis是一个单线程、单进程的数据库,及时从多台服务器同时从一个公共REDIS数据库里POP数据也不会读取重复, redisk会自动排队,不用担心分布式进程重复发送问题。如上面的逻辑,发送如果失败会放回列表的右侧,重新发送。
另外对于秒杀活动,使用传统关系型数据库的事务和锁,如只限前200用户购买,可能因为锁并发堵塞产生不公平的现象,同时数据库可能也无法承担这么高的大事务量, 此时可以应用和redis协调部署,把购买用户丢进LIST的右测队列,如果llen长度超过200,前台就直接break请求就OK,减少了对数据库无效的冲击。

同样也可以做如关系型数据库中的分页查询

[root@ANBOB.COM list]# vi paging.py
class Paging:

    def __init__(self, client, key):
        self.client = client
        self.key = key

    def add(self, item):
        self.client.lpush(self.key, item)

    def get_page(self, page_number, item_per_page):
        """
        """
        start_index = (page_number - 1) * item_per_page
        end_index = page_number * item_per_page - 1
        return self.client.lrange(self.key, start_index, end_index)

    def size(self):
        """
        """
        return self.client.llen(self.key)


[root@ANBOB.COM list]# python paging.py
[root@ANBOB.COM list]# python
Python 2.7.5 (default, Nov 20 2015, 02:00:19)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from redis import Redis
>>> from paging import Paging
>>> client = Redis(decode_responses=True)
>>> topics = Paging(client, "user-topics")
>>> for i in range(20):
...   topics.add(i)
...
>>> topics.get_page(2, 5)
[u'14', u'13', u'12', u'11', u'10']

–over —

打赏

,

对不起,这篇文章暂时关闭评论。