python D40 pymsql和navicat

一 IDE工具介绍(Navicat)

  生产环境仍是推荐使用mysql命令行,但为了方便咱们测试,可使用IDE工具,咱们使用Navicat工具,这个工具本质上就是一个socket客户端,可视化的链接mysql服务端的一个工具,而且他是图形界面版的。咱们使用它和直接使用命令行的区别就相似linux和windows系统操做起来的一个区别。html

二 MySQL数据备份

#1. 物理备份: 直接复制数据库文件,适用于大型数据库环境。但不能恢复到异构系统中如Windows。
#2. 逻辑备份: 备份的是建表、建库、插入等操做所执行SQL语句,适用于中小型数据库,效率相对较低。
#3. 导出表: 将表导入到文本文件中。 

 

  1、使用mysqldump实现逻辑备份python

复制代码
#语法:
# mysqldump -h 服务器 -u用户名 -p密码 数据库名 > 备份文件.sql

#示例:
#单库备份
mysqldump -uroot -p123 db1 > db1.sql
mysqldump -uroot -p123 db1 table1 table2 > db1-table1-table2.sql

#多库备份
mysqldump -uroot -p123 --databases db1 db2 mysql db3 > db1_db2_mysql_db3.sql

#备份全部库
mysqldump -uroot -p123 --all-databases > all.sql 
复制代码

 

 

 

    2、恢复逻辑备份mysql

复制代码
#方法一:
[root@localhost backup]# mysql -uroot -p123 < /backup/all.sql

#方法二:
mysql> use db1;
mysql> SET SQL_LOG_BIN=0;
mysql> source /root/db1.sql

#注:若是备份/恢复单个库时,能够修改sql文件
DROP database if exists school;
create database school;
use school; 
复制代码

 

  3、备份/恢复案例linux

复制代码
#数据库备份/恢复实验一:数据库损坏
备份:
1. # mysqldump -uroot -p123 --all-databases > /backup/`date +%F`_all.sql
2. # mysql -uroot -p123 -e 'flush logs' //截断并产生新的binlog
3. 插入数据 //模拟服务器正常运行
4. mysql> set sql_log_bin=0; //模拟服务器损坏
mysql> drop database db;

恢复:
1. # mysqlbinlog 最后一个binlog > /backup/last_bin.log
2. mysql> set sql_log_bin=0; 
mysql> source /backup/2014-02-13_all.sql //恢复最近一次彻底备份 
mysql> source /backup/last_bin.log //恢复最后个binlog文件


#数据库备份/恢复实验二:若是有误删除
备份:
1. mysqldump -uroot -p123 --all-databases > /backup/`date +%F`_all.sql
2. mysql -uroot -p123 -e 'flush logs' //截断并产生新的binlog
3. 插入数据 //模拟服务器正常运行
4. drop table db1.t1 //模拟误删除
5. 插入数据 //模拟服务器正常运行

恢复:
1. # mysqlbinlog 最后一个binlog --stop-position=260 > /tmp/1.sql 
# mysqlbinlog 最后一个binlog --start-position=900 > /tmp/2.sql 
2. mysql> set sql_log_bin=0; 
mysql> source /backup/2014-02-13_all.sql //恢复最近一次彻底备份
mysql> source /tmp/1.log //恢复最后个binlog文件
mysql> source /tmp/2.log //恢复最后个binlog文件

注意事项:
1. 彻底恢复到一个干净的环境(例如新的数据库或删除原有的数据库)
2. 恢复期间全部SQL语句不该该记录到binlog中
复制代码

 

  4、实现自动化备份sql

复制代码
备份计划:
1. 什么时间 2:00
2. 对哪些数据库备份
3. 备份文件放的位置

备份脚本:
[root@localhost~]# vim /mysql_back.sql
#!/bin/bash
back_dir=/backup
back_file=`date +%F`_all.sql
user=root
pass=123

if [ ! -d /backup ];then
mkdir -p /backup
fi

# 备份并截断日志
mysqldump -u${user} -p${pass} --events --all-databases > ${back_dir}/${back_file}
mysql -u${user} -p${pass} -e 'flush logs'

