python 的 redis 库,连接池怎么用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 的 redis 库,连接池怎么用相关的知识,希望对你有一定的参考价值。

参考技术A #!/usr/bin/env python
# -*- coding: utf-8 -*-

import redis

host = r'localhost'
port = 6379
POOL = redis.ConnectionPool(host=host, port=port, db=0)
my_server = redis.Redis(connection_pool=POOL)


def read_in_data(file_name):
    data = []
    with open(file_name, 'r') as f:
        for line in f:
            line = line.strip()
            data.append(line.split(','))
    return data


def write_data_to_redis(data):
    my_server.set('counter', 0)
    for items in data:
        my_server.incr('counter')
        for item in items:
            my_server.rpush(items[0], item)


def get_data_from_redis():
    keys = my_server.keys()
    for key in keys:
        data_type = my_server.type(key)
        if data_type == 'list':
            val = my_server.lrange(key, 0, -1)
        elif data_type == 'string':
            val = my_server.get(key)
        print "%s:%s" % (key, val)


if __name__ == '__main__':
    fileName = r'/Users/qinchuanqing/Code/workspace/IMP/installation/storage/NeSchedule.txt'
    data = read_in_data(fileName)
    write_data_to_redis(data)
    print 'all data items number is %s' % my_server.get('counter')
    print my_server.keys()
    print get_data_from_redis()

参考技术B Redis 的连接池是多线程安全的、多进程安全的、自动重连的。

你扔 flask.g 之类的全局的地方当然也行,反正 Redis 总是会使用连接池(不指定它每次就用一个新的)。显式指定连接池的话差异不大,反正你总是要手动在某个全局的地方存一样东西(连接池对象或者 Redis 对象)。

python redis之连接池的原理

什么是连接池

通常情况下, 当我们需要做redis操作时, 会创建一个连接, 并基于这个连接进行redis操作, 操作完成后, 释放连接,

一般情况下, 这是没问题的, 但当并发量比较高的时候, 频繁的连接创建和释放对性能会有较高的影响

于是, 连接池就发挥作用了

连接池的原理是, 通过预先创建多个连接, 当进行redis操作时, 直接获取已经创建的连接进行操作, 而且操作完成后, 不会释放, 用于后续的其他redis操作

这样就达到了避免频繁的redis连接创建和释放的目的, 从而提高性能了

原理

那么, 在redis-py中, 他是怎么进行连接池管理的呢

连接池使用

首先看下如何进行连接池操作的

rdp = redis.ConnectionPool(host=‘127.0.0.1‘, port=6379, password=‘xxxxx‘)
rdc = redis.StrictRedis(connection_pool=rdp)
rdc.set(‘name‘, ‘Yi_Zhi_Yu‘)
rdc.get(‘name‘)

原理解析

当redis.ConnectionPool 实例化的时候, 做了什么

def __init__(self, connection_class=Connection, max_connections=None,
                 **connection_kwargs):
        max_connections = max_connections or 2 ** 31
        if not isinstance(max_connections, (int, long)) or max_connections < 0:
            raise ValueError(‘"max_connections" must be a positive integer‘)

        self.connection_class = connection_class
        self.connection_kwargs = connection_kwargs
        self.max_connections = max_connections

这个连接池的实例化其实未做任何真实的redis连接, 仅仅是设置最大连接数, 连接参数和连接类

StrictRedis 实例化的时候, 又做了什么

 def __init__(self, ...connection_pool=None...):
        if not connection_pool:
            ...
            connection_pool = ConnectionPool(**kwargs)
        self.connection_pool = connection_pool

以上仅保留了关键部分代码

可以看出, 使用StrictRedis 即使不创建连接池, 他也会自己创建

到这里, 我们还没有看到什么redis连接真实发生

继续

下一步就是set 操作了, 很明显, 这个时候一定会发生redis连接(要不然怎么set)

def set(self, name, value, ex=None, px=None, nx=False, xx=False):
    ...
    return self.execute_command(‘SET‘, *pieces)

我们继续看看execute_command

 def execute_command(self, *args, **options):
        "Execute a command and return a parsed response"
        pool = self.connection_pool
        command_name = args[0]
        connection = pool.get_connection(command_name, **options)
        try:
            connection.send_command(*args)
            return self.parse_response(connection, command_name, **options)
        except (ConnectionError, TimeoutError) as e:
            connection.disconnect()
            if not connection.retry_on_timeout and isinstance(e, TimeoutError):
                raise
            connection.send_command(*args)
            return self.parse_response(connection, command_name, **options)
        finally:
            pool.release(connection)

终于, 在这我们看到到了连接创建

connection = pool.get_connection(command_name, **options)

这里调用的是ConnectionPool的get_connection

def get_connection(self, command_name, *keys, **options):
        "Get a connection from the pool"
        self._checkpid()
        try:
            connection = self._available_connections.pop()
        except IndexError:
            connection = self.make_connection()
        self._in_use_connections.add(connection)
        return connection

如果有可用的连接, 获取可用的链接, 如果没有, 创建一个

def make_connection(self):
        "Create a new connection"
        if self._created_connections >= self.max_connections:
            raise ConnectionError("Too many connections")
        self._created_connections += 1
        return self.connection_class(**self.connection_kwargs)

终于, 我们看到了, 在这里创建了连接

在ConnectionPool的实例中, 有两个list, 依次是_available_connections_in_use_connections,

分别表示可用的连接集合正在使用的连接集合, 在上面的get_connection中, 我们可以看到获取连接的过程是

  1. 从可用连接集合尝试获取连接,
  2. 如果获取不到, 重新创建连接
  3. 将获取到的连接添加到正在使用的连接集合

上面是往_in_use_connections里添加连接的, 这种连接表示正在使用中, 那是什么时候将正在使用的连接放回到可用连接列表中的呢

这个还是在execute_command里, 我们可以看到在执行redis操作时, 在finally部分, 会执行一下

pool.release(connection)

连接池对象调用release方法, 将连接从_in_use_connections 放回 _available_connections, 这样后续的连接获取就能再次使用这个连接了

release 方法如下

 def release(self, connection):
        "Releases the connection back to the pool"
        self._checkpid()
        if connection.pid != self.pid:
            return
        self._in_use_connections.remove(connection)
        self._available_connections.append(connection)

总结

至此, 我们把连接池的管理流程走了一遍, ConnectionPool通过管理可用连接列表(_available_connections) 和 正在使用的连接列表从而实现连接池管理

以上是关于python 的 redis 库,连接池怎么用的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate多库连接池

通用连接池帮你解决资源管理难题

golng mysql库连接池分析

spring可以不用连接池吗

python的urllib3库(http连接池)

Python连接redis,单例和线程池,遇到问题解析