41 - 数据库-pymysql41 - 数据库-pymysql-DBUtils

2024-04-13 06:32

本文主要是介绍41 - 数据库-pymysql41 - 数据库-pymysql-DBUtils,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

41 - 数据库-pymysql41 - 数据库-pymysql-DBUtils

 

目录

  • 1 Python操作数据库
  • 2 安装模块
  • 3 基本使用
    • 3.1 创建一个连接
    • 3.2 连接数据库
    • 3.3 游标
      • 3.3.1 利用游标操作数据库
      • 3.3.2 事务管理
      • 3.3.3 执行SQL语句
        • 3.3.3.1 批量执行
        • 3.3.3.2 SQL注入攻击
        • 3.3.3.3 参数化查询
    • 3.4 获取查询结果
      • 3.4.1 带列明的查询
    • 3.5 上下文支持
  • 4 DBUtils连接池

 

1 Python操作数据库

        Python 提供了程序的DB-API,支持众多数据库的操作。由于目前使用最多的数据库为MySQL,所以我们这里以Python操作MySQL为例子,同时也因为有成熟的API,所以我们不必去关注使用什么数据,因为操作逻辑和方法是相同的。

2 安装模块

        Python 程序想要操作数据库,首先需要安装 模块 来进行操作,Python 2 中流行的模块为 MySQLdb,而该模块在Python 3 中将被废弃,而使用PyMySQL,这里以PyMySQL模块为例。下面使用pip命令安装PyMSQL模块

pip3 install pymysql

如果没有pip3命令那么需要确认环境变量是否有添加,安装完毕后测试是否安装完毕。

lidaxindeMacBook-Pro:~ DahlHin$ python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymysql
>>>
# 如果没有报错,则表示安装成功

3 基本使用

        首先我们需要手动安装一个MySQL数据库,这里不再赘述,参考博文:http://www.cnblogs.com/dachenzi/articles/7159510.html

连接数据库并执行sql语句的一般流程是:

  1. 建立连接
  2. 获取游标(创建)
  3. 执行SQL语句
  4. 提交事务
  5. 释放资源

对应到代码上的逻辑为:

  1. 导入相应的Python模块
  2. 使用connect函数连接数据库,并返回一个Connection对象
  3. 通过Connection对象的cursor方法,返回一个Cursor对象
  4. 通过Cursor对象的execute方法执行SQL语句
  5. 如果执行的是查询语句,通过Cursor对象的fetchall语句获取返回结果
  6. 调用Cursor对象的close关闭Cursor
  7. 调用Connection对象的close方法关闭数据库连接

3.1 创建一个连接

使用pymysql.connect方法来连接数据库

import pymysqlpymysql.connect(host=None, user=None, password="",database=None, port=0, unix_socket=None,charset=''......)

主要的参数有:

  • host:表示连接的数据库的地址
  • user:表示连接使用的用户
  • password:表示用户对应的密码
  • database:表示连接哪个库
  • port:表示数据库的端口
  • unix_socket:表示使用socket连接时,socket文件的路径
  • charset:表示连接使用的字符集 
  • read_default_file:读取mysql的配置文件中的配置进行连接

3.2 连接数据库

        调用connect函数,将创建一个数据库连接并得到一个Connection对象,Connection对象定义了很多的方法和异常。

host = '10.0.0.13'
port = 3306
user = 'dahl'
password = '123456'
database = 'test'conn = pymysql.connect(host, user, password, database, port)
print(conn)  # <pymysql.connections.Connection object at 0x000001ABD3063550>
conn.ping()  # 没有返回值,无法连接会提示异常

这里conn就是一个Connection对象,它具有一下属性和方法:

  • begin:开始事务
  • commit:提交事务
  • rollback:回滚事务
  • cursor:返回一个Cursor对象
  • autocommit:设置事务是否自动提交
  • set_character_set:设置字符集编码
  • get_server_info:获取数据库版本信息
  • ping(reconnect=True): 测试数据库是否活着,reconnect表示断开与服务器连接后是否重连,连接关闭时抛出异常(一般用来测通断)

        在实际的编程过程中,一般不会直接调用begin、commit和rollback函数,而是通过上下文管理器实现事务的提交与回滚操作

