(二十七)Flask之数据库连接池DBUtils库

目录:

  • 每篇前言:
  • DBUtils库
    • 模式一(底层使用threading.local实现):
    • 模式二:
  • Flask中使用
    • 方式一:直接将DBUtils初始化放到settings.py文件中
    • 方式二:从utils文件夹中导入
  • 脚本使用DBUtils代码demo:

每篇前言:

  • 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者

  • 🔥🔥本文已收录于Flask框架从入门到实战专栏:《Flask框架从入门到实战》
  • 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
  • 📝​📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
  • 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
  • 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!

在这里插入图片描述

引子:

在上一个demo项目中,登录部分验证是直接写死的,本文模拟实际生产,查询MySQL数据库做验证。

一个很low的方法是:
项目根目录下创建utils/sql.py:

import pymysql


class SQLHelper(object):

    @staticmethod
    def open():
        conn = pymysql.connect(host='127.0.0.1', port=3306, password='123456', db='UserInfo')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        return conn, cursor

    @staticmethod
    def close(conn, cursor):
        conn.commit()
        cursor.close()
        conn.close()

    @classmethod
    def fetch_one(cls, sql, args):
        conn, cursor = cls.open()
        cursor.execute(sql, args)
        obj = cursor.fetchone()
        cls.close(conn, cursor)
        return obj

在这里插入图片描述

上面这种用法很严重且明显的一个问题是: 每次登录一次都要和数据库创建一个连接!

解决方法就是使用DBUtils三方库:

DBUtils库

pip install DBUtils==1.3

DBUtils 是一套用于管理数据库连接池的Python包,为高频度高并发的数据库访问提供更好的性能,可以自动管理连接对象的创建和释放。并允许对非线程安全的数据库接口进行线程安全包装。

这种连接池有两种连接模式:

  1. PersistentDB:提供线程专用的数据库连接,并自动管理连接。

    为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭!

  2. PooledDB:提供线程间可共享的数据库连接,并自动管理连接。

    创建一批连接到连接池,供所有线程共享使用。

    PS:由于pymysql的threadsafety值为1,而DBUtils库用的内部又是用的pymysql,所以该模式连接池中的线程会被所有线程共享。

实测证明 PersistentDB 的速度是最高的(即第一种模式),但是在某些特殊情况下,数据库的连接过程可能异常缓慢,而此时的PooledDB(即模式二,所以推荐这个)则可以提供相对来说平均连接时间比较短的管理方式。

模式一(底层使用threading.local实现):

  • 了解即可~~~
from DBUtils.PersistentDB import PersistentDB
import pymysql

POOL = PersistentDB(
    creator=pymysql,       # 连接数据库的模块
    maxusage=None,         # 一个数据库连接最多被重复使用的次数,None表示无限制
    setsession=[],         # 开始会话前执行的命令列表。如["set time zone ..."]
    ping=0,                # ping MySQL客户端,检查服务是否可用。  【一般用0,4,7】
    # 0 = None = never;  1 = default = whenever if is requested;  2 = when a cursor is created; 4 = when a query is executed;  7 = always
    closeable=False,       # False:conn.close()实际上被忽略,供下次使用,在线程关闭时,才会自动关闭连接;True:conn.close()则关闭连接,再次调用就是一个新的连接了
    threadlocal=None,      # 本线程独享值的对象,用于保存链接对象,如果链接对象被重置,也清除
    host='127.0.0.1',
    port=6379,
    user='root',
    password='123456',
    database='UserInfo',
    charset='utf8'
)

使用:

def demo():
    conn = POOL.connection(shareable=False)
    cursor = conn.cursor()
    cursor.execute('select * from users')
    result = cursor.fetchall()
    cursor.close()  
    conn.close()
    

模式二:

  • 用这个~~~
from DBUtils.PooledDB import PooledDB
import pymysql

