什么是Redis
Remote Dictionary Server,即远程字典服务,是一款开源的、基于内存也支持持久化的key-value数据库,提供多种语言API。通常应用于需要处理大规模数据读写的场景中。
和其它关系型数据相比,其有以下优势:
-
基于内存,读写操作性能优越,可以支持每秒十几万次的读写操作;
-
支持集群、分布式、主动同步等配置;
-
具备事务能力,保证了高并发场景下数据的安全和一致性。
数据结构
Redis支持五种数据结构:
字符串(string):value中存储的是一个字符串,编码格式是raw或int
哈希(hash):value中存储的是一个哈希映射表(hashmap),redis提供了存取该哈希表内容的接口;
列表(list):value是通过一个双向链表存储;
集合(set):即hashset,可以将重复的元素放入而set会自动去重,底层实现是哈希映射表(hashmap),一个value永远是null的hashmap。
有序集合(zset):内部使用hashmap和跳跃表skiplist来保证数据的存储和有序。Sorted Set的实现是HashMap(element->score, 用于实现ZScore及判断element是否在集合内),和SkipList(score->element,按score排序)的混合体。SkipList有点像平衡二叉树那样,不同范围的score被分成一层一层,每层是一个按score排序的链表。
python API
1. 数据库连接
-
直接连接
import redis
# 直接连接
red = redis.Redis(host='localhost', port=6379, db=1)
-
连接池连接
import redis
pool = redis.ConnectionPool(host='localhost', port=6379, db=1)
red = redis.Redis(connection_pool=pool)
连接池连接原理是,通过预先创建多个连接,当进行redis操作时可以直接获取已经创建的连接进行操作,而且操作完成后不会释放,用于后续的其它redis操作;这样可以避免频繁地进行redis创建和释放,从而提高性能。
2. 数据存取
# String类型存取
red.set('key', 'value') # 默认不存在则创建,存在则修改
red.setex('key', 'value', time) # 设置过期时间,秒,px为毫秒
red.mset({'key1':'value1', 'key2':'value2'}) # 批量设置值
red.mget({'key1':'value1', 'key2':'value2'}) # 批量获取值
red.get('key') # 获取'key'对应的值
red.strlen('key') # 返回'key'对应值的长度
red.append('key', 'hello') # 在‘key’对应的值后面追加内容
# Hash类型存取,hash即一个name对应一个dic字典
red.hset('name', 'key', 'value') # 设置一对key-value,不存在则创建,存在则修改
red.hget('name', 'key')
red.hmset('name', dic)
red.hgetall('name')
red.hmget('name', 'key1', 'key2') # 批量输出键对应的值
red.hlen('name') # 获取hash键值对个数
red.hkeys('name') # 获取hash中的所有key
red.hvals('name') #获取hash中所有value
red.hexists('name', 'key')
red.hdel('name', 'key') # 删除key对应的键值对
red.incrby('name', 'key', amount=10) # 将amount的值与hash中key对应的值相加,key不存在则创建key=amount(amount为整数)
# list类型存取,list即一个name对应一个列表
red.lpush('name', 'list_item1', 'list_item2') # 元素从list左边添加
red.rpush('name', 'list_item1', 'list_item2') # 元素从list右边添加
red.lpushx('name', 'list_item1', 'list_item2') # 当name存在时,元素从list左边添加
red.rpushx('name', 'list_item1', 'list_item2') # 当name存在时,元素从list右边添加
red.llen('name')
red.linsert('name', BEFORE/AFTER, 'item1', 'item2') # 在列表中找到第一个元素’item1‘,在他的前面插入'item2'
red.lset('name', index, value)
red.lrem('name', value, num=0) # 删除list中的value值,num=0删除列表中所有的value值,num=2从前往后删除两个,num=-2从后向前删除两个
red.lpop('name') # 删除list左侧第一个元素,并返回
red.lindex('name', index)
red.lrange('name', start_index, end) # 分片获取list元素
red.ltrim('name', 0, 2) # 移除list中没有在该索引(0-2)之内的值
# set类型存取,set即不允许重复的列表
red.sadd('name', 'item1')
red.sadd('name', 'item1', 'item2')
red.scard('name') # 获取集合中的元素个数
red.smembers('name')
red.sdiff('name', 'name1', 'name2') # 在'name'中,且不在'name1'和'name2'的元素
red.sismember('name', 'item') # 检测’item‘是否是’name‘集合中的元素
red.smove('name1', 'name2', 'item') # 将’item‘从’name1‘中移到’name2‘中
red.spop('name') # 从集合的右侧移除一个元素其它常用操作
3. 其它常用操作
red.flushdb(asynchronous=False) # 清空当前db中的数据,默认同步
red.flushall(asynchronous=False)
4.管道
redis默认在执行每次操作请求时都会创建和断开连接,或者去连接池申请连接和释放连接,这就需要在服务器和客户端之前反复进行TCP数据包的传送。而管道(pipeline)可以实现一次请求指定多个命令,并且默认一次pipeline是原子性操作。
import redis
pool = redis.ConnectionPool(host='localhost', prot=6379, decode_response=True) # decode_response=True,默认取出结果是字节,true返回字符串
red = redis.Redis(connection_pool=pool)
# 创建管道
pipe = red.pipeline()
# 向管道里注入命令
pipe.set('name', 'jane')
pipe.sadd('gender', 'male')
# 执行管道命令
pipe.execute()
# 命令和执行可以连起来
Redis事务
Redis事务本质上是一组命令的集合。
Redis事务不保证原子性,且没有回滚。事务中的单条命令是原子性执行的,事务中任意命令执行失败,其余命令会继续被执行。
Redis事务的三个阶段:
-
开始事务(MULTI)
-
命令入队
-
执行事务(EXEC)
相关指令
MULTI:标记一个事务的开始
EXEC: 执行事务中的所有命令
DISCARD: 取消事务,放弃事务中的所有命令
WATCH keyname:监视一个或多个key,如果事务执行之前被监视的key被改动,则事务将被打断(类似乐观锁)
UNWATCH: 取消watch对所有key的监控
分布式锁
分布式锁是用来解决实际应用中并发冲突的一种手段。
# 分布式锁实现
class RedisLock(object):
def __init__(self, lock_name, identifier, expire=10):
pool = redis.ConnectionPool(host='10.68.4.*', port='6479', password='root', db=3)
self.conn = redis.Redis(connection_pool=pool)
self.lock_name = lock_name
self.identifier = identifier
self.expire = expire
# 获取锁
def acquire_lock(self):
if self.conn.setnx(self.lock_name, self.identifier):
self.conn.expire(self.lock_name, self.expire)
return self.identifier
elif not self.conn.ttl(self.lock_name):
self.conn.expire(self.lock_name, self.expire)
return False
# 释放锁
def release_lock(self):
pipe = self.conn.pipeline()
while True:
try:
pipe.watch(self.lock_name)
if pipe.get(self.lock_name) == self.identifier:
pipe.delete(self.self.lock_name)
pipe.execute()
return True
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
count = 10
def func(thread_index):
my_lock = RedisLock('locker_name', 'ticket')
my_lock.acquire_lock()
print("线程:{}--得到了锁".format(thread_index))
global count
if count < 1:
print("线程:{}--没抢到票,票被抢完了".format(thread_index))
count -= 1
my_lock.release_lock()
if __name__ == '__main__':
for i in range(50):
t = threading.Thread(target=func, args=(i,))
t.start()
这里没有加获取锁的超时时间。