国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

面試官:Mysql千萬級(jí)大表如何進(jìn)行深度分頁優(yōu)化?

這篇具有很好參考價(jià)值的文章主要介紹了面試官:Mysql千萬級(jí)大表如何進(jìn)行深度分頁優(yōu)化?。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

背景

假如有一張千萬級(jí)的訂單表,這張表沒有采用分區(qū)分表,也沒有使用ES等技術(shù),分頁查詢進(jìn)行到一定深度分頁之后(比如1000萬行后)查詢比較緩慢,我們?cè)撊绾芜M(jìn)行優(yōu)化?

數(shù)據(jù)準(zhǔn)備

訂單表結(jié)構(gòu)如下:

CREATE TABLE `t_order` (  
    `id` BIGINT ( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',  
    `order_no` VARCHAR ( 16 ) NOT NULL DEFAULT '' COMMENT '訂單編號(hào)',  
    `customer_no` VARCHAR ( 16 ) NOT NULL DEFAULT '' COMMENT '客戶編號(hào)',  
    `order_status` TINYINT ( 4 ) NOT NULL DEFAULT 0 COMMENT '訂單狀態(tài)',  
    `warehouse_code` VARCHAR ( 16 ) NOT NULL DEFAULT '' COMMENT '發(fā)貨地倉庫編碼',  
    `country` VARCHAR ( 16 ) NOT NULL DEFAULT '' COMMENT '收貨人國家',  
    `state` VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '收貨人州',  
    `city` VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '收貨人城市',  
    `street` VARCHAR ( 256 ) NOT NULL DEFAULT '' COMMENT '收貨人街道',  
    `zip_code` VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '收貨人郵編',  
    `contact_email` VARCHAR ( 128 ) NOT NULL DEFAULT '' COMMENT '收貨人郵箱',  
    `contact_name` VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '收貨人姓名',  
    `contact_mobile` VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '收貨人手機(jī)號(hào)',  
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',  
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',  
    `deleted` TINYINT ( 2 ) NOT NULL DEFAULT 0 COMMENT '是否已被刪除',  
    PRIMARY KEY ( `id` ),  
    KEY `idx_customer` ( `customer_no`, `deleted` ),  
    KEY `idx_create_time` ( `create_time`, `deleted` )  
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = '銷售訂單表';

其中Mysql版本為8.0。我們使用Python腳本向表中插入2000萬條數(shù)據(jù)。

import pymysql  
from faker import Faker  
import random  
from datetime import datetime  
from concurrent.futures import ThreadPoolExecutor  
  
# MySQL 連接配置  
db_config = {  
   'host': 'your_database_host', 
   'user': 'your_database_user', 
   'password': 'your_database_password', 
   'database': 'your_database_name'
}  
  
# 創(chuàng)建 MySQL 連接  
conn = pymysql.connect(**db_config)  
cursor = conn.cursor()  
  
# 使用 Faker 生成模擬數(shù)據(jù)  
fake = Faker() 

# 獲取國家下發(fā)貨倉庫編碼  
def generate_warehousecode(country):  
    if country == "US":  
        return "US-"+random.choice(["WEST", "EAST", "MIDDLE", "SOUTH", "NORTH"])+"-0" + str(random.choice([1, 2, 3, 4, 5]))  
    else:  
        return country + "00" + str(random.choice([1, 2, 3, 4, 5]))

# 插入 t_order 表數(shù)據(jù)(多線程并發(fā),每個(gè)線程插入1萬條,共2000個(gè)線程)  
def insert_data_thread(thread_id):  
    # 創(chuàng)建 MySQL 連接  
    conn = pymysql.connect(**db_config)  
    cursor = conn.cursor()  
  
    order_data = []  
    for _ in range(10000):  
        order_no = "OC"+ fake.uuid4()[:12]  # 取前16位  
        customer_no = fake.uuid4()[:16]
        order_status = random.choice([1, 2, 3, 4, 5])  
        country = random.choice(  
            ["CA", "US", "MX", "JP", "UK", "TR", "DE", "ES", "FR", "IT", "NL", "PL", "SE", "BR", "CN"])  
        warehouse_code = generate_warehousecode(country)  
        state = fake.uuid4()[:16]  
        city = fake.uuid4()[:16]  
        street = fake.uuid4()  
        zip_code = fake.uuid4()[:6]  
        contact_email = fake.email()  
        contact_name = fake.name()  
        contact_mobile = fake.phone_number()  
        create_time = fake.date_time_between(start_date=datetime(2019, 1, 1), end_date=datetime.now())  
        update_time = create_time  
        deleted = 0  # 默認(rèn)未刪除  
  
        cursor.execute("""  
            INSERT INTO t_order (                order_no, customer_no, order_status, warehouse_code, country,                state, city, street, zip_code, contact_email, contact_name,                contact_mobile, create_time, update_time, deleted            )            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)        """, (order_no, customer_no, order_status, warehouse_code, country,  
              state, city, street, zip_code, contact_email, contact_name,  
              contact_mobile, create_time, update_time, deleted))  
  
        order_data.append((cursor.lastrowid, order_no, customer_no, create_time))  # 保存插入的行的 ID  
    # 提交 t_order 數(shù)據(jù)插入  
    conn.commit()  
    print(thread_id+ "已經(jīng)跑完10000條數(shù)據(jù)。。。。。。。。。")  
    # 關(guān)閉數(shù)據(jù)庫連接  
    cursor.close()  
    conn.close()  
  
# 使用 ThreadPoolExecutor 并發(fā)插入  
with ThreadPoolExecutor(max_workers=10) as executor:  # 可以根據(jù)需要調(diào)整最大線程數(shù)  
    executor.map(insert_data_thread, range(500))

問題復(fù)現(xiàn)

導(dǎo)出數(shù)據(jù)時(shí)我們需要按照時(shí)間倒序。所以我們先執(zhí)行以下sql查詢前100條

select * FROM t_order ORDER BY create_time desc LIMIT 100;

共花費(fèi)210ms。執(zhí)行計(jì)劃如下:

然后我們繼續(xù)執(zhí)行sql,我們從第100萬行開始取100條數(shù)據(jù):

select * FROM t_order ORDER BY create_time desc LIMIT 1000000,100;

此時(shí)耗時(shí)3分2秒,耗時(shí)明顯增加。執(zhí)行計(jì)劃如下:


由執(zhí)行計(jì)劃看,此時(shí)索引已經(jīng)失效了。。。。

我們繼續(xù)執(zhí)行sql,從第1000萬行開始取100條數(shù)據(jù):

select * FROM t_order ORDER BY create_time desc LIMIT 10000000,100;

此時(shí)耗時(shí)4分14秒,時(shí)間真是太長了,執(zhí)行計(jì)劃如下:

后面還有接近1000萬條數(shù)據(jù)沒有取出來,直接就廢了。

原因分析

當(dāng)我們使用?LIMIT offset, count?查詢語句進(jìn)行深度分頁查詢時(shí),例如?LIMIT 10000000,100?,會(huì)發(fā)生以下過程:

  1. MySQL首先會(huì)根據(jù)給定條件從相應(yīng)的索引樹中查找m+n條記錄。對(duì)于聚集索引來說,它直接找到需要的結(jié)果即丟棄前offset條數(shù)據(jù),返回count條數(shù)據(jù)并返回;而對(duì)于二級(jí)索引,則可能涉及回表操作。
  2. 如果使用的是二級(jí)索引,在查到m+n條記錄后還需要通過這些記錄所關(guān)聯(lián)的主鍵ID去聚集索引里再次搜索出完整的行數(shù)據(jù),然后再丟棄掉前offset條數(shù)據(jù),返回count條數(shù)據(jù)。因此在這個(gè)過程中可能會(huì)產(chǎn)生大量的“回表”操作,這將導(dǎo)致性能下降。

我們借助B+ Tree Visualization演示一下這張表的索引結(jié)構(gòu):

  • 聚集索引(主鍵ID)
  • 二級(jí)索引(idx_create_time)

以上述例子來說,當(dāng)我們查詢LIMIT 10000000,100時(shí),它會(huì)先從二級(jí)索引中查詢10000000+100條記錄對(duì)應(yīng)的ID,然后再用這些記錄的ID去聚集索引中查詢ID對(duì)應(yīng)的記錄,然后舍棄掉前10000000條數(shù)據(jù),返回后100條數(shù)據(jù)。

所以當(dāng)offset+count量很大時(shí),Mysql的執(zhí)行器認(rèn)為全表掃描的性能更由于使用索引,所以也導(dǎo)致索引失效。所以我們要做的盡可能的減少回表的記錄數(shù)量。

解決方案

使用子查詢

我們改造sql,通過一個(gè)子查詢按照create_time倒排,獲取第offset + 1條記錄的最新的create_time,create_time直接從二級(jí)索引上可以獲取,不會(huì)進(jìn)行回表,然后我們?cè)俑鶕?jù)這個(gè)create_time傳遞到主查詢時(shí),取100條數(shù)據(jù),即回表數(shù)據(jù)也僅僅只有count條即100條數(shù)據(jù),大大減少了回表的記錄數(shù)量。