POOL = PooledDB(
    creator=pymysql,       # 连接数据库的模块
    maxconnections=6,      # 连接池允许的最大连接数, 0和None表示不限制
    mincached=2,           # 初始化时,连接池中至少创建的空闲的连接,0表示不创建
    maxcached=5,           # 连接池中最多闲置的连接,0和None不限制
    maxshared=3,           # 连接池中最多共享的连接数量,0和None表示全部共享【默认为0,而且哪怕设置别的值也无用!!!下面会将为啥】
    blocking=True,         # 连接池中如果没有可用连接后,是否阻塞等待。True:等待;False:不等待直接报错
    maxusage=None,         # 一个连接最多被重复使用的次数,None表示无限制
    setsession=[],         # 开始会话前执行的命令列表。如["set time zone ..."]
    ping=0,                # ping MySQL客户端,检查服务是否可用。  【一般用0,4,7】
    # 0 = None = never;  1 = default = whenever if is requested;  2 = when a cursor is created; 4 = when a query is executed;  7 = always

    host='127.0.0.1',
    port=6379,
    user='root',
    password='123456',
    database='UserInfo',
    charset='utf8'
)


  • 为什么设置maxshared参数的值是无用的!因为DBUtils使用的pymysql,而pymysql内部threadsafety值为1。看源码:

    import pymysql
    

    在这里插入图片描述

    DBUtils源码:

    from DBUtils.PooledDB import PooledDB
    

    在这里插入图片描述

使用:

def demo():
	"""
	检测当前正在运行连接数是否小于最大连接数;如果不小于则等待或报错:raise TooManyConnection异常
	否则则优先去初始化时创建的连接中获取连接:SteadyDBConnection
	然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
	如果最开始创建的连接没有连接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。
	
	一旦关闭连接后,连接就返回到连接池让后续线程继续使用~
	"""
    conn = POOL.connection()
    
    cursor = conn.cursor()
    cursor.execute('select * from users')
    result = cursor.fetchall()
    conn.close()

Flask中使用

方式一:直接将DBUtils初始化放到settings.py文件中

在这里插入图片描述
在这里插入图片描述

方式二:从utils文件夹中导入

在这里插入图片描述
在这里插入图片描述

脚本使用DBUtils代码demo:

# coding=utf-8
"""
使用DBUtils数据库连接池中的连接,操作数据库
"""
import datetime
import pymysql
from DBUtils.PooledDB import PooledDB


class MysqlClient(object):
    def __init__(self, **kwargs):
        self.pool = self.create_pool(**kwargs)
        self.connection = None
        self.cursor = None

    def create_pool(self, **kwargs):
        return PooledDB(
            pymysql,
            mincached=kwargs.get('mincached', 10),
            maxcached=kwargs.get('maxcached', 20),
            maxshared=kwargs.get('maxshared', 10),
            maxconnections=kwargs.get('maxconnections', 200),
            blocking=kwargs.get('blocking', True),
            maxusage=kwargs.get('maxusage', 100),
            setsession=kwargs.get('setsession', None),
            reset=kwargs.get('reset', True),
            host=kwargs.get('host', '127.0.0.1'),
            port=kwargs.get('port', 3306),
            db=kwargs.get('db', 'mysqldemo'),
            user=kwargs.get('user', 'root'),
            passwd=kwargs.get('passwd', '123456'),
            charset=kwargs.get('charset', 'utf8mb4'),
            cursorclass=pymysql.cursors.DictCursor
        )

    def get_conn_cursor(self):
        self.connection = self.pool.connection()
        self.cursor = self.connection.cursor()

    def close(self):
        try:
            if self.cursor:
                self.cursor.close()
            if self.connection:
                self.connection.close()
        except Exception as e:
            print(e)

    def execute(self, sql, param=()):
        try:
            self.get_conn_cursor()
            count = self.cursor.execute(sql, param)
            print(count)
            return count
        finally:
            self.close()

    def __dict_datetime_obj_to_str(self, result_dict):
        """把字典里面的datetime对象转成字符串,使json转换不出错"""
        if result_dict:
            return {k: v.__str__() if isinstance(v, datetime.datetime) else v for k, v in result_dict.items()}
        return result_dict

    def select_one(self, sql, param=()):
        """查询单个结果"""
        try:
            self.get_conn_cursor()
            count = self.execute(sql, param)
            result = self.cursor.fetchone()
            result = self.__dict_datetime_obj_to_str(result)
            return count, result
        finally:
            self.close()

    def select_many(self, sql, param=()):
        """查询多个结果"""
        try:
            self.get_conn_cursor()
            count = self.execute(sql, param)
            result = self.cursor.fetchall()
            result = [self.__dict_datetime_obj_to_str(row_dict) for row_dict in result]
            return count, result
        finally:
            self.close()

    def begin(self):
        """开启事务"""
        try:
            self.get_conn_cursor()
            self.connection.autocommit(False)
        except Exception as e:
            print(e)

    def end(self, option='commit'):
        """结束事务"""
        try:
            if option == 'commit':
                self.connection.commit()
            else:
                self.connection.rollback()
        except Exception as e:
            print(e)
        finally:
            self.connection.autocommit(True)


