mysql單錶百萬資料分頁查詢效能優化

2021-09-29 08:12:48 字數 4496 閱讀 3890

1. 背景

(1)這張表的資料會每天同步過來,同步完成後資料就不會在更新了,只會查詢。

(2)需要在前端展示出來,資料量大概有一千多萬,所以肯定得在後端分頁。

(3)還有搜尋、篩選、排序等功能。

2. 表結構

下面是實驗用的表

create

table

`log`

(`id`

int(11)

notnull

auto_increment

,`title`

varchar

(255

)not

null

,`content`

text

notnull

,`create_time`

datetime

notnull

,primary

key(

`id`))

engine

=innodb

default

charset

=utf8mb4

用python插入了8百萬的資料, python**如下

import random

import string

import datetime

import threading

import mysqldb

deff()

: conn = mysqldb.connect(host=

'localhost'

, user=

'user'

, password=

'password'

, database=

'test'

) cursor = conn.cursor(

) sql =

'''insert into log (title, content, create_time) values ('{}', '{}', '{}')'''

for i in

range

(2000000):

title =

''.join(random.choices(string.ascii_letters, k=10)

) content =

''.join(random.choices(string.ascii_letters, k=30)

) create_time =

(datetime.datetime.now(

)- datetime.timedelta(

days=random.randint(1,

1000))

).strftime(

'%y-%m-%d %h:%m:%s'

) cursor.execute(sql.

format

(title, content, create_time)

)# 注意不要在每個execute後commit,這樣速度會很慢

# 但是如果在所有迴圈完之後再commit,在生產環境的話,

# 可能會很容易失敗,所以這個迴圈的資料需要計算下

print

(i) conn.commit(

) conn.close(

)defm(

):p1 = threading.thread(target=f)

p2 = threading.thread(target=f)

p3 = threading.thread(target=f)

p4 = threading.thread(target=f)

p1.start(

) p2.start(

) p3.start(

) p4.start(

) p1.join(

) p2.join(

) p3.join(

) p4.join(

)

3. 說明

後台用的時django,因為需要計算資料的總行數,本來準本把innodb引擎換成myisam的,因為在沒有where條件中的myisam的count(*)非常快,因為myisam儲存了總行數。

但是myisam是表鎖,在同步資料任務的時候,不好做任務拆分,很容易報錯。另外myisam的索引是壓縮索引,應用場景中需要使用倒序,這樣會很慢。

所以還是使用innodb。

4. 實驗

select

count(*

)from log;

oktime: 4.628s

select

*from log limit

900000,50

;oktime: 0.893s

# offset小於100萬時,查詢時間基本在1s以內。之後時間就會比較長了。

#但是如果只選擇id的話就比較快了,因為走的是索引

select id from log limit

900000,50

;oktime: 0.182s

所以我們可以基於索引來做優化,先把對應的id查出來,在根據直接根據id去取對應行的資料

select

*from log a inner

join

(select id from log limit

900000,50

) b using

(id);ok

time: 0.149s

在加上排序的對比

# 這個就非常慢了

select

*from log order

by create_time desc

limit

900000,50

;oktime: 24.436s

# 這個也需要4s多了

select

*from log a inner

join

(select id from log order

by create_time desc

limit

900000,50

) b using

(id)

order

by create_time desc;ok

time: 4.227s

在需要排序的字段上面加個索引在看看

alter

table log add

index

`create_time`

(`create_time`);

# 這種方式基本同上面一樣,時間沒變化。因為就算排序有索引,但是select取值時還是需要在磁碟上隨機查詢,所以索引的優勢沒有利用到。

select

*from log order

by create_time desc

limit

900000,50

;oktime: 25.586s

# 時間明顯降低,所以基本就按照這樣優化了

select

*from log a inner

join

(select id from log order

by create_time desc

limit

900000,50

) b using

(id)

order

by create_time desc;ok

time: 0.224s

5. django中使用

查詢了好久的文件,都沒有查到怎麼用django的orm構造這種同一張表的自連線。

所以在django裡面實現的時候,還是需要先把id查出來,然後在用where in這種來實現。

總函式的問題,因為時每天更新完資料就不會變了,就直接在資料更新完成後在快取裡面寫入總數。沒有where的查詢時,直接讀這個總行數,前端就可以根據這個來顯示有多少頁了。

通過實驗感覺在where欄位上面不管是否新增索引,當offset變大,速度都比較慢。

select

*from log where title like

'%zz%'

order

by create_time desc

limit

50ok

time: 0.026s

select

*from log a inner

join

(select id from log where title like

'%zz%'

order

by create_time desc

limit

50) b using

(id)

order

by create_time desc

oktime: 0.035s

MySQL單錶百萬資料記錄分頁效能優化

自己的乙個 由於單錶的資料記錄高達了一百萬條,造成資料訪問很慢,google分析的後台經常報告超時,尤其是頁碼大的頁面更是慢的不行。先讓我們熟悉下基本的sql語句,來檢視下我們將要測試表的基本資訊 use infomation schema select from tables where tabl...

MySQL單錶百萬資料記錄分頁效能優化

先讓我們熟悉下基本的sql語句,來檢視下我們將要測試表的基本資訊 use infomation schema select from tables where table schema dbname and table name product 查詢結果 從上圖中我們可以看到表的基本資訊 錶行數 8...

MySQL單錶百萬資料記錄分頁效能優化

自己的乙個 由於單錶的資料記錄高達了一百萬條,造成資料訪問很慢,google分析的後台經常報告超時,尤其是頁碼大的頁面更是慢的不行。先讓我們熟悉下基本的sql語句,來檢視下我們將要測試表的基本資訊 use infomation schema select from tables where tabl...