什么是连接池?

在网上常常给出这样的解释:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

更通俗一点就是,连接池维护着项目于数据库之间的联系,保证项目不会过于频繁的去连接数据库导致数据库压力过大。

为什么要使用连接池?

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。如果连接池存在,操作只需要把任务丢给连接池,至于对数据库操作的进程等一系列问题,全权交给连接池来进行操作,由连接池自己控制与数据库的访问与操作。在没有连接池的情况下,连接数据库通常需要以下步骤:

  • TCP建立连接的三次握手
  • MySQL认证的三次握手
  • 真正的SQL执行
  • MySQL的关闭
  • TCP的四次握手关闭

这样操作在代码上实现起来是很简单的,但是连接过程太耗时,连接过多容易出现内存泄漏,对于数据库访问大的项目来说这是十分致命的。

连接池的实现

基于别人的工具

实际上连接池的问题由来已久,因此早有人考虑到连接池的问题并为此写好了工具,在python语言中,提供一个库:sqlalchemy ,这个库可以维护一个连接,让操作者不必担心在项目执行过程中断掉与数据库的联系。

使用案例如下:

1
2
3
4
5
engine = create_engine("mysql://user:password@hostname/dbname?charset=utf8",
echo=True,
pool_size=8,
pool_recycle=60*30
)

自己写一个

如果自己写一个也是可以的,自己写连接池需要注意以下几点:

  • 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
  • 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
  • 最大空闲时间
  • 获取连接超时时间
  • 超时重试连接次数

在网上也有不少人写出来了一些自己的连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import pymysql
from tools.setting import config

DB_CONFIG = config.mysql_info()


class SQLManager(object):

# 初始化实例方法
def __init__(self):
self.conn = None
self.cursor = None
self.connect()

# 连接数据库
def connect(self):
try: # 防止长连接导致挂掉
self.conn.ping()
except:
self.conn = pymysql.connect(
host=DB_CONFIG["HOST"],
port=int(DB_CONFIG["PORT"]),
user=DB_CONFIG["USER"],
passwd=DB_CONFIG["PASSWORD"],
db=DB_CONFIG["DBNAME"],
charset=DB_CONFIG["CHARSET"]
)
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)

# 查询多条数据
def get_list(self, sql, args=None):
self.connect()
self.cursor.execute(sql.encode('utf8'), args)
result = self.cursor.fetchall()
return result

# 查询单条数据
def get_one(self, sql, args=None):
self.connect()
self.cursor.execute(sql.encode('utf8'), args)
result = self.cursor.fetchone()
return result

# 执行单条SQL语句
def moddify(self, sql, args=None):
self.connect()
self.cursor.execute(sql, args)
self.conn.commit()

# 我如果要批量执行多个创建操作,虽然只建立了一次数据库连接但是还是会多次提交,可不可以改成一次连接,
# 一次提交呢?
# 可以,只需要用上pymysql的executemany()
# 方法就可以了。
# 执行多条SQL语句
def multi_modify(self, sql, args=None):
self.connect()
self.cursor.executemany(sql.encode('utf8'), args)
self.conn.commit()

# 创建单条记录的语句
def create(self, sql, args=None):
self.connect()
self.cursor.execute(sql.encode('utf8'), args)
self.conn.commit()
last_id = self.cursor.lastrowid
return last_id

# 关闭数据库cursor和连接
def close(self):
self.connect()
self.cursor.close()
self.conn.close()

# 最后,我们每次操作完数据库之后都要手动关闭,可不可以写成自动关闭的呢?
# 联想到我们之前学过的文件操作,使用with语句可以实现缩进结束自动关闭文件句柄的例子。
# 我们来把我们的数据库连接类SQLManager类再优化下,使其支持with语句操作。
# 进入with语句自动执行
def __enter__(self):
return self

# 退出with语句块自动执行
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()


db = SQLManager()

if __name__ == "__main__":
# info = db.get_list('select user_name from user_table where user_name="张三"')
# time_info=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# sql_str = "INSERT INTO user_table (create_time,update_time,user_name,password) VALUES (now(),now(),'{}',{})"
# db.create(sql_str)
# print(info)
# db.close()
sql_str = "SELECT * FROM articles ORDER BY create_time DESC LIMIT {},2;".format(2*2)
result = db.get_list(sql_str)
print(result)