请选择 进入手机版 | 继续访问电脑版

湖南新梦想

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 10|回复: 0

如何在Python中操作MySQL?(二)

[复制链接]

2775

主题

3174

帖子

1万

积分

论坛元老

Rank: 8Rank: 8

积分
11260
发表于 6 天前 | 显示全部楼层 |阅读模式
            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 PersistentDB
  import 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.3 模式二
  #! /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来连接数据库时,单线程应用完全没有问题,但如果涉及到多线程应用那么就需要加锁,一旦加锁那么连接势必就会排队等待,当请求比较多时,性能就会降低了。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|湖南新梦想 ( 湘ICP备18019834号-2 )

GMT+8, 2022-6-29 14:08 , Processed in 0.039920 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表