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()
相關文章
相關標籤/搜索