# 只保留最近一周的备份
cd $back_dir
find . -mtime +7 -exec rm -rf {} \;

手动测试:
[root@localhost ~]# chmod a+x /mysql_back.sql 
[root@localhost ~]# chattr +i /mysql_back.sql
[root@localhost ~]# /mysql_back.sql

配置cron:
[root@localhost ~]# crontab -l
2 * * * /mysql_back.sql
复制代码

 

  5、表的导出和导入数据库

复制代码
SELECT... INTO OUTFILE 导出文本文件
示例:
mysql> SELECT * FROM school.student1
INTO OUTFILE 'student1.txt'
FIELDS TERMINATED BY ',' //定义字段分隔符
OPTIONALLY ENCLOSED BY '”' //定义字符串使用什么符号括起来
LINES TERMINATED BY '\n' ; //定义换行符


mysql 命令导出文本文件
示例:
# mysql -u root -p123 -e 'select * from student1.school' > /tmp/student1.txt
# mysql -u root -p123 --xml -e 'select * from student1.school' > /tmp/student1.xml
# mysql -u root -p123 --html -e 'select * from student1.school' > /tmp/student1.html

LOAD DATA INFILE 导入文本文件
mysql> DELETE FROM student1;
mysql> LOAD DATA INFILE '/tmp/student1.txt'
INTO TABLE school.student1
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '”'
LINES TERMINATED BY '\n';
复制代码

 

 

复制代码
#可能会报错
mysql> select * from db1.emp into outfile 'C:\\db1.emp.txt' fields terminated by ',' lines terminated by '\r\n';
ERROR 1238 (HY000): Variable 'secure_file_priv' is a read only variable


#数据库最关键的是数据,一旦数据库权限泄露,那么经过上述语句就能够轻松将数据导出到文件中而后下载拿走,于是mysql对此做了限制,只能将文件导出到指定目录
在配置文件中
[mysqld]
secure_file_priv='C:\\' #只能将数据导出到C:\\下

重启mysql
从新执行上述语句
复制代码

 

  6、数据库迁移vim

务必保证在相同版本之间迁移
# mysqldump -h 源IP -uroot -p123 --databases db1 | mysql -h 目标IP -uroot -p456

 

三 pymysql模块

  咱们要学的pymysql就是用来在python程序中如何操做mysql,它和mysql自带的那个客户端还有navicat是同样的,本质上就是一个套接字客户端,只不过这个套接字客户端是在python程序中用的,既然是客户端套接字,应该怎么用,是否是要链接服务端,而且和服务端进行通讯啊,让咱们来学习一下pymysql这个模块windows

 

#安装
pip3 install pymysql

 

  一 连接、执行sql、关闭(游标)bash

    

复制代码
import pymysql
user=input('用户名: ').strip()
pwd=input('密码: ').strip()

#连接,指定ip地址和端口,本机上测试时ip地址能够写localhost或者本身的ip地址或者127.0.0.1,而后你操做数据库的时候的用户名,密码,要指定你操做的是哪一个数据库,指定库名,还要指定字符集。否则会出现乱码
conn=pymysql.connect(host='localhost',port=3306,user='root',password='123',database='student',charset='utf8') #指定编码为utf8的时候,注意没有-,别写utf-8,数据库为
#获得conn这个链接对象 #游标 cursor=conn.cursor() #这就想到于mysql自带的那个客户端的游标mysql> 在这后面输入指令,回车执行 #cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) #获取字典数据类型表示的结果:{'sid': 1, 'gender': '男', 'class_id': 1, 'sname': '理解'} {'字段名':值} #而后给游标输入sql语句并执行sql语句execute sql='select * from userinfo where name="%s" and password="%s"' %(user,pwd) #注意%s须要加引号,执行这句sql的前提是医药有个userinfo表,里面有name和password两个字段,还有一些数据,本身添加数据昂 print(sql) res=cursor.execute(sql) #执行sql语句,返回sql查询成功的记录数目,是个数字,是受sql语句影响到的记录行数,其实除了受影响的记录的条数以外,这些记录的数据也都返回了给游标,这个就至关于咱们subprocess模块里面的管道PIPE,乘放着返回的数据
#all_data=cursor.fetchall() #获取返回的全部数据,注意凡是取数据,取过的数据就没有了,结果都是元祖格式的
#many_data=cursor.fetchmany(3) #一下取出3条数据,
#one_data=cursor.fetchone() #按照数据的顺序,一次只拿一个数据,下次再去就从第二个取了,由于第一个被取出去了,取一次就没有了,结果也都是元祖格式的

  fetchone:(1, '男', 1, '理解')
  fetchone:(2, '女', 1, '钢蛋')
  fetchall:((3, '男', 1, '张三'), (4, '男', 1, '张一'))服务器

