MySQL 基于TCP 协议之上的开发,可是网络链接后,传输的数据必须遵循MySQL的协议,封装好MySQL协议的包,就是驱动程序 python
MySQL 的驱动
MySQLDB 最有名的库,对MySQL 的C Client 封装实现,支持python2,不更新了,不支持python3
MySQL 官方的connector
pymysql 语法兼容MySQLdb,使用python写的库,支持python3mysql
本文使用的是mariadb数据库,与MySQL类似sql
grant all(表示全部操做) on .(库.表(全部的,也能够指定)) to root@localhost(设置用户名为root,连接为本地连接) identified by 'roiot123';(设置密码)数据库
flush privileges;(刷新权限)缓存
server.cnf 中的操做(前面必须有空格,不然不生效,不能写入汉字)服务器
client.cnf 操做同上网络
重启加载字符集多线程
如上,则表示加载完毕!!!socket
2 查看安装是否完成,若完成,则不会报错ide
创建链接
获取游标
执行SQL
提交事务
释放资源
导入名重命名重命名
参数含义以下
connection 初始化经常使用参数 | 说明 |
---|---|
host | 主机 |
user | 用户名 |
password | 密码 |
database | 数据库 |
port | 端口 |
其中必选参数是user和passwd 其余可选
其中user 表示数据库的用户名,就是上面初始化的用户名和密码,db 是上面初始化的数据库,host 表示本地连接,可使用IP地址或域名进行远程连接,charset 表示连接使用的字符集,若是和上面的utf8不对应,则可能出现乱码现象
对于数据库的操做有 增insert 删 delete 改 update 等 查 select show 等
建立完成后须要提交,若是不提交则不生效,提交使用的是建立的连接的关键字。
须要关闭连接,首先须要关闭的是游标,其次是连接。
在MySQL数据库中进行查看:
显示与数据库查询类似的结果show 和 select
数据库中的显示
再次建立一个数据表以备查询所用
使用游标.fetchone()表示每次查看一行操做,两个表示两个操做一块儿输出
将其恢复游标到起始位置,能够进行屡次查看,若是没有此配置,默认重上一次查询的下一行开始查询
查看
查看
查看
当%s 没有双引号时:
查看
查看生成结果:
查看是否生成:
pymysql 是第三方模块库,须要安装
pip install pymysql
pymysql.connect() 方法返回的是connections模块下的connection类实例,connect方法传递就是给connection类的__init__提供参数
源码以下
def __init__(self, host=None, user=None, password="", database=None, port=0, unix_socket=None, charset='', sql_mode=None, read_default_file=None, conv=None, use_unicode=None, client_flag=0, cursorclass=Cursor, init_command=None, connect_timeout=10, ssl=None, read_default_group=None, compress=None, named_pipe=None, autocommit=False, db=None, passwd=None, local_infile=False, max_allowed_packet=16*1024*1024, defer_connect=False, auth_plugin_map=None, read_timeout=None, write_timeout=None, bind_address=None, binary_prefix=False, program_name=None, server_public_key=None):
上述初始化参数中autocommit=False,则指的是默认的事务提交是关闭的,及须要手动提交事务
Connection.ping() 方法,测试数据库服务器是否活着,有一个参数热connection表示断开与服务器链接是否重连
def ping(self, reconnect=True): """ Check if the server is alive. :param reconnect: If the connection is closed, reconnect. :raise Error: If the connection is closed and reconnect=False. """ if self._sock is None: if reconnect: self.connect() reconnect = False else: raise err.Error("Already closed") try: self._execute_command(COMMAND.COM_PING, "") self._read_ok_packet() except Exception: if reconnect: self.connect() self.ping(False) else: raise
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) finally: if conn: print('关闭连接:',conn.ping(False)) conn.close()
结果以下
操做数据库,必须使用游标,须要先获取一个游标对象
Connection.cursor(cursor=None) 方法返回一个新的游标对象
链接没有关闭以前,游标对象能够反复使用
cursor参数,能够指定一个Cursor类,若是为None,则使用默认Cursor类
数据库操做须要使用Cursor 类的实例,提供的execute()方法,执行SQL语句,成功返回影响行数。
基本代码以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) # 此处未提交事务。若关闭,则直接致使事务回滚 print (line) cursor.close() finally: if conn: print('关闭连接:',conn.ping(False)) conn.close()
结果以下
数据库结果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) # 此处未提交事务。若关闭,则直接致使事务回滚 print (line) cursor.close() # 若没有异常,则提交事务 conn.commit() except: # 若存在异常,则回滚事务 conn.rollback() finally: if conn: print('关闭连接:',conn.ping(False)) conn.close()
结果以下
数据库结果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() for i in range(5): sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) print (line) # 此处未提交事务。若关闭,则直接致使事务回滚 # 若没有异常,则提交事务 conn.commit() except: # 若存在异常,则回滚事务 conn.rollback() finally: if cursor: cursor.close() if conn: conn.close()
数据库结果以下
变量的方式插入多行数据
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() for i in range(10,15): sql="insert into t(id,username,password) values({},'mysql','mysql')".format(i) line=cursor.execute(sql) # 此处未提交事务。若关闭,则直接致使事务回滚 # 若没有异常,则提交事务 conn.commit() except: # 若存在异常,则回滚事务 conn.rollback() finally: if cursor: cursor.close() if conn: conn.close()
结果以下
cursor类的获取查询结果集的方法有fetchone(),fetchmany(size=None),fetchall() 三种
fetchone()方法,获取结果集的下一行
fetchmany(size=None) 方法,size指定返回的行数的行,None则返回用组
fetchall() 方法,获取全部行
返回多行,若是走到末尾,就返回空元组,不然返回一个元组,其元素就是每一行的记录,每一行的记录都装载在一个元组中。
注意:fetch操做的是结果集,结果集是保存在客户端的,也就是说fetch的时候,查询已经结束了。
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() sql="select * from t" line=cursor.execute(sql) print ('获取一个',cursor.fetchone()) # 获取一个 print ('获取下面两个',cursor.fetchmany(2)) # 获取下面两个 print ('获取全部',cursor.fetchall()) # 获取全部 cursor.rownumber=0 # 游标初始化,支持负索引,当大于len的索引,则会不存在 # 此处未提交事务。若关闭,则直接致使事务回滚 # 若没有异常,则提交事务 print ('获取全部',cursor.fetchall()) # 获取全部 print ('获取一个',cursor.fetchone()) #此处没法获取到了 conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
结果以下
Cursor类有一个Mixin的子类DictCursor
只须要cursor=conn.cursor(DictCursor)就好了
返回多行,放在列表中,元素是字典,表明行,返回是列表
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor(DictCursor) sql="select * from t" line=cursor.execute(sql) print (cursor.fetchall()) # 获取一个 finally: if cursor: cursor.close() if conn: conn.close()
结果以下
猜想后台数据的查询语句使用拼接字符串的方式,从而通过设计为服务端传递参数,令其拼接处特殊的字符串,返回用户想要的结果
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 cursor=conn.cursor() sql="select * from t where id={}".format('5 or 1=1') print (sql) line=cursor.execute(sql) print (cursor.fetchall()) # 获取一个 finally: if cursor: cursor.close() if conn: conn.close()
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 获取游标 name="'test'" passwd="'test' or 1=1" cursor=conn.cursor() sql="select * from t where username={} and password ={}".format(name,passwd) print (sql) line=cursor.execute(sql) print (cursor.fetchall()) # 获取一个 finally: if cursor: cursor.close() if conn: conn.close()
结果以下
参数化查询,能够有效防止SQL注入,并提升查询效率
cursor.execute(query,args=None)
args, 必须是元祖,列表或者字典,若是查询字符串使用%(name)s,就必须使用字典
参数化查询为何能提升效率
缘由就是SQL语句缓存
数据库服务器通常都会对SQL语句编译和缓存,编译只是对SQL部分,因此参数化就算有SQL指令也不会被执行
编译过程,须要词法分析,语法分析,生成AST,优化,生成执行计划等过程,比较耗资源,服务端会先查询是否对同一条语句进行缓存,若是缓存未失效,则不须要再次编译,从而下降了编译的成本,减低了内存消耗。
能够认为SQL语句字符串就是一个key,若是使用拼接方案,每次发过去的SQL语句都不同,都须要从新编译并缓存。
大量查询的时候,首选使用参数化查询,以节省资源。
开发时,应该使用参数化查询
注意:这里说的是查询字符串的缓存,不是查询结果的缓存
>
慢查询通常用不上缓存。
字典参数化查询
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') d={'name':"'test'",'passwd':"'test' or 1=1"} cursor=conn.cursor() sql="select * from t where username=%(name)s and password=%(passwd)s" line=cursor.execute(sql,d) print (cursor.fetchall()) # 获取一个 finally: if cursor: cursor.close() if conn: conn.close()
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') d={'id': '1 or 1=1'} cursor=conn.cursor() sql="select * from t where id=%(id)s" line=cursor.execute(sql,d) print (cursor.fetchall()) # 获取一个 finally: if cursor: cursor.close() if conn: conn.close()
元祖处理
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') cursor=conn.cursor() L1=[ (i,'admin','admin') for i in range(20,23)] for x in L1: sql="insert into t(id,username,password) values(%s,%s,%s)" line=cursor.execute(sql,x) print (line) conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
结果以下
列表以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') cursor=conn.cursor() L1=[ [i,'admin','admin'] for i in range(24,26)] for x in L1: sql="insert into t(id,username,password) values(%s,%s,%s)" line=cursor.execute(sql,x) conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
结果以下
def __enter__(self): """Context manager that returns a Cursor""" warnings.warn( "Context manager API of Connection object is deprecated; Use conn.begin()", DeprecationWarning) return self.cursor() def __exit__(self, exc, value, traceback): """On successful exit, commit. On exception, rollback""" if exc: self.rollback() else: self.commit()
有上述代码可得,连接在使用上下文时会自动返回cursor游标,并在连接关闭时会自动判断执行语句是否出错,若出错,则直接回滚,不然提交,但未定义相关的关闭连接的操做
def __enter__(self): return self def __exit__(self, *exc_info): del exc_info self.close()
有上述可知,游标的上下文返回的是本身,关闭连接时游标会自动关闭连接
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') with conn as cursor: #此处返回一个cursor连接 sql="select * from t" cursor.execute(sql) print (cursor.fetchmany(5)) finally: if cursor: #上述的代码中未使用cursor的上下文,所以此处仍是须要的 cursor.close() if conn: conn.close()
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') with conn as cursor: #此处返回一个cursor连接 with cursor: #此处使用cursor的上下文,默认会关闭其游标 sql="select * from t" cursor.execute(sql) print (cursor.fetchmany(5)) finally: if conn: conn.close()
结果以下
链接不该该随随便便销毁,应该造成重复使用的习惯,应该是多个cursor共享一个链接 。
这里的链接池,指的是数据库的链接池
链接池,是一个容器,里面存放着已经链接到数据库的链接,若是须要链接,使用者能够直接从池中获取一个链接,使用完后归还便可
从而减小频繁的建立,销毁数据库链接的过程,提升了性能。
一个链接池,应该是一个能够设置大小的容器,里面存放着数据库的链接。
使用者须要链接,从池中获取一个链接,用完则须要归还。
面向对象的设计思路,构建一个链接池
构建时,传入链接的服务器相关参数(主机,端口,用户名,密码,库名称),还有提供一个池的容量
考虑多线程的使用,使用者从池中get一个链接,用完后归还该链接便可。
#!/usr/local/bin/python3.6 #coding:utf-8 from pymysql.connections import Connection from threading import local import queue class ConnPool: def __init__(self,size,*args,**kwargs): self.__size=size self.__pool=queue.Queue(size) # 此处用于保存链接 self.local=local() # 此处用于隔离线程间数据,用于对不一样的conn进行区分,具体在线程基础一章全部介绍 for i in range(size): # 此处用于生成链接池,此处是建立链接 conn=Connection(*args,**kwargs) # 初始化链接 self.__pool.put(conn) @property def maxsize(self): return self.__size @property def size(self): return self.__pool.qsize() def __getconn(self): # 此处用于返回conn链接 return self.__pool.get() def __returnconn(self,conn:Connection): # 此处用于归还链接 if isinstance(conn,Connection): # 此处如果一个链接,则可用于返回 self.__pool.put(conn) def __enter__(self): # 此处用于返回一个游标,固然能够返回一个链接 if getattr(self.local,'conn',None) is None: # 若不存在,则进行添加,若存在,则返回 self.local.conn=self.__getconn() return self.local.conn.cursor() def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: self.local.conn.rollback() else: self.local.conn.commit() self.__returnconn(self.local.conn) self.local.conn=None # 此处若重置此连接,则不会致使cursor的不可用,由于cursor中保存着conn的相关信息,但若被删除,则会致使所有出错,del # 语句不可轻易执行 pool=ConnPool(3,'192.168.1.120','root','666666','test') with pool as cursor: with cursor: sql="select * from login" cursor.execute(sql) print (cursor.fetchall()) sql="SHOW PROCESSLIST;" #此处是SQL 自己自带的用于查看进程的命令 cursor.execute(sql) for x in cursor: print (x)
上述的cursor可以被遍历的缘由是其中有iter方法,具体源码以下
def __iter__(self): return iter(self.fetchone, None)
结果以下