Python操做MySQL以及數據庫索引

python操做MySQL

安裝

pip3 install pymsqlmysql

使用

方法:sql

  • conn = pymysql.conn():實例化對象,創建mysql鏈接數據庫

    --- host:鏈接的mysql主機ip安全

    --- user:鏈接mysql的用戶app

    --- password:鏈接mysql的密碼函數

    --- database:鏈接mysql的數據庫工具

    --- charset:鏈接mysql的字符編碼性能

  • cursor = conn.cursor():創建遊標fetch

  • cursor.execute(sql):執行sql語句

  • conn.commit():提交,若是要增刪改的話須要提交才能執行sql

  • cursor.fetchall():取出sql執行完畢的全部數據,默認以元組結果展現

  • cursor.fetchone():取出sql執行完畢的一條數據

  • cursor.fetchmany(size):取出sql執行完畢的size條數據

  • conn.commit():增刪改數據庫數據的時候要寫上這個代碼

  • cursor.lastrowid:獲取最後一行記錄

  • cursor.close():關閉鏈接

  • conn.close():關閉鏈接

import pymysql

user = input("請輸入用戶名").strip()
pwd = input("請輸入密碼").strip()

# 創建鏈接
conn = pymysql.connect(
    host="192.168.32.130",
    user="root",
    password="123",
    database="work",
    charset="utf8"
)

# cursor = conn.cursor()    # 執行完畢以後的結果默認以元組形式輸出
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)     # 指定輸出類型爲字典

# 定義sql語句
# sql = "insert into pymysql_test (user ,pwd) values (%s,%s)"       # 增
# sql = "delete from pymysql_test where id=2;"                      # 刪
# sql = "update pymysql_test set pwd="123" where id=3;"             # 改
sql = "select * from pymysql_test where user=%s and pwd=%s"       # 查
sql = "select * from pymysql_test where user='%s' and pwd='%s'" % (user,pwd)      # 查 演示sql注入,注意,佔位符會把字符串類型的引號去掉,讓它再也不是字符串類型
# sql = "select * from pymysql_test"                                # 查

# 執行sql語句
# cursor.execute(sql)
cursor.execute(sql,(user,pwd))

# 若是要 增刪改數據的話 須要commit,查不須要
conn.commit()

# 獲取最後一行的ID值
print(cursor.lastrowid)

# 接收執行結果
res = cursor.fetchall()     # 取出全部數據
# res = cursor.fetchone()     # 取出一條數據
# res = cursor.fetchmany(2)     # 取出指定條數數據,若是指定條數超過數據庫裏已有的條目則所有展現,不會報錯
print(res)

# 關閉鏈接
cursor.close()
conn.close()

if res:
    print("登陸成功")
else:
    print("登陸失敗")

SQL注入問題

那麼以上咱們對pymysql進行了簡單的使用,那麼接下來有一個安全上的問題,就是sql注入問題

之因此產生sql注入問題的緣由是 咱們沒有對用戶的數據進行約束,用戶在輸入的時候可能會隨便輸入,就會引起sql注入問題:

咱們正常登錄是這樣的:

請輸入用戶名qinyj
請輸入密碼123
sql語句是:select * from pymysql_test where user='qinyj' and pwd='123'

登錄成功

那麼有些用戶可能隨便輸入用戶名也能登錄成功,好比如下這種:

輸入用戶名:qinyj' or 1=1 #
輸入密碼:dsadsa

請輸入用戶名qinyj' #
請輸入密碼123
sql語句是: select * from pymysql_test where user='qinyj' #' and pwd='123'

登錄成功

以上用戶名輸入錯誤還能登錄成功的狀況,是由於有 #號,mysql在碰到 這個符號以後不會執行後面的sql語句,因此至關於select * from pymysql_test查全部的數據

這種狀況咱們就稱之爲sql注入問題,這是一個很是嚴重的問題,在公司中開發是絕對不容許的,咱們要解決這個問題能夠有兩種方法:

--- 1. 本身編寫對用戶輸入的規則,經過後才執行sql