#上面fetch的结果都是元祖格式的,无法看出哪一个数据是对应的哪一个字段,这样是否是不太好看,想想,咱们能够经过python的哪种数据类型,能把字段和对应的数据表示出来最清晰,固然是字典{'字段名':值}
#咱们能够再建立游标的时候,在cursor里面加上一个参数:cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)获取的结果就是字典格式的,fetchall或者fetchmany取出的结果是列表套字典的数据形式

上面咱们说,咱们的数据取一次是否是就没有了啊,实际上不是的,这个取数据的操做就像读取文件内容同样,每次read以后,光标就移动到了对应的位置,咱们能够经过seek来移动光标
一样,咱们能够移动游标的位置,继续取咱们前面的数据,经过cursor.scroll(数字,模式),第一个参数就是一个int类型的数字,表示日后移动的记录条数,第二个参数为移动的模式,有两个值:absolute:绝对移动,relative:相对移动
#绝对移动:它是相对于全部数据的起始位置开始日后面移动的
#相对移动:他是相对于游标的当前位置开始日后移动的

#绝对移动的演示
#print(cursor.fetchall())
#cursor.scroll(3,'absolute') #从初始位置日后移动三条,那么下次取出的数据为第四条数据
#print(cursor.fetchone())

#相对移动的演示
#print(cursor.fetchone())
#cursor.scroll(1,'relative') #经过上面取了一次数据,游标的位置在第二条的开头,我如今相对移动了1个记录,那么下次再取,取出的是第三条,我相对于上一条,往下移动了一条
#print(cursor.fetchone())
print(res) #一个数字 cursor.close() #关闭游标 conn.close() #关闭链接 if res: print('登陆成功') else: print('登陆失败')
复制代码

 

  

 

 

  二 execute()之sql注入

    以前咱们进行用户名密码认证是先将用户名和密码保存到一个文件中,而后经过读文件里面的内容,来和客户端发送过来的用户名密码进行匹配,如今咱们学了数据库,咱们能够将这些用户数据保存到数据库中,而后经过数据库里面的数据来对客户端进行用户名和密码的认证。

    自行建立一个用户信息表userinfo,里面包含两个字段,username和password,而后里面写两条记录

    

    

复制代码
#咱们来使用数据来进行一下用户名和密码的认证操做
import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='666', database='crm', charset='utf8' ) cursor = conn.cursor(pymysql.cursors.DictCursor) uname = input('请输入用户名:') pword = input('请输入密码:') sql = "select * from userinfo where username='%s' and password='%s';"%(uname,pword) res = cursor.execute(sql) #res咱们说是获得的行数,若是这个行数不为零,说明用户输入的用户名和密码存在,若是为0说名存在,你想一想对不 print(res) #若是输入的用户名和密码错误,这个结果为0,若是正确,这个结果为1 if res: print('登录成功') else: print('用户名和密码错误!') #经过上面的验证方式,比咱们使用文件来保存用户名和密码信息的来进行验证操做要方便不少。
复制代码

 

    

    可是咱们来看下面的操做,若是将在输入用户名的地方输入一个 chao'空格而后--空格而后加上任意的字符串,就可以登录成功,也就是只知道用户名的状况下,他就能登录成功的状况:

复制代码
uname = input('请输入用户名:')
pword = input('请输入密码:')

sql = "select * from userinfo where username='%s' and password='%s';"%(uname,pword)
print(sql)
res = cursor.execute(sql) #res咱们说是获得的行数,若是这个行数不为零,说明用户输入的用户名和密码存在,若是为0说名存在,你想一想对不