SELECT * FROM t_order  
WHERE create_time <= (
SELECT create_time FROM t_order ORDER BY create_time desc LIMIT 1000000,1
)
ORDER BY create_time desc LIMIT 100;

查詢第100萬時(shí)耗時(shí)556毫秒。

執(zhí)行結(jié)果,執(zhí)行計(jì)劃


可以看出主查詢以及子查詢都使用到了索引,回表查詢的數(shù)據(jù)記錄數(shù)也大大減少。

繼續(xù)查詢到第1000萬行時(shí)耗時(shí)接近6秒。
執(zhí)行結(jié)果,執(zhí)行計(jì)劃


可以看出主查詢以及子查詢都使用到了索引,回表查詢的數(shù)據(jù)記錄數(shù)也大大減少。

這種方式需要create_time 的分布是相對(duì)均勻的,否則可能會(huì)導(dǎo)致某個(gè)時(shí)間段內(nèi)的數(shù)據(jù)較多,影響查詢性能。

INNER JOIN

我們改造sql,create_timeid都存儲(chǔ)在二級(jí)索引中,我們獲取這兩列值不需要回表,所以我們創(chuàng)建一個(gè)偏移量為offset,個(gè)數(shù)為count并且包含create_time以及id的臨時(shí)表,臨時(shí)表中數(shù)據(jù)不需要回表。然后再跟自身通過主鍵ID進(jìn)行關(guān)聯(lián),僅需要回表count條數(shù)據(jù),大大減少了回表的記錄格式。同時(shí)也使用了主鍵索引關(guān)聯(lián),效率也大大提高。

