Python脚本之操作Redis【一】

本文为博主原创,未经授权,严禁转载及使用。
本文链接:https://blog.csdn.net/zyooooxie/article/details/120334491

因为最近的需求常常接触到Redis,但公司都是用的redis Cluster,为了做展示,我在自己电脑装了 redis的客户端,就简单对 Redis instance做个分享;

个人博客:https://blog.csdn.net/zyooooxie

Redis

Redis是一个开源的内存数据存储系统,也被称为数据结构服务器。它支持多种数据结构,包括字符串、哈希表、列表、集合、有序集合等,并提供了丰富的操作命令,可以对这些数据结构进行快速、高效的读写操作。

Redis具有以下特点:

  1. 内存存储:Redis将数据存储在内存中,因此具有非常高的读写性能。同时,Redis还支持数据持久化,可以将数据保存到磁盘上,以防止数据丢失。

  2. 多种数据结构:Redis支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等,可以满足不同场景下的需求。

  3. 高效的操作命令:Redis提供了丰富的操作命令,可以对数据进行快速、高效的读写操作。例如,可以使用命令对字符串进行增删改查,对列表进行插入和删除元素等。

  4. 分布式缓存:Redis可以作为缓存系统使用,将热点数据存储在内存中,加快读取速度,并减轻后端数据库的负载。

  5. 发布订阅模式:Redis支持发布订阅模式,可以实现消息的发布和订阅,用于构建实时通信、消息队列等应用。

  6. 事务支持:Redis支持事务操作,可以将多个命令打包成一个事务,保证这些命令的原子性执行。

  7. 高可用性:Redis支持主从复制和哨兵机制,可以实现数据的备份和自动故障转移,提高系统的可用性。

总之,Redis是一个功能强大、性能优异的内存数据存储系统,适用于各种场景,包括缓存、计数器、排行榜、实时分析等。它的简单易用和高可靠性使其成为许多应用程序的首选存储解决方案。

Redis instance

Python脚本之操作Redis【一】_第1张图片

Python脚本之操作Redis【一】_第2张图片

Redis命令【一】

想要详细学习 可以参考 http://doc.redisfans.com/index.html

在我的日常工作中,常用到的有:

  1. 服务器相关的

DBSIZE
INFO

  1. 通用的

KEYS 【KEYS命令 不能用于生产环境】
TYPE KEY_NAME
DEL KEY_NAME
EXISTS KEY_NAME
EXPIRE KEY_NAME seconds
TTL KEY_NAME
SCAN cursor
UNLINK KEY_NAME

  1. String:

GET KEY_NAME
SET KEY_NAME KEY_VALUE
MGET KEY_NAME1 KEY_NAME2
MSET KEY_NAME1 KEY_VALUE1 KEY_NAME2 KEY_VALUE2

代码

"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
@email: [email protected]
"""

gl_port = 6379
gl_pwd = 'zyooooxie'
gl_host = '127.0.0.1'

gl_real_string = 'TEST_string'
gl_real_hash = 'TEST_hash'
gl_real_list = 'TEST_list'
gl_real_set = 'TEST_set'
gl_no_exist = 'TEST_zyooooxie'

# pip install redis==4.4.1
# https://redis.readthedocs.io/en/v4.4.1/commands.html


def redis_connect():
    # https://redis.readthedocs.io/en/v4.1.2/connections.html
    # https://redis.readthedocs.io/en/v4.1.2/examples/connection_examples.html

    # All responses are returned as bytes in Python. To receive decoded strings, set decode_responses=True.
    r = Redis(host=gl_host, port=gl_port, decode_responses=True, password=gl_pwd, db=0)

    # Based on configuration, an instance will either use a ConnectionPool, or Connection object to talk to redis.
    Log.info(r)
    Log.info(type(r))

    Log.error('redis 已连接')

    return r
    

def test_server():
    r = redis_connect()

    # INFO [section [section ...]]
    # 返回关于 Redis 服务器的各种信息和统计数值
    # 通过给定可选的参数 section ,可以让命令只返回某一部分的信息

    # info(): Returns a dictionary containing information about the Redis server

    Log.info(r.info())  # 当不带参数直接调用 INFO 命令时,使用 default 作为默认参数

    Log.info(r.info(section='replication'))  # 主/从复制信息
    Log.info(r.info(section='Clients'))  # 已连接客户端信息
    Log.info(r.info(section='Server'))  # Redis 服务器信息

    Log.info('----')

    # DBSIZE

    # 返回值:当前数据库的 key 的数量。

    # dbsize(): Returns the number of keys in the current database
    Log.info(f'the number of keys : {r.dbsize()}')

    r.close()

"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
@email: [email protected]
"""