print(res) #若是输入的用户名和密码错误,这个结果为0,若是正确,这个结果为1
if res:
    print('登录成功')
else:
    print('用户名和密码错误!')
#运行看结果:竟然登录成功
请输入用户名:chao' -- xxx
请输入密码:
select * from userinfo where username='chao' -- xxx' and password='';
1
登录成功

咱们来分析一下:
此时uname这个变量等于什么,等于chao' -- xxx,而后咱们来看咱们的sql语句被这个字符串替换以后是个什么样子:
select * from userinfo where username='chao' -- xxx' and password=''; 其中chao后面的这个',在进行字符串替换的时候,咱们输入的是chao',这个引号和前面的引号组成了一对,而后后面--在sql语句里面是注释的意思,也就是说--后面的sql语句被注释掉了。也就是说,拿到的sql语句是select * from userinfo where username='chao';而后就去本身的数据库里面去执行了,发现可以找到对应的记录,由于有用户名为chao的记录,而后他就登录成功了,可是其实他连密码都不知道,只知道个用户名。。。,他完美的跳过了你的认证环节。
复制代码

 

    而后咱们再来看一个例子,直接连用户名和密码都不知道,可是依然可以登录成功的状况:

复制代码
请输入用户名:xxx' or 1=1 -- xxxxxx
请输入密码:
select * from userinfo where username='xxx' or 1=1 -- xxxxxx' and password='';
3
登录成功

咱们只输入了一个xxx' 加or 加 1=1 加 -- 加任意字符串
看上面被执行的sql语句你就发现了,or 后面跟了一个永远为真的条件,那么即使是username对不上,可是or后面的条件是成立的,也可以登录成功。
复制代码

    上面两个例子就是两个sql注入的问题,看完上面这两个例子,有没有感受后背发凉啊同志们,别急,咱们来解决一下这个问题,怎么解决呢?

      有些网站直接在你输入内容的时候,是否是就给你限定了,你不能输入一些特殊的符号,由于有些特殊符号能够改变sql的执行逻辑,其实不光是--,还有一些其余的符号也能改变sql语句的执行逻辑,这个方案咱们是在客户端给用户输入的地方进行限制,可是别人可不能够模拟你的客户端来发送请求,是能够的,他模拟一个客户端,不按照你的客户端的要求来,就发一些特殊字符,你的客户端是限制不了的。因此单纯的在客户端进行这个特殊字符的过滤是不能解决根本问题的,那怎么办?咱们服务端也须要进行验证,能够经过正则来将客户端发送过来的内容进行特殊字符的匹配,若是有这些特殊字符,咱们就让它登录失败。

    在服务端来解决sql注入的问题:不要本身来进行sql字符串的拼接了,pymysql能帮咱们拼接,他可以防止sql注入,因此之后咱们再写sql语句的时候按下面的方式写:

复制代码
以前咱们的sql语句是这样写的:
sql = "select * from userinfo where username='%s' and password='%s';"%(uname,pword)

之后再写的时候,sql语句里面的%s左右的引号去掉,而且语句后面的%(uname,pword)这些内容也不要本身写了,按照下面的方式写
sql = "select * from userinfo where username=%s and password=%s;"
难道咱们不传值了吗,不是的,咱们经过下面的形式,在excute里面写参数:
#cursor.execute(sql,[uname,pword]) ,其实它本质也是帮你进行了字符串的替换,只不过它会将uname和pword里面的特殊字符给过滤掉。

看下面的例子:
uname = input('请输入用户名:') #输入的内容是:chao' -- xxx或者xxx' or 1=1 -- xxxxx
pword = input('请输入密码:')

sql = "select * from userinfo where username=%s and password=%s;"
print(sql)
res = cursor.execute(sql,[uname,pword]) #res咱们说是获得的行数,若是这个行数不为零,说明用户输入的用户名和密码存在,若是为0说名存在,你想一想对不

print(res) #若是输入的用户名和密码错误,这个结果为0,若是正确,这个结果为1
if res:
    print('登录成功')
else:
    print('用户名和密码错误!')