SELECT torder.* FROM  t_order torder
	INNER JOIN (
	SELECT id FROM t_order ORDER BY create_time DESC LIMIT 1000000,100 
	) tmp ON torder.id = tmp.id 
ORDER BY
	create_time DESC

查詢第100萬時(shí)耗時(shí)260毫秒。
執(zhí)行結(jié)果,執(zhí)行計(jì)劃。

繼續(xù)查詢到第1000萬行時(shí)耗時(shí)接近2秒
執(zhí)行結(jié)果,執(zhí)行計(jì)劃

這種方式要保證INNER JOIN使用了合適的索引。

SEARCH AFTER

每次查詢都保留上次的最小的create_time,然后下次查詢只查詢比上一頁的create_time小的數(shù)據(jù)。單表查詢,并且使用索引,回表數(shù)據(jù)少,不需要子查詢以及關(guān)聯(lián)查詢,查詢效率高。類似ES的SEARCH AFTER的查詢方式。

-- 我們模擬連續(xù)分頁到第1000000頁,最小的一條數(shù)據(jù)的create_time
SELECT * FROM t_order  
ORDER BY create_time ASC  LIMIT 1000000, 1

SELECT * FROM t_order  
WHERE create_time <= '2023-01-22 00:00:00' 
ORDER BY create_time desc LIMIT 100;

查詢第100萬時(shí)耗時(shí)142毫秒。

執(zhí)行結(jié)果,執(zhí)行計(jì)劃

繼續(xù)查詢到第1000萬行時(shí)耗時(shí)244毫秒

執(zhí)行結(jié)果,執(zhí)行計(jì)劃

當(dāng)然該種方式缺點(diǎn)也很明顯:只能支持連續(xù)分頁,不能支持獲取隨意頁的數(shù)據(jù)。

其他方案

  • 限制查詢范圍: 在需求層面,可以限制只能查詢前100頁數(shù)據(jù),或者規(guī)定只能獲取某個(gè)時(shí)間段內(nèi)的數(shù)據(jù),從而避免深度分頁。
  • 水平分表:考慮將數(shù)據(jù)按照某個(gè)維度進(jìn)行水平分表,以減小單表的數(shù)據(jù)量
  • 使用ES,Hive,ClickHouse等OLAP方案

本文已收錄于我的個(gè)人博客:碼農(nóng)Academy的博客,專注分享Java技術(shù)干貨,包括Java基礎(chǔ)、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中間件、架構(gòu)設(shè)計(jì)、面試題、程序員攻略等文章來源地址http://www.zghlxwxcb.cn/news/detail-825188.html

