如何在Python中操作MySQL?

一. python操作数据库介绍

Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。Python 数据库接口支持非常多的数据库,你可以选择适合你项目的数据库:

  • GadFly
  • mSQL
  • MySQL
  • PostgreSQL
  • Microsoft SQL Server 2000
  • Informix
  • Interbase
  • Oracle
  • Sybase...

你可以访问Python数据库接口及API查看详细的支持数据库列表。

不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。

DB-API 是一个规范. 它定义了一系列必须的对象和数据库存取方式, 以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口 。

Python的DB-API,为大多数的数据库实现了接口,使用它连接各数据库后,就可以用相同的方式操作各数据库。

Python DB-API使用流程:

  • 引入 API 模块。
  • 获取与数据库的连接。
  • 执行SQL语句和存储过程。
  • 关闭数据库连接。

二. python操作MySQL模块

Python操作MySQL主要使用两种方式:

  1. DB模块(原生SQL)
    • PyMySQL(支持python2.x/3.x)
    • MySQLdb(目前仅支持python2.x)
  2. ORM框架
    • SQLAchemy

2.1 PyMySQL模块

本文主要介绍PyMySQL模块,MySQLdb使用方式类似

2.1.1 安装PyMySQL

PyMySQL是一个Python编写的MySQL驱动程序,让我们可以用Python语言操作MySQL数据库。

pip install PyMySQL

2.2 基本使用

#! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = 'shuke'# Date: 2018/5/13

import pymysql

# 创建连接conn = pymysql.connect(host='127.0.0.1', port=3306, user='zff', passwd='zff123', db='zff', charset='utf8mb4')

# 创建游标(查询数据返回为元组格式)# cursor = conn.cursor()

# 创建游标(查询数据返回为字典格式)cursor = conn.cursor(pymysql.cursors.DictCursor)

# 1. 执行SQL,返回受影响的行数effect_row1 = cursor.execute('select * from USER')

# 2. 执行SQL,返回受影响的行数,一次插入多行数据effect_row2 = cursor.executemany('insert into USER (NAME) values(%s)', [('jack'), ('boom'), ('lucy')])  # 3

# 查询所有数据,返回数据为元组格式result = cursor.fetchall()

# 增/删/改均需要进行commit提交,进行保存conn.commit()

# 关闭游标cursor.close()

# 关闭连接conn.close()

print(result)'''[{'id': 6, 'name': 'boom'}, {'id': 5, 'name': 'jack'}, {'id': 7, 'name': 'lucy'}, {'id': 4, 'name': 'tome'}, {'id': 3, 'name': 'zff'}, {'id': 1, 'name': 'zhaofengfeng'}, {'id': 2, 'name': 'zhaofengfeng02'}]'''

2.3 获取最新创建的数据自增ID

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = 'shuke'
# Date: 2018/5/13

import pymysql

# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='zff', passwd='zff123', db='zff', charset='utf8mb4')

# 创建游标(查询数据返回为元组格式)
cursor = conn.cursor()

# 获取新创建数据自增ID
effect_row = cursor.executemany('insert into USER (NAME)values(%s)', [('eric')])

# 增删改均需要进行commit提交
conn.commit()

# 关闭游标
cursor.close()

# 关闭连接
conn.close()

new_id = cursor.lastrowid
print(new_id)
'''
8
'''

2.4 查询操作

#! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = 'shuke'# Date: 2018/5/13

import pymysql

# 创建连接conn = pymysql.connect(host='127.0.0.1', port=3306, user='zff', passwd='zff123', db='zff', charset='utf8mb4')

# 创建游标cursor = conn.cursor()

cursor.execute('select * from USER')

# 获取第一行数据row_1 = cursor.fetchone()

# 获取前n行数据row_2 = cursor.fetchmany(3)## # 获取所有数据row_3 = cursor.fetchall()

# 关闭游标cursor.close()

# 关闭连接conn.close()print(row_1)print(row_2)print(row_3)

⚠️ 在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:

  • cursor.scroll(1,mode='relative')  # 相对当前位置移动
  • cursor.scroll(2,mode='absolute')  # 相对绝对位置移动

2.5 防止SQL注入

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = 'shuke'
# Date: 2018/5/13

import pymysql

# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='zff', passwd='zff123', db='zff', charset='utf8mb4')

# 创建游标
cursor = conn.cursor()

# 存在sql注入情况(不要用格式化字符串的方式拼接SQL)
sql = 'insert into USER (NAME) values('%s')' % ('zhangsan',)
effect_row = cursor.execute(sql)