#看结果:
请输入用户名:xxx' or 1=1 -- xxxxx
请输入密码:
select * from userinfo where username=%s and password=%s;
0
用户名和密码错误!
复制代码

 

  经过pymysql提供的excute完美的解决了问题。

总结我们刚才说的两种sql注入的语句
#一、sql注入之:用户存在,绕过密码 chao' -- 任意字符 #二、sql注入之:用户不存在,绕过用户与密码 xxx' or 1=1 -- 任意字符

    解决方法总结:

复制代码
复制代码
# 原来是咱们对sql进行字符串拼接
# sql="select * from userinfo where name='%s' and password='%s'" %(user,pwd)
# print(sql)
# res=cursor.execute(sql)

#改写为(execute帮咱们作字符串拼接,咱们无需且必定不能再为%s加引号了)
sql="select * from userinfo where name=%s and password=%s" #!!!注意%s须要去掉引号,由于pymysql会自动为咱们加上
res=cursor.execute(sql,[user,pwd]) #pymysql模块自动帮咱们解决sql注入的问题,只要咱们按照pymysql的规矩来。
复制代码
复制代码

  三 增、删、改:conn.commit()

    查操做在上面已经说完了,咱们来看一下增删改,也要注意,sql语句不要本身拼接,交给excute来拼接

复制代码
复制代码
import pymysql
#连接
conn=pymysql.connect(host='localhost',port='3306',user='root',password='123',database='crm',charset='utf8')
#游标
cursor=conn.cursor()

#执行sql语句
#part1
# sql='insert into userinfo(name,password) values("root","123456");'
# res=cursor.execute(sql) #执行sql语句,返回sql影响成功的行数
# print(res)
# print(cursor.lastrowid) #返回的是你插入的这条记录是到了第几条了 #part2 # sql='insert into userinfo(name,password) values(%s,%s);' # res=cursor.execute(sql,("root","123456")) #执行sql语句,返回sql影响成功的行数 # print(res) #还能够进行更改操做:
#res=cursor.excute("update userinfo set username='taibaisb' where id=2")
#print(res) #结果为1 #part3 sql='insert into userinfo(name,password) values(%s,%s);' res=cursor.executemany(sql,[("root","123456"),("lhf","12356"),("eee","156")]) #执行sql语句,返回sql影响成功的行数,一次插多条记录 print(res) #上面的几步,虽然都有返回结果,也就是那个受影响的函数res,可是你去数据库里面一看,并无保存到数据库里面, conn.commit() #必须执行conn.commit,注意是conn,不是cursor,执行这句提交后才发现表中插入记录成功,没有这句,上面的这几步操做其实都没有成功保存。 cursor.close() conn.close()
复制代码
复制代码

  四 查:fetchone,fetchmany,fetchall

    

复制代码
复制代码
import pymysql
#连接
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon')
#游标
cursor=conn.cursor()

#执行sql语句
sql='select * from userinfo;'
rows=cursor.execute(sql) #执行sql语句,返回sql影响成功的行数rows,将结果放入一个集合,等待被查询

# cursor.scroll(3,mode='absolute') # 相对绝对位置移动
# cursor.scroll(3,mode='relative') # 相对当前位置移动
res1=cursor.fetchone()
res2=cursor.fetchone()
res3=cursor.fetchone()
res4=cursor.fetchmany(2)
res5=cursor.fetchall()
print(res1)
print(res2)
print(res3)
print(res4)
print(res5)
print('%s rows in set (0.00 sec)' %rows)



conn.commit() #提交后才发现表中插入记录成功
cursor.close()
conn.close()

'''
(1, 'root', '123456')
(2, 'root', '123456')
(3, 'root', '123456')
((4, 'root', '123456'), (5, 'root', '123456'))
((6, 'root', '123456'), (7, 'lhf', '12356'), (8, 'eee', '156'))
rows in set (0.00 sec)
'''
复制代码
复制代码

  五 获取插入的最后一条数据的自增ID

复制代码
复制代码
import pymysql
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon')
cursor=conn.cursor()

sql='insert into userinfo(name,password) values("xxx","123");'
rows=cursor.execute(sql)
print(cursor.lastrowid) #在插入语句后查看

conn.commit()

cursor.close()
conn.close()
相关文章
相关标签/搜索