def test_generic_1():
    # https://redis.io/commands/?group=generic

    r = redis_connect()

    # KEYS pattern
    # 查找所有符合给定模式 pattern 的 key

    # 返回值: 符合给定模式的 key 列表

    # keys():Returns a list of keys matching ``pattern``

    # Supported glob-style patterns:
    # h?llo matches hello, hallo and hxllo
    # h*llo matches hllo and heeeello
    # h[ae]llo matches hello and hallo, but not hillo
    # h[^e]llo matches hallo, hbllo, ... but not hello
    # h[a-b]llo matches hallo and hbllo

    keys_res = r.keys('key*')
    Log.info(keys_res)
    Log.info(len(keys_res))
    Log.info(type(keys_res))

    # Don't use KEYS in your regular application code.
    # If you're looking for a way to find keys in a subset of your keyspace, consider using SCAN or sets.

    Log.info('----')

    # EXISTS key [key ...]
    # 检查给定 key 是否存在

    # 返回值:若 key 存在,返回 1 ,否则返回 0

    # exists():Returns the number of ``names`` that exist

    Log.info(r.exists(gl_no_exist))  # 0

    Log.info(r.exists(gl_real_string))  # 1
    Log.info(r.exists(gl_real_hash))
    Log.info(r.exists(gl_real_list))
    Log.info(r.exists(gl_real_set))

    Log.info('https://redis.io/commands/exists/')
    # Starting with Redis version 3.0.3: Accepts multiple key arguments.

    Log.info('----')

    # DEL key [key ...]
    # 删除给定的一个或多个 key。不存在的 key 会被忽略。

    # 返回值:被删除 key 的数量

    # delete():Delete one or more keys specified by ``names``

    Log.info(f'总长度:{len(keys_res)}')
    r_keys_res_1 = keys_res[0:2]
    Log.info(r_keys_res_1)

    for k in r_keys_res_1:
        Log.info(r.delete(k))

    r_keys_res_2 = keys_res[4: 150]  # 150随便写的,没这么多
    r_keys_res_2.append(gl_no_exist)
    Log.info(r_keys_res_2)
    Log.info(r.delete(*r_keys_res_2))

    Log.info(r.delete(gl_no_exist))  # 0

    Log.info('----')

    # EXPIRE key seconds [NX | XX | GT | LT]
    # 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。

    # 返回值: 设置成功返回 1;当 key 不存在或者不能为 key 设置生存时间时,返回 0

    # expire(): Set an expire flag on key ``__name`` for ``time`` seconds.
    # ``time`` can be represented by an integer or a Python timedelta object

    abc = keys_res[3]
    Log.info(f'{abc, r.ttl(abc)}')

    Log.info(r.expire(abc, 600))
    Log.info(f'{r.ttl(abc)}')

    Log.info(r.expire(abc, datetime.timedelta(hours=2)))
    Log.info(f'{r.ttl(abc)}')

    Log.info('https://redis.io/commands/expire/')
    # Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.

    Log.info(r.expire(gl_no_exist, 600))

    r.close()