--- 2. 使用pymysql自帶的檢驗工具 cursor.execute(sql, (user, pwd)),將用戶名、密碼傳入執行sql的參數中。

MySQL的索引

在介紹 MySQL索引以前,咱們先建立一個表,表結構以下

mysql> desc pymysql_test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| user  | varchar(32) | NO   |     |         |                |
| email | varchar(32) | NO   |     |         |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

來對這個表插入500W條數據,方便下面的講解。

import pymysql

conn = pymysql.connect(
    host="192.168.32.130",
    user="root",
    password="123",
    database="work",
    charset="utf8"
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

sql = "insert into pymysql_test(user,email) values (%s,%s)"

data = []
for i in range(5000000):
    info = ("qinyj" + str(i),"qinyj" + str(i) + "@qq.com")
    data.append(info)
# print(data)

cursor.executemany(sql,data)    # 新增多條數據
conn.commit()
cursor.close()
conn.close()


select * from pymysql_test;
...
5000000 rows in set (20.45 sec)

爲何使用索引

使用索引就是爲了提升查詢的效率,相似於新華字典中的目錄

索引的本質就是一個特殊的文件,記錄着索引信息

索引的底層原理:B + 樹

索引的種類

  • 主鍵索引
  • 惟一索引
  • 普通索引
mysql> desc test;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(32) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

主鍵索引

加速查找、不能重複、不能爲空

建立主鍵索引:primary key

一、create table test(id int auto_increment primary key)charset=utf8;
二、alter table test modify id int auto_increment primary key;
三、alter table test add primary key(id);

刪除主鍵索引:
注意:若是主鍵加上了自增的約束就不能被刪除
alter table test drop primary key;

惟一索引

加速查找、不能重複 unique(xxx)

惟一聯合索引:unique(xxx,xxx)

建立惟一索引:unique
一、create table test(id int auto_increment primary key,name varchar(32) not null default '',unique u_name(name))charset=utf8;
二、create unique index u_name on test(name);
三、alter table test add unique index u_name (name);

建立聯合索引:
一、create table test(id int auto_increment primary key,name varchar(32) not null default '',address varchar(32) not null default '',age int not null default 0, unique u_name_age(name,age))charset=utf8;
二、alter table pymysql_test add unique index pt_name (user,email);


刪除惟一索引:
alter table test drop index u_name;

普通索引

加速查找 index(xxx)

普通聯合索引:index(xxx,xxx)

建立普通索引:index
一、create table test(id int auto_increment primary key,name varchar(32) not null default '',index ix_name(name))charset=utf8;
二、create index ix_name on test(name);
三、alter table test add index ix_name (name);

建立聯合普通索引:
create table test(id int auto_increment primary key,name varchar(32) not null default '',age int not null default 0,index ix_name_age (name,age))charset=utf8;

刪除普通索引
alter table test drop index ix_name;

索引優缺點

經過觀察 *.ibd 文件可知:

  • 索引加快了查詢速度
  • 加了索引以後,會佔用大量的磁盤空間

因此索引不是加的越多越好

不會命中索引的狀況

mysql> desc pymysql_test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| user  | varchar(32) | NO   |     |         |                |
| email | varchar(32) | NO   |     |         |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

會下降sql的查詢效率的狀況:

  1. 在sql語句中進行四則運算

    select * from pymysql_test where id*2=5127;
    Empty set (6.55 sec)

  2. 使用聚合函數

select count(id) from pymysql_test;

1 row in set (4.06 sec)

  1. 查詢類型不一致的時候,若是列是字符串類型,條件也必須用引號引發來表示字符串

    select * from pymysql_test where email=999;

    Empty set, 65535 warnings (9.20 sec)

  2. 排序 order by

    排序條件爲索引,則查詢字段必須也是索引字段,不然沒法命中

    若是查詢的字段與排序字段的不是主鍵的字段,那麼速度依然很慢

    select email from pymysql_test order by email desc;

    若是查詢的字段與排序的字段是主鍵的字段,那麼速度仍是很快

    select id from pymysql_test order by id desc;

    5000004 rows in set (9.48 sec)

  3. count(1) 或 count(列) 代替 count(*) 在mysql中沒有差異

    select count(*) from pymysql_test;

    select count(1) from pymysql_test;

    select count(id) from pymysql_test;

    1 row in set (4.29 sec)

  4. 組合索引最左前綴

    何時會建立聯合索引?

    根據公司業務場景,在最經常使用的幾列上添加索引

    mysql> desc pymysql_test;
    +-------+-------------+------+-----+---------+----------------+
    | Field | Type        | Null | Key | Default | Extra          |
    +-------+-------------+------+-----+---------+----------------+
    | id    | int(11)     | NO   | PRI | NULL    | auto_increment |
    | user  | varchar(32) | NO   |     |         |                |
    | email | varchar(32) | NO   |     |         |                |
    +-------+-------------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)
    
    
    建立聯合索引
    alter table pymysql_test add unique index pt_name (user,email);
    Query OK, 0 rows affected (1 min 4.77 sec)
    Records: 0  Duplicates: 0  Warnings: 0

    建立完聯合索引以後呢,咱們使用組合查詢效率會很是高,

    聯合查詢:效率很是高
    select * from pymysql_test where user="qinyj1324" and email="qinyj1324@qq.com";
    1 row in set (0.00 sec)
    
    
    查詢最左前前綴:效率也是很是高
    select * from pymysql_test where user="qinyj122432" ;
    1 row in set (0.00 sec)
    
    查詢後面的:效率就低了
    select * from pymysql_test where email="qinyj1324@qq.com"; 
    1 row in set (5.96 sec)

    例子:

    組合索引:index (a,b,c,d)

    where a=2 and b=2 and c=2 and d=5 --> 命中索引

    where a=2 and c=2 and d=5 -->部分命中索引

    where d =5 -->沒有命中

