初识Redis

什么是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事务的三个阶段:

  1. 开始事务(MULTI)

  2. 命令入队

  3. 执行事务(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()

这里没有加获取锁的超时时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值