3.3 游标

        游标是系统为用户开设的一个数据缓存区,存放SQL语句执行的结果,用户可以用SQL语句逐一从游标中获取记录,并赋值给变量,交由Python进一步处理。
        在数据库中,游标是一个十分重要的概念。游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标总是与一条SQL 选择语句相关联因为游标由结果集(可以是零条、一条或由相关的选择语句检索出的多条记录)和结果集中指向特定记录的游标位置组成。当决定对结果集进行处理时,必须声明一个指向该结果集的游标。
        正如前面我们使用Python对文件进行处理,那么游标就像我们打开文件所得到的文件句柄一样,只要文件打开成功,该文件句柄就可代表该文件。对于游标而言,其道理是相同的。

3.3.1 利用游标操作数据库

在进行数据库的操作之前需要创建一个游标对象,来执行sql语句。

cursor = conn.cursor()

下面利用游标来执行sql语句:

import pymysqldef connect_mysql():db_config = {'host':'127.0.0.1','port':3306,'user':'root','password':'abc.123','charset':'utf8'}return  pymysql.connect(**db_config)if __name__ == '__main__':conn = connect_mysql()cursor = conn.cursor()    # 创建游标sql = r" select user,host from mysql.user "  # 要执行的sql语句cursor.execute(sql)  # 交给 游标执行result = cursor.fetchall()  # 获取游标执行结果print(result)

3.3.2 事务管理

Connection对象包含如下三个方法用于事务管理:

  • begin:开始事务
  • commit:提交事务
  • rollback:回滚事务
import pymysqldef connect_mysql():db_config = {'host': '10.0.0.13','port': 3306,'user': 'dahl','password': '123456','charset': 'utf8'}return pymysql.connect(**db_config)if __name__ == '__main__':conn = connect_mysql()cursor = conn.cursor()  # 创建游标conn.begin()sql = r"insert into test.student (id,name,age) VALUES (5,'dahl',23)"  # 要执行的sql语句res = cursor.execute(sql)  # 交给 游标执行print(res)conn.commit()

这样使用是极其不安全的,因为sql语句有可能执行失败,当失败时,我们应该进行回滚

if __name__ == '__main__':conn = None cursor = Nonetry:conn = connect_mysql()cursor = conn.cursor()  # 创建游标sql = r"insert into test.student (id,name,age) VALUES (6,'dahl',23)"  # 要执行的sql语句cursor.execute(sql)  # 交给 游标执行conn.commit()    # 提交事务except:conn.rollback()   # 当SQL语句执行失败时,回滚finally:if cursor:   cursor.close()    # 关闭游标if conn:conn.close()   # 关闭连接

3.3.3 执行SQL语句

用于执行SQL语句的两个方法为:

  • cursor.execute(sql):执行一条sql语句
  • executemany(sql,parser):执行多条语句
sql = r"select * from test.student" 
res = cursor.execute(sql)  

3.3.3.1 批量执行

executemany用于批量执行sql语句,

  • sql 为模板,c风格的占位符。
  • parser:为模板填充的数据(可迭代对象)
sql = r"insert into test.student (id,name,age) values (%s,'daxin',20)"
cursor.executemany(sql,(7,8,9,))

3.3.3.2 SQL注入攻击

我们一般在程序中使用sql,可能会有如下代码:

# 接受用户id,然后拼接查询用户信息的SQL语句,然后查询数据库。
userid = 10 # 来源于程序
sql = 'select * from user where user_id = {}'.format(userid)

userid是可变的,比如通过客户端发来的request请求,直接拼接到查询字符串中。如果客户端传递的userid是 '5 or 1='呢

sql = 'select * from test.student where id = {}'.format('5 or 1=1')

此时真正在数据库中查询的sql语句就变成了

select * from test.student where id = 5 or 1=1

这条语句的where条件的函数含义是:当id等于5,或者1等于1时,列出所有匹配的字段,这样就轻松的查到了标准所有的数据!!!

永远不要相信客户端传来的数据是规范及安全的。

3.3.3.3 参数化查询

        cursor.execute(sql)方法还额外提供了一个args参数,用于进行参数化查询,它的类型可以为元组、列表、字典。如果查询字符串使用命名关键字(%(name)s),那么就必须使用字典进行传参。

sql = 'select * from test.student where id = %s'
cursor.execute(sql,args=(2,))sql = 'select * from test.student where id = %(id)s'
cursor.execute(sql,args={'id':2})

        我们说使用参数化查询,效率会高一点,为什么呢?因为SQL语句缓存。数据库一般会对SQL语句编译和缓存,编译只对SQL语句部分,所以参数中就算有SQL指令也不会被执行。编译过程,需要此法分析、语法分析、生成AST、优化、生成执行计划等过程,比较耗费资源。服务端会先查找是否是同一条语句进行了缓存,如果缓存未失效,则不需要再次编译,从而降低了编译的成本,降低了内存消耗。