if __name__ == "__main__":
    mc = MysqlClient()
    sql1 = 'SELECT * FROM customers  WHERE  customerNumber = 103'
    result1 = mc.select_one(sql1)
    print(result1[1])

    sql2 = 'SELECT * FROM customers  WHERE  customerNumber IN (%s,%s,%s)'
    param = (103, 144, 145)
    print(mc.select_many(sql2, param)[1])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/534348.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

防止公司文件泄密的有效措施有哪些

防止公司文件泄密的有效措施有哪些 公司文件防止泄密是一项涉及多个层面和多种策略的综合性工作。以下列举了多种常用且有效的防止公司文件泄密的方式. 一、政策与流程制定 保密协议(NDA):要求员工、合作伙伴、供应商等签署保密协议&#x…

【计算机基础】期末考试必考,学会就能提高 40 分

二进制与十进制相互转换的详细过程及例题解析 二进制和十进制是两种常用的数制系统。二进制系统仅使用0和1两个数字,而十进制系统则使用0到9的十个数字。在计算机科学和数字电路设计等领域,经常需要在这两种数制之间进行转换。本文将详细介绍二进制与十…

【域适应】基于域分离网络的MNIST数据10分类典型方法实现

关于 大规模数据收集和注释的成本通常使得将机器学习算法应用于新任务或数据集变得异常昂贵。规避这一成本的一种方法是在合成数据上训练模型,其中自动提供注释。尽管它们很有吸引力,但此类模型通常无法从合成图像推广到真实图像,因此需要域…

Springboot项目的测试类书写(速通)

目录 前言1. 单元测试的测试类2. 框架测试的测试类 前言 在实际开发中,如果只是做一个简单的单元测试(不涉及端到端、数据库交互、API调用、消息队列处理等),我为了方便一般都是找块儿地方写一个main方法来跑一下就行了&#xff…

CSS-文字环绕浮动、行内块分页、三角强化妙用、伪元素选择器

文字环绕浮动 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文字环绕浮动效果</title><s…

[leetcode] all-nodes-distance-k-in-binary-tree 二叉树中所有距离为 K 的结点

. - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树&#xff08;具有根结点 root&#xff09;&#xff0c; 一个目标结点 target &#xff0c;和一个整数值 k 。 返回到目标结点 target 距离为 k 的所有结点的值的列表。 答案可以以 任何顺序 返回。 示例 1&#xff1a…

玩转公众号|掌握公众号运营技巧,让账号脱颖而出

随着互联网的普及&#xff0c;微信公众号已经成为了企业进行品牌宣传、产品推广和客户服务的重要渠道。而且&#xff0c;企业微信公众号是可以进行二次开发的&#xff0c;这样就能够满足企业的私域运营的需求。然而&#xff0c;对于许多企业来说&#xff0c;运营公众号和二次开…

LLM 构建Data Multi-Agents 赋能数据分析平台的实践之②:数据治理之二(自动处理)

前述 在前文的multi Agents for Data Analysis的设计说起&#xff0c;本文将继续探索和测试借助llm实现基于私有知识库的数据治理全自动化及智能化。整体设计如下&#xff1a; 整个体系设计了3个Agent以及一个Planer&Execute Agent&#xff0c;第一个Agent用于从企业数据…

结合ArcGIS+SWAT模型+Century模型:流域生态系统水-碳-氮耦合过程模拟