# 正确方式一
# execute函数接受一个元组/列表作为SQL参数,元素个数只能有1个
sql = 'insert into USER (NAME) values(%s)'
effect_row1 = cursor.execute(sql, ['wang6'])
effect_row2 = cursor.execute(sql, ('wang7',))

# 正确方式二
sql = 'insert into USER (NAME) values(%(name)s)'
effect_row1 = cursor.execute(sql, {'name': 'wudalang'})

# 写入插入多行数据
effect_row2 = cursor.executemany('insert into USER (NAME) values(%s)', [('ermazi'), ('dianxiaoer')])

# 提交
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()

这样,SQL操作就更安全了。如果需要更详细的文档参考PyMySQL文档吧。不过好像这些SQL数据库的实现还不太一样,PyMySQL的参数占位符使用%s这样的C格式化符,而Python自带的sqlite3模块的占位符好像是问号(?)。因此在使用其他数据库的时候还是仔细阅读文档吧。Welcome to PyMySQL’s documentation

三. 数据库连接池

上文中的方式存在一个问题,单线程情况下可以满足,程序需要频繁的创建释放连接来完成对数据库的操作,那么,我们的程序/脚本在多线程情况下会引发什么问题呢?此时,我们就需要使用数据库连接池来解决这个问题!

3.1 DBUtils模块

DBUtils是Python的一个用于实现数据库连接池的模块。

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

  • 为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接才会自动关闭
  • 创建一批连接到连接池,供所有线程共享使用(推荐使用)

3.2 模式一

#! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = 'shuke'# Date: 2018/5/13

from DBUtils.PersistentDB import PersistentDBimport pymysql

POOL = PersistentDB(    creator=pymysql,  # 使用链接数据库的模块    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    closeable=False,    # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)    threadlocal=None,  # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置    host='127.0.0.1',    port=3306,    user='zff',    password='zff123',    database='zff',    charset='utf8',)

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

result = func()print(result)

3.2 模式二

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = 'shuke'
# Date: 2018/5/13

import time
import pymysql
import threading
from DBUtils.PooledDB import PooledDB, SharedDBConnection

POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    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='zff',
    password='zff123',
    database='zff',
    charset='utf8'
)

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

# print('连接被拿走了', conn._con)
    # print('池子里目前有', POOL._idle_cache, '\r\n')

cursor = conn.cursor()
    cursor.execute('select * from USER')
    result = cursor.fetchall()
    conn.close()
    return result

result = func()
print(result)

⚠️ 由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享,因此是线程安全的。如果没有连接池,使用pymysql来连接数据库时,单线程应用完全没有问题,但如果涉及到多线程应用那么就需要加锁,一旦加锁那么连接势必就会排队等待,当请求比较多时,性能就会降低了。

3.3 加锁

#! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = 'shuke'# Date: 2018/5/13

import pymysqlimport threadingfrom threading import RLock

LOCK = RLock()CONN = pymysql.connect(host='127.0.0.1',                       port=3306,                       user='zff',                       password='zff123',                       database='zff',                       charset='utf8')

def task(arg):    with LOCK:        cursor = CONN.cursor()        cursor.execute('select * from USER ')        result = cursor.fetchall()        cursor.close()

        print(result)

for i in range(10):    t = threading.Thread(target=task, args=(i,))    t.start()

3.4 无锁(报错)

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = 'shuke'
# Date: 2018/5/13

import pymysql
import threading

CONN = pymysql.connect(host='127.0.0.1',
                       port=3306,
                       user='zff',
                       password='zff123',
                       database='zff',
                       charset='utf8')

def task(arg):
    cursor = CONN.cursor()
    cursor.execute('select * from USER ')
    # cursor.execute('select sleep(10)')
    result = cursor.fetchall()
    cursor.close()
    print(result)

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()

此时可以在数据库中查看连接情况: show status like 'Threads%';

四. 数据库连接池结合pymsql使用

# cat sql_helper.py