可以认为SQL语句字符串就是一个key,如果使用拼接方案,每次发过去的SQL语句都不一样,都需要编译并缓存。大量查询的时候,首选使用参数化查询,以节省资源。

3.4 获取查询结果

Cursor类提供了三种查询结果集的方法:(返回值为元组,多个值为嵌套元组)

  • fetchone():获取一条
  • fetchmany(size=None):获取多条,当size为None时,返回一个包含一个元素的嵌套元组
  • fetchall():获取所有
sql = 'select * from test.student'
cursor.execute(sql)
print(cursor.fetchall())
print(cursor.fetchone())  # None
print(cursor.fetchmany(2))  # None

        fetch存在一个指针,fetchone一次,就读取结果集中的一个结果,如果fetchall,那么一次就会读取完所有的结果。可以通过调整这个指针来重复读取

  • cursor.rownumber: 返回当前行号,可以修改,支持 负数
  • cursor.rowcount: 返回总行数
sql = 'select * from test.student'
cursor.execute(sql)
print(cursor.fetchall())  # ((2, 'dahl', 20), (3, 'dahl', 21), (4, 'daxin', 22), (5, 'dahl', 23), (6, 'dahl', 23), (7, 'daxin', 20), (8, 'daxin', 20), (9, 'daxin', 20))
cursor.rownumber = 0
print(cursor.fetchone())  # (2, 'dahl', 20)
print(cursor.fetchmany()) # ((3, 'dahl', 21),)

fetch操作的是结果集,结果集是保存在客户端的,也就是说fetch的时候,查询已经结束了。

3.4.1 带列明的查询

        结果中不包含字段名,除非我们记住字段的顺序,不然很麻烦,那么下面来解决这个问题。

观察cursor原码,我们发现,它接受一个参数cursor,有一个参数是:DictCursor,查看源码得知,它是Cursor的Mixin子类。

def cursor(self, cursor=None):"""Create a new cursor to execute queries with.:param cursor: The type of cursor to create; one of :py:class:`Cursor`,:py:class:`SSCursor`, :py:class:`DictCursor`, or :py:class:`SSDictCursor`.None means use Cursor."""if cursor:return cursor(self)return self.cursorclass(self)

观察DictCursor的原码,得知结果集会返回一个字典,一个字段名:值的结果,所以,只需要传入cursor参数即可

    from pymysql import cursors  # DictCursor存在与cursors模块中... ... cursor = conn.cursor(cursors.DictCursor) sql = 'select * from test.student'cursor.execute(sql)print(cursor.fetchone())  # {'id': 2, 'name': 'dahl', 'age': 20}

3.5 上下文支持

Connection和Cursor类实现了__enter__和__exit__方法,所以它支持上下文管理。

  • Connection:进入时返回一个cursor,退出时如果有异常,则回滚,否则提交
  • Cursor: 进入时返回cursor本身,退出时,关闭cursor连接

所以利用上下文的特性,我们可以这样使用

import pymysqldef connect_mysql():db_config = {'host': '10.0.0.13','port': 3306,'user': 'dahl','password': '123456','charset': 'utf8'}return pymysql.connect(**db_config)if __name__ == '__main__':
conn = connect_mysql()
with conn as cursor:sql = 'select * from student'cursor.execute(sql)print(cursor.fetchmany(2))# conn的exit只是提交了,并没有关闭curosr
cursor.close()
conn.close()

如果要自动关闭cursor,可以进行如下改写

if __name__ == '__main__':
conn = connect_mysql()
with conn as cursor:with cursor   # curosr的exit会关闭cursorsql = 'select * from student'cursor.execute(sql)print(cursor.fetchmany(2))conn.close()

多个 cursor共享一个 conn

4 DBUtils连接池

        在python编程中可以使用MySQLdb/pymysql等模块对数据库的连接及诸如查询/插入/更新等操作,但是每次连接mysql数据库请求时,都是独立的去请求访问,相当浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响。因此,实际使用中,通常会使用数据库的连接池技术,来访问数据库达到资源复用的目的。

dbutils.png?raw=trueuploading.4e448015.gif转存失败重新上传取消db_utils