到了這里,關(guān)于面試官:Mysql千萬級(jí)大表如何進(jìn)行深度分頁優(yōu)化?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 阿里二面:千萬級(jí)、億級(jí)數(shù)據(jù),如何性能優(yōu)化? 教科書級(jí) 答案來了

    阿里二面:千萬級(jí)、億級(jí)數(shù)據(jù),如何性能優(yōu)化? 教科書級(jí) 答案來了

    在尼恩指導(dǎo)了幾百個(gè)小伙伴的面試,在這些過程中, 非常、非常高頻的一個(gè)面試題: 千萬級(jí)數(shù)據(jù),如何做性能優(yōu)化? 億級(jí)數(shù)據(jù),如何做性能優(yōu)化? 最近,有個(gè)小伙伴阿里二面,又遇到了這個(gè)問題。 其實(shí),尼恩一直想梳理一個(gè)教科書式的答案, 但是由于千萬級(jí)數(shù)據(jù)、億級(jí)數(shù)

    2024年02月02日
    瀏覽(22)
  • kafka千萬級(jí)數(shù)據(jù)積壓原因以及解決方案

    一、原因 kafka作為消息隊(duì)列,其中數(shù)據(jù)積壓也是經(jīng)常遇到的問題之一。 我們都知道,數(shù)據(jù)積壓的直接原因,一定是系統(tǒng)中的某個(gè)部分出現(xiàn)了性能問題,來不及處理上游發(fā)送的數(shù)據(jù),才會(huì)導(dǎo)致數(shù)據(jù)積壓。 那么我們就需要分析在使用kafka時(shí),如何通過優(yōu)化代碼以及參數(shù)配置來最大

    2024年02月12日
    瀏覽(23)
  • 千萬級(jí)數(shù)據(jù)深分頁查詢SQL性能優(yōu)化實(shí)踐

    如何在Mysql中實(shí)現(xiàn)上億數(shù)據(jù)的遍歷查詢?先來介紹一下系統(tǒng)主角:關(guān)注系統(tǒng),主要是維護(hù)京東用戶和業(yè)務(wù)對(duì)象之前的關(guān)注關(guān)系;并對(duì)外提供各種關(guān)系查詢,比如查詢用戶的關(guān)注商品或店鋪列表,查詢用戶是否關(guān)注了某個(gè)商品或店鋪等。但是最近接到了一個(gè)新需求,要求提供查

    2024年02月11日
    瀏覽(30)
  • ElasticSearch + Canal 開發(fā)千萬級(jí)的實(shí)時(shí)搜索系統(tǒng)【轉(zhuǎn)】

    ElasticSearch + Canal 開發(fā)千萬級(jí)的實(shí)時(shí)搜索系統(tǒng)【轉(zhuǎn)】

    我們總結(jié)一下新搜索系統(tǒng)需要解決的幾個(gè)問題: 海量請(qǐng)求。幾百萬的請(qǐng)求毫無壓力,上千萬上億也要可以扛得住。 實(shí)時(shí)搜索。指的是當(dāng)一個(gè)用戶修改了其數(shù)據(jù)之后,另一個(gè)用戶能實(shí)時(shí)地搜索到改用戶。 海量請(qǐng)求。要扛得起海量的搜索請(qǐng)求,可以使用ElasticSearch來實(shí)現(xiàn),它是在

    2024年04月09日
    瀏覽(18)
  • Java中處理千萬級(jí)數(shù)據(jù)的最佳實(shí)踐:性能優(yōu)化指南

    在今天的數(shù)字化時(shí)代,處理大規(guī)模數(shù)據(jù)已經(jīng)成為許多Java應(yīng)用程序的核心任務(wù)。無論您是構(gòu)建數(shù)據(jù)分析工具、實(shí)現(xiàn)實(shí)時(shí)監(jiān)控系統(tǒng),還是處理大規(guī)模日志文件,性能優(yōu)化都是確保應(yīng)用程序能夠高效運(yùn)行的關(guān)鍵因素。本指南將介紹一系列最佳實(shí)踐,幫助您在處理千萬級(jí)數(shù)據(jù)時(shí)提高

    2024年02月03日
    瀏覽(37)
  • 千萬級(jí)數(shù)據(jù)并發(fā)解決方案(理論+實(shí)戰(zhàn)) 高并發(fā)解決思路 方案

    千萬級(jí)數(shù)據(jù)并發(fā)解決方案(理論+實(shí)戰(zhàn)) 高并發(fā)解決思路 方案

    課程地址 項(xiàng)目地址 秒殺 高并發(fā) 新聞系統(tǒng) 超大數(shù)據(jù)量 一般的網(wǎng)站 寫入的少 讀取的次數(shù)多 模糊查詢 數(shù)據(jù)量少的時(shí)候可以用 like 數(shù)據(jù)量多的時(shí)候用 Elasticsearch搜索引擎 占用磁盤空間比較大 在laravel中 創(chuàng)建 要提前配置好數(shù)據(jù)庫 在Tests文件中寫入 運(yùn)行命令 php artisan app:tests 生成

    2024年02月08日
    瀏覽(27)
  • 千萬級(jí)數(shù)據(jù)的表,我把慢sql優(yōu)化后性能提升30倍!

    千萬級(jí)數(shù)據(jù)的表,我把慢sql優(yōu)化后性能提升30倍!

    背景:系統(tǒng)中有一個(gè)統(tǒng)計(jì)頁面加載特別慢,前端設(shè)置的40s超時(shí)時(shí)間都加載不出來數(shù)據(jù),因?yàn)槭莻€(gè)統(tǒng)計(jì)頁面,基本上一猜就知道是mysql的語句有問題,遺留了很久沒有解決,正好趁不忙的時(shí)候,下定決心一定把它給搞定! (mysql5.7) 執(zhí)行一下問題sql,可以看到單表查就需要61s 這

    2024年02月14日
    瀏覽(21)
  • 數(shù)據(jù)量超過億級(jí)別,MySQL大表遷移該如何做?

    數(shù)據(jù)量超過億級(jí)別,MySQL大表遷移該如何做?

    MySQL 作為當(dāng)前應(yīng)用最廣泛的開源關(guān)系型數(shù)據(jù)庫之一,具有高性能、穩(wěn)定性和易用性等特性,是許多網(wǎng)站、應(yīng)用和商業(yè)產(chǎn)品的主要數(shù)據(jù)存儲(chǔ)。在一些場景中,如果出現(xiàn)單表行數(shù)上億的情況,就可能需要開發(fā)和 DBA 對(duì)大表進(jìn)行優(yōu)化:分表、歸檔或擴(kuò)容操作,而在這些操作中都涉及

    2024年02月11日
    瀏覽(20)
  • 權(quán)威答案!靈犀醫(yī)療引入 Zilliz Cloud,千萬級(jí)向量數(shù)據(jù)庫賦能醫(yī)學(xué) AIGC 平臺(tái)

    權(quán)威答案!靈犀醫(yī)療引入 Zilliz Cloud,千萬級(jí)向量數(shù)據(jù)庫賦能醫(yī)學(xué) AIGC 平臺(tái)

    “醫(yī)療行業(yè)是一個(gè)信息差較大的行業(yè),術(shù)語體系龐雜且知識(shí)門類較多,如何能搜索到最精準(zhǔn)的醫(yī)學(xué)知識(shí)并采用最合理方式進(jìn)行總結(jié),這是我們醫(yī)學(xué) AIGC 平臺(tái) EviMed 所遇見的最主要的技術(shù)問題。 傳統(tǒng)的數(shù)據(jù)庫和全文檢索方式難以滿足我們的技術(shù)要求,結(jié)合了 Zilliz Cloud 向量數(shù)據(jù)

    2024年04月25日
    瀏覽(17)
  • EMQX+阿里云飛天洛神云網(wǎng)絡(luò) NLB:MQTT 消息億級(jí)并發(fā)、千萬級(jí)吞吐性能達(dá)成

    EMQX+阿里云飛天洛神云網(wǎng)絡(luò) NLB:MQTT 消息億級(jí)并發(fā)、千萬級(jí)吞吐性能達(dá)成

    隨著物聯(lián)網(wǎng)技術(shù)的發(fā)展與各行業(yè)數(shù)字化進(jìn)程的推進(jìn),全球物聯(lián)網(wǎng)設(shè)備連接規(guī)模與日俱增。一個(gè)可靠高效的物聯(lián)網(wǎng)系統(tǒng)需要具備高并發(fā)、大吞吐、低時(shí)延的數(shù)據(jù)處理能力,支撐海量物聯(lián)網(wǎng)數(shù)據(jù)的接入與分析,從而進(jìn)一步挖掘數(shù)據(jù)價(jià)值。 于今年五月發(fā)布的 EMQX 5.0 版本全球首個(gè)實(shí)

    2023年04月15日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包