原文链接&#xff1a;结合ArcGISSWAT模型Century模型&#xff1a;流域生态系统水-碳-氮耦合过程模拟https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&tempkeyMTI2NV9sMGRZNUJoVkNVc1ZzSzRuMl9XXzhqX0R3cXpESWFwM1E4cFY4ejNqWFh3VUl0dlZkNWk4b20ydFdFTy1xS2ZObGN0Z0ZXSjly…

大话设计模式——9.单例模式(Singleton Pattern)

简介 确保一个类只有一个实例&#xff0c;并提供全局访问点来获取该实例&#xff0c;是最简单的设计模式。 UML图&#xff1a; 单例模式共有两种创建方式&#xff1a; 饿汉式&#xff08;线程安全&#xff09; 提前创建实例&#xff0c;好处在于该实例全局唯一&#xff0c;不…

c++之旅第九弹——模版

大家好啊&#xff0c;这里是c之旅第九弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一.模版的概念…

改进的注意力机制的yolov8和UCMCTrackerDeepSort的多目标跟踪系统

基于yolov8和UCMCTracker/DeepSort的注意力机制多目标跟踪系统 本项目是一个强大的多目标跟踪系统&#xff0c;基于[yolov8]链接和[UCMCTracker/DeepSot]/链接构建。 &#x1f3af; 功能 多目标跟踪&#xff1a;可以实现对视频中的多目标进行跟踪。目标检测&#xff1a;可以实…

2023年上半年信息系统项目管理师综合知识真题与答案解释(2)

2023年上半年信息系统项目管理师综合知识真题与答案解释(2) And Her Name Is? 她的名字是&#xff1f; During my second month of college, our professor gave us a pop quiz. 在我上大学的第二个月&#xff0c;我们的教授给了我们一个流行测验。 I was a conscientio…

自然语言控制机械臂:ChatGPT与机器人技术的融合创新(上)

引言&#xff1a; 自OpenAI发布ChatGPT以来&#xff0c;世界正迅速朝着更广泛地将AI技术融合到机器人设备中的趋势发展。机械手臂&#xff0c;作为自动化与智能化技术的重要组成部分&#xff0c;在制造业、医疗、服务业等领域的应用日益广泛。随着AI技术的进步&#xff0c;机械…

开源大数据集群部署(二十)Trino部署

作者&#xff1a;櫰木 1 解压trino的包到opt目录 cd /root/bigdata tar -xzvf trino-server-389.tar.gz -C /opt/ ln -s /opt/trino-server-389 /opt/trino2 创建trino用户&#xff0c;并配置专属jdk11 useradd trino su – trino chown -R trino:hadoop /opt/trino-server-…

async+await——用法——基础积累

对于asyncawait&#xff0c;我一直都不太会用。。。。 今天记录一下asyncawait的实际用法&#xff1a; 下面是一个实际的使用场景&#xff1a; 上面的代码如下&#xff1a; async fnConfirmCR(){let type this.crType;let crId this.crId;if(typeof crId object){let ne…

一起学习python——基础篇(13)

前言&#xff0c;python编程语言对于我个人来说学习的目的是为了测试。我主要做的是移动端的开发工作&#xff0c;常见的测试主要分为两块&#xff0c;一块为移动端独立的页面功能&#xff0c;另外一块就是和其他人对接工作。 对接内容主要有硬件通信协议、软件接口文档。而涉…

andorid 矢量图fillColor设置无效

问题&#xff1a;andorid 矢量图fillColor设置无效 解决&#xff1a;去掉如下 android:tint一行

股票手续费怎么降下来?这些技巧帮你省钱!

在股票交易中&#xff0c;手续费是每个投资者都必须面对的成本。降低手续费可以有效地增加投资回报。以下是一些降低股票手续费的方法&#xff1a; 1. 选择低佣金的券商&#xff1a;不同的证券公司提供的佣金费率不同&#xff0c;选择佣金较低的券商可以直接减少交易成本 2. 增…

antd+vue——datepicker日期控件——禁用日期功能

需求&#xff1a;今天之前的日期禁用 <a-date-pickerv-model.trim"formNE.deliveryTime":disabled-date"disabledDate"valueFormat"YYYY-MM-DD"allowClearstyle"width: 100%" />禁用日期的范围&#xff1a; //时间范围 disab…