连接池对性能的提升表现在:

  1. 在程序创建连接的时候,可以从一个空闲的连接中获取,不需要重新初始化连接,提升获取连接的速度
  2. 关闭连接的时候,把连接放回连接池,而不是真正的关闭,所以可以减少频繁地打开和关闭连接

        DBUtils是一套Python数据库连接池包,并允许对非线程安全的数据库接口进行线程安全包装。DBUtils来自Webware for Python。

DBUtils提供两种外部接口:

  • PersistentDB:提供线程专用的数据库连接,并自动管理连接。(为每一个线程创建一个)
  • PooledDB:提供线程间可共享的数据库连接,并自动管理连接。

为每个线程创建一个,资源消耗太大,所以我们常用的是PooledDB.

PooledDB常用的参数为:

  • creator=pymysql: 使用链接数据库的模块
  • maxconnections=6: 连接池允许的最大连接数,0和None表示不限制连接数
  • mincached=2: 初始化时,链接池中至少创建的空闲的链接,0表示不创建,如果空闲连接数小于这个数,pool会创建一个新的连接
  • maxcached=5: 链接池中最多闲置的链接,0和None不限制,如果空闲连接数大于这个数,pool会关闭空闲连接
  • maxshared=3: 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
  • blocking=True: 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
  • maxusage=None: 一个链接最多被重复使用的次数,None表示无限制
  • setsession=[]: 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
  • ping=0: ping MySQL服务端,检查是否服务可用。
    • 0 = None = never
    • 1 = default = whenever it is requested
    • 2 = when a cursor is created
    • 4 = when a query is executed
    • 7 = always
  • host='127.0.0.1',
  • port=3306,
  • user='root',
  • password='123',
  • database='pooldb',
  • charset='utf8'

安装DBUtils

pip install DBUtils

示例代码:

import threading
import time
from DBUtils.PooledDB import PooledDB
import pymysqlPOOL = PooledDB(creator=pymysql,maxconnections=6,mincached=2,maxcached=5,# maxshared=3,blocking=True,maxusage=None,setsession=[],ping=0,host='10.0.0.13',port=3306,user='dahl',password='123456',database='test',charset='utf8'
)def func():conn = POOL.connection()cursor = conn.cursor(pymysql.cursors.DictCursor)time.sleep(2)cursor.execute('select * from employees')res = cursor.fetchall()conn.close()print(threading.get_ident(), res)if __name__ == '__main__':for i in range(10):threading.Thread(target=func).start()

所有巧合的是要么是上天注定要么是一个人偷偷的在努力。

这篇关于41 - 数据库-pymysql41 - 数据库-pymysql-DBUtils的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/899380

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

Python+PyQt5实现MySQL数据库备份神器

《Python+PyQt5实现MySQL数据库备份神器》在数据库管理工作中,定期备份是确保数据安全的重要措施,本文将介绍如何使用Python+PyQt5开发一个高颜值,多功能的MySQL数据库备份工具... 目录概述功能特性核心功能矩阵特色功能界面展示主界面设计动态效果演示使用教程环境准备操作流程代码深度解

MySQL数据库实现批量表分区完整示例

《MySQL数据库实现批量表分区完整示例》通俗地讲表分区是将一大表,根据条件分割成若干个小表,:本文主要介绍MySQL数据库实现批量表分区的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录一、表分区条件二、常规表和分区表的区别三、表分区的创建四、将既有表转换分区表脚本五、批量转换表为分区

MySQL Workbench工具导出导入数据库方式

《MySQLWorkbench工具导出导入数据库方式》:本文主要介绍MySQLWorkbench工具导出导入数据库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录mysql Workbench工具导出导入数据库第一步 www.chinasem.cn数据库导出第二步

Mysql数据库中数据的操作CRUD详解

《Mysql数据库中数据的操作CRUD详解》:本文主要介绍Mysql数据库中数据的操作(CRUD),详细描述对Mysql数据库中数据的操作(CRUD),包括插入、修改、删除数据,还有查询数据,包括... 目录一、插入数据(insert)1.插入数据的语法2.注意事项二、修改数据(update)1.语法2.有

查看MySQL数据库版本的四种方法

《查看MySQL数据库版本的四种方法》查看MySQL数据库的版本信息可以通过多种方法实现,包括使用命令行工具、SQL查询语句和图形化管理工具等,以下是详细的步骤和示例代码,需要的朋友可以参考下... 目录方法一:使用命令行工具1. 使用 mysql 命令示例:方法二:使用 mysqladmin 命令示例:方