import pymysqlimport threadingfrom DBUtils.PooledDB import PooledDB, SharedDBConnectionPOOL = PooledDB(    creator=pymysql,  # 使用链接数据库的模块    maxconnections=20,  # 连接池允许的最大连接数,0和None表示不限制连接数    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制    #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='192.168.11.38',    port=3306,    user='root',    passwd='apNXgF6RDitFtDQx',    db='m2day03db',    charset='utf8')

def connect():    # 创建连接    # conn = pymysql.connect(host='192.168.11.38', port=3306, user='root', passwd='apNXgF6RDitFtDQx', db='m2day03db')    conn = POOL.connection()    # 创建游标    cursor = conn.cursor(pymysql.cursors.DictCursor)

    return conn,cursor

def close(conn,cursor):    # 关闭游标    cursor.close()    # 关闭连接    conn.close()

def fetch_one(sql,args):    conn,cursor = connect()    # 执行SQL,并返回收影响行数    effect_row = cursor.execute(sql,args)    result = cursor.fetchone()    close(conn,cursor)

    return result

def fetch_all(sql,args):    conn, cursor = connect()

    # 执行SQL,并返回收影响行数    cursor.execute(sql,args)    result = cursor.fetchall()

    close(conn, cursor)    return result

def insert(sql,args):    '''    创建数据    :param sql: 含有占位符的SQL    :return:    '''    conn, cursor = connect()

    # 执行SQL,并返回收影响行数    effect_row = cursor.execute(sql,args)    conn.commit()

    close(conn, cursor)

def delete(sql,args):    '''    创建数据    :param sql: 含有占位符的SQL    :return:    '''    conn, cursor = connect()

    # 执行SQL,并返回收影响行数    effect_row = cursor.execute(sql,args)

    conn.commit()

    close(conn, cursor)

    return effect_row

def update(sql,args):    conn, cursor = connect()

    # 执行SQL,并返回收影响行数    effect_row = cursor.execute(sql, args)

    conn.commit()

    close(conn, cursor)

    return effect_row

PS: 可以利用静态方法封装到一个类中,方便使用

——End——
(0)

相关推荐

  • 数据库连接和数据库池

    Pymysql 安装   pip install PyMySQL # -*- coding: utf-8 -*-   import pymysql   conn = pymysql.connect(h ...

  • Python Pymysql实现数据存储

    Python连接并操作MySQL数据库,主要通过Pymysql模块实现.本节讲解如何将抓取的数据存储至MySQL数据库. 创建存储数据表 首先您应该确定您的计算机上已经安装了MySQL数据库,然后再进 ...

  • 几种Python 数据读写方式,面向Txt、csv文档及MongoDB、MySQL等数据库

    几种Python 数据读写方式,面向Txt、csv文档及MongoDB、MySQL等数据库

  • 如何在Python中操作MySQL~

    一. python操作数据库介绍 Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口.Python 数据库接口支持非常多的数据库, ...

  • 如何在Python中操作日期和时间

    编写Python程序,处理日期和时间经常会遇到,幸好Python本身集成了很多日期.时间相关的模块,让这件繁琐的工作变的方便.本文介绍Python中功能强大的datetime模块. datetime模 ...

  • 高手如何在Python中使用collections模块

    来源:AI入门学习 介   绍 Python 3有许多内置的数据结构,包括元组.字典和列表.数据结构为我们提供了一种组织和存储数据的方法.collections模块能够帮助我们高效地填充和操作数据结构 ...

  • 如何在 Python 中使用断点调试

    实际上没人能一次就写出完美的代码,除了我.但是世界上只有一个我. -- 林纳斯·托瓦兹(Linux 之父) 既然不是神,写代码自然免不了要修改.修改代码的过程被称作调试,又叫 debug. 刚接触编程 ...

  • 如何在win10中安装MySQL(免)

    首先进入mysql官网,网址:https://www.mysql.com/, 之后点击上方DOWNLOADS进入下面这个界面, 找到最下方的MySQL Community (GPL) Download ...

  • 如何在python中执行另一个py文件

      更新时间:2020年04月30日 10:24:22   作者:杰鑫哥   这篇文章主要介绍了如何在python中执行另一个py文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参 ...

  • 【Excel教程】如何在Excel中按月汇总数据,公式和操作都告诉你!(文末赠书)

    公众号回复2016   下载office2016 对于使用Excel处理数据的伙伴们来说,各种统计汇总工作是日常必须面对的问题,关于汇总的函数,我们也学习了不少,求和用SUM,单条件求和用SUMIF, ...

  • Python骚操作,提取pdf文件中的表格数据!

    在实际研究中,我们经常需要获取大量数据,而这些数据很大一部分以pdf表格的形式呈现,如公司年报.发行上市公告等.面对如此多的数据表格,采用手工复制黏贴的方式显然并不可取.那么如何才能高效提取出pdf文 ...

  • 如何用Python中Tushare包轻松完成股票筛选(详细流程操作)

    如何用Python中Tushare包轻松完成股票筛选(详细流程操作) 本文包括安装以及调用Tushare包的详细流程操作 一.Tushare简介 Tushare是Python中一个十分好用的免费调用股 ...