"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
@email: [email protected]
"""


def test_generic_2():
    r = redis_connect()

    # TYPE key
    # 返回 key 所储存的值的类型

    # 返回值:the type of key, or none when key doesn't exist.

    # type():Returns the type of key ``__name``

    Log.info(r.type(gl_real_string))  # string
    Log.info(r.type(gl_real_hash))  # hash
    Log.info(r.type(gl_real_list))  # list
    Log.info(r.type(gl_real_set))  # set

    Log.info(r.type(gl_no_exist))  # none

    Log.info('----')

    # TTL key
    # 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。

    # 返回值:
    # 当 key 不存在时,返回 -2 。
    # 当 key 存在 但 没有设置剩余生存时间时【永久生效】,返回 -1 。
    # 否则,以秒为单位,返回 key 的剩余生存时间。

    # ttl():Returns the number of seconds until the key ``__name`` will expire

    Log.info(r.ttl(gl_real_string))  # -1
    Log.info(r.ttl(gl_real_hash))
    Log.info(r.ttl(gl_real_list))
    Log.info(r.ttl(gl_real_set))

    Log.info(r.ttl(gl_no_exist))  # -2

    Log.info('----')

    # SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

    # SCAN is a cursor based iterator.
    # This means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call.

    # 增量地迭代,每次执行都只会返回少量元素。
    # SCAN 迭代的是当前数据库中的所有数据库键。

    # SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。
    # 当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

    abc = r.scan(0)
    Log.info(abc)

    # SCAN 命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。
    # the SCAN return value is an array of two values: the first value is the new cursor to use in the next call, the second value is an array of elements.

    abc2 = r.scan(abc[0])
    Log.info(abc2)

    Log.info(r.scan(abc2[0]))

    Log.info('--------------------')

    # 通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素, 这一点可以通过在执行增量式迭代命令时, 通过给定 MATCH  参数来实现。
    # 需要注意的是, 对元素的模式匹配工作是在命令从数据集中取出元素之后, 向客户端返回元素之前的这段时间内进行的,
    # 所以如果被迭代的数据集 只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中 都不返回任何元素。

    key_name = 'TEST*'

    abc = r.scan(0, key_name)
    Log.info(abc)

    abc2 = r.scan(abc[0], key_name)
    Log.info(abc2)

    Log.info(r.scan(abc2[0], key_name))

    # 以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历(full iteration)。
    # Starting an iteration with a cursor value of 0, and calling SCAN until the returned cursor is 0 again is called a full iteration.

    # 只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。
    # 如果新游标返回0,表示迭代已结束,即不再有下一页的数据;如果返回数据为空,但是游标不为空表示迭代还未结束。

    Log.info('--------------------')

    cur = 0

    while True:
        # cur, data = r.scan(cur, key_name)

        cur, data = r.scan(cur, key_name, count=500)
        # 用户可以通过 COUNT 选项来指定每次迭代返回元素的最大值。
        # 基本上, COUNT 选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。

        # COUNT 参数的默认值为 10 。

        # 并非每次迭代都要使用相同的 COUNT 值。用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。

        # 并不保证每次执行都返回某个给定数量的元素。 增量式命令甚至可能会返回零个元素。

        # Log.info(cur)
        Log.info(len(data))

        if cur == 0:
            break

    Log.info('--------------------')

    # SCAN 命令 以及其他增量式迭代命令, 在进行完整遍历的情况下可以为用户带来以下保证: 从完整遍历开始直到完整遍历结束期间, 一直存在于数据集内的所有元素都会被完整遍历返回;
    # 这意味着, 如果有一个元素, 它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中, 那么 SCAN 命令总会在某次迭代中将这个元素返回给用户。

    # The SCAN command and the closely related commands SSCAN, HSCAN and ZSCAN are used in order to incrementally iterate over a collection of elements.
    #
    # SCAN iterates the set of keys in the currently selected Redis database.
    # SSCAN iterates elements of Sets types.
    # HSCAN iterates fields of Hash types and their associated values.
    # ZSCAN iterates elements of Sorted Set types and their associated scores.

    Log.info('----')

    # UNLINK key [key ...]

    # This command is very similar to DEL: it removes the specified keys. Just like DEL a key is ignored if it does not exist.

    # However the command performs the actual memory reclaiming in a different thread, so it is not blocking, while DEL is.

    # 返回值:the number of keys that were unlinked.

    Log.info('Available since:4.0.0')

    Log.info(r.unlink(gl_no_exist, 'key1', 'abc123'))
    Log.info(r.unlink('key2', 'key3'))
    Log.info(r.unlink('key5'))

    r.close()

Python脚本之操作Redis【一】_第3张图片

"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
@email: [email protected]
"""


