什么是連接池
通常情況下, 當我們需要做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中, 我們可以看到獲取連接的過程是
- 從可用連接集合嘗試獲取連接,
- 如果獲取不到, 重新創建連接
- 將獲取到的連接添加到正在使用的連接集合
上面是往_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) 和 正在使用的連接列表從而實現連接池管理