explain

explain關鍵字能夠模擬優化器執行sql語句,從而知道MySQL是如何處理sql語句的,分析查詢語句或是結構的性能瓶頸

咱們只須要在查詢sql語句前面加上explain就好了

explain select * from pymysql_test where email="qinyj1324@qq.com"\G;;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pymysql_test
         type: index
possible_keys: NULL
          key: pt_name
      key_len: 196
          ref: NULL
         rows: 4988562
        Extra: Using where; Using index
1 row in set (0.00 sec)

參數解釋:

  • type:索引指向,若是是all的話那就是使用索引
  • possible_keys:可能用到的索引
  • key:確實用到的索引
  • key_len:索引長度
  • rows:掃描的行數
  • Extra:描述信息,使用到了索引

索引覆蓋

select id from pymysql_test where id=20000;

MySQL慢查詢日誌

查看慢sql的相關變量

show variables like '%slow%';
+---------------------------+------------------------------------+
| Variable_name             | Value                              |
+---------------------------+------------------------------------+
| log_slow_admin_statements | OFF                                |
| log_slow_slave_statements | OFF                                |
| slow_launch_time          | 2                                  |
| slow_query_log            | OFF                                |
| slow_query_log_file       | /data/app/mysql/localhost-slow.log |
+---------------------------+------------------------------------+


show variables like '%long%';
+--------------------------------------------------------+-----------+
| Variable_name                                          | Value     |
+--------------------------------------------------------+-----------+
| long_query_time                                        | 10.000000 |

參數解釋:

  • slow_query_log:慢查詢日誌開關,默認關閉
  • slow_query_log_file:慢查詢存放位置
  • long_query_time:慢查詢時間限制

配置慢查詢的變量:

set global slow_query_log = on;
set global slow_query_log_file = '/data/app/mysql/myslow.log';
set global long_query_time = 1;

配置完畢以後退出重連一下就會有生成的慢查詢日誌了

ll /data/app/mysql/myslow.log -rw-rw---- 1 mysql mysql 180 Oct 31 05:37 /data/app/mysql/myslow.log

相關文章
相關標籤/搜索