def test_string():
    # https://redis.io/commands/?group=string

    r = redis_connect()
    test_str_ = 'test:zyooooxie:str'

    Log.info('----')

    # GET key
    # 返回 key 所关联的字符串值。

    # 返回值:当 key 不存在时,返回 None ;否则,返回 key 的值。如果 key 不是字符串类型,那么返回一个错误。

    # get(): Return the init_value at key ``__name``, or None if the key doesn't exist

    Log.info(r.get(gl_real_string))
    Log.info(r.get(gl_no_exist))

    Log.info(r.get(test_str_))
    r.delete(test_str_)
    Log.info(r.get(test_str_))

    Log.info('----')

    # SET key value [NX | XX] [GET] [EX seconds | PX milliseconds |
    # 将字符串值 init_value 关联到 key
    # 如果 key已经持有其他值, SET 就覆写旧值,无视类型。
    # 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键 原有的 TTL 将被清除。

    # 返回值:SET 在设置操作成功完成时,才返回 OK;如果设置了 NX 或者 XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空。

    # SET key init_value [EX seconds] [PX milliseconds] [NX|XX]
    # EX second :设置键的过期时间为 second 秒
    # PX millisecond :设置键的过期时间为 millisecond 毫秒
    # NX :只在键不存在时,才对键进行设置操作
    # XX :只在键已经存在时,才对键进行设置操作

    # set(): ex=None, px=None, nx=False, xx=False
    # ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
    # ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
    # ``nx`` if set to True, set the value at key ``name`` to ``value`` only if it does not exist.
    # ``xx`` if set to True, set the value at key ``name`` to ``value`` only if it already exists.

    ex_time = random.randint(1200, 1260)
    value = random.randint(10, 100)

    res = r.set(test_str_, value, ex=ex_time)
    Log.info(res)  # True
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    time.sleep(4)
    Log.info(r.ttl(test_str_))

    Log.info('----')

    ex_time = datetime.timedelta(hours=1)
    res = r.set(test_str_, value + 10000, ex=ex_time)
    Log.info(res)
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    Log.info('----')

    px = (random.randint(2000, 3000)) * 1000
    res = r.set(test_str_, value=value + 20000, px=px)
    Log.info(res)
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    Log.info('----')

    px = datetime.timedelta(days=1)
    res = r.set(test_str_, value=value + 30000, px=px)
    Log.info(res)
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    Log.info('----')

    res = r.set(test_str_, value=value + 40000, nx=True)
    Log.info(res)  # None
    Log.info('已存在的key,实际value不变;set()失败')
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    Log.info('----')

    res = r.set(gl_no_exist, value='no exist', nx=True, px=600 * 1000)
    Log.info(res)  # True
    Log.info('原本不存在的key;set()成功')
    Log.info(r.get(gl_no_exist))

    time.sleep(3)
    Log.info(r.ttl(gl_no_exist))
    Log.info(r.delete(gl_no_exist))

    Log.info('----')

    res = r.set(test_str_, value=value + 50000, xx=True, ex=2222)
    Log.info(res)  # True
    Log.info('已经存在的key,实际value、失效时间 已经修改;set()成功')
    Log.info(r.get(test_str_))
    Log.info(r.ttl(test_str_))

    Log.info('----')

    res = r.set(gl_no_exist, value='no exist' * 2, xx=True)
    Log.info(res)  # None
    Log.info('原本不存在的key-此时也不存在;ttl为-2;get为None;set()失败')
    Log.info(r.get(gl_no_exist))
    Log.info(r.ttl(gl_no_exist))

    Log.info('https://redis.io/commands/set')
    # Redis version >= 2.6.12: Added the `EX`, `PX`, `NX` and `XX` options.

    Log.info('----')

    # MSET key value [key value ...]
    # 同时设置一个或多个 key-init_value 对。
    # 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值。

    # 返回值: 总是返回 OK (因为 MSET 不可能失败)

    # mset(): Sets key/values based on a mapping. Mapping is a dictionary of key/init_value pairs.  Both keys and values should be strings or types that can be cast to a string via str().

    # init_value:Convert to a bytes, string, int or float first.

    res = r.mset(
        {'key1': 'zyooooxie - 1111',
         'key2': 2222,
         'key3': str(('value3', 3333)),
         'key4': 0.4444,
         'key5': 5555.5555,
         'key6': str({'6666': 6666}),
         'key7': str({7777}),
         'key8': 'True',
         test_str_: str({'最新123', 456, '789.000', (1.1, 22, 333.333)}),
         'abc123': 'https://blog.csdn.net/zyooooxie'}
    )
    Log.info(res)  # True
    Log.info(r.get('abc123'))
    Log.info(r.get('key1'))

    r.mset({'key1': '新的1', 'key2': '新的2'})
    Log.info(r.get('key1'))

    Log.info('----')

    # MGET key [key ...]
    # 返回所有(一个或多个)给定 key 的值。
    # 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。

    # 返回值:一个包含所有给定 key 的值的列表。

    # mget(): Returns a list of values ordered identically to ``keys``

    Log.info(r.mget('key1', 'key2', 'key1', 'key2', 'key3', 'key4', test_str_, gl_no_exist, 'abc123'))

    r.expire(test_str_, 600)

    r.close()

pip install hiredis【Hiredis is a C library maintained by the core Redis team. Pieter Noordhuis was kind enough to create Python bindings. Using Hiredis can provide up to a 10x speed improvement in parsing responses from the Redis server. The performance increase is most noticeable when retrieving many pieces of data, such as from LRANGE or SMEMBERS operations.】

本文链接:https://blog.csdn.net/zyooooxie/article/details/120334491

交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie

你可能感兴趣的:(中间件学习,redis,python)