溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Mysql排序的特性是什么

發(fā)布時間:2021-10-29 18:07:06 來源:億速云 閱讀:137 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Mysql排序的特性是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Mysql排序的特性是什么”吧!

    1、問題場景

    新上線一個交易記錄導(dǎo)出功能,邏輯很簡單:根據(jù)查詢條件,導(dǎo)出對應(yīng)的數(shù)據(jù)。由于數(shù)據(jù)量比較大,在查詢數(shù)據(jù)庫時采用了分頁查詢,每次查詢1000條數(shù)據(jù)。

    自測正常,測試環(huán)境正常,上線之后運營反饋導(dǎo)出的數(shù)據(jù)有重復(fù)記錄。

    原本是以為業(yè)務(wù)邏輯問題,重新Review了一遍代碼,依舊未找到問題原因。最后只好把SQL語句拿出來單獨執(zhí)行,導(dǎo)出數(shù)據(jù),對比發(fā)現(xiàn)竟然是SQL語句查詢結(jié)果亂序?qū)е碌摹?/p>

    2、原因分析

    查詢語句以create_time進(jìn)行倒序排序,通過limit進(jìn)行分頁,在正常情況下不會出現(xiàn)問題。但當(dāng)業(yè)務(wù)并發(fā)量比較大,導(dǎo)致create_time存在大量相同值時,再基于limit進(jìn)行分頁,就會出現(xiàn)亂序問題。

    出現(xiàn)的場景是:create_time排序,當(dāng)create_time存在相同值,通過limit分頁,導(dǎo)致分頁數(shù)據(jù)亂序。

    比如,查詢1000條數(shù)據(jù),其中有一批create_time記錄值都為”2021-10-28 12:12:12“,當(dāng)創(chuàng)建時間相同的這些數(shù)據(jù),一部分出現(xiàn)在第一頁,一部分出現(xiàn)在第二頁,在查詢第二頁的數(shù)據(jù)時,可能會出現(xiàn)第一頁已經(jīng)查過的數(shù)據(jù)。

    也就是說,數(shù)據(jù)會來回跳動,一會兒出現(xiàn)在第一頁,一會兒出現(xiàn)在第二頁,這就導(dǎo)致導(dǎo)出的數(shù)據(jù)一部分重復(fù),一部分缺失。

    查看了Mysql 5.7和8.0的官方文檔,描述如下:

    If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.

    上述內(nèi)容概述:在使用ORDER BY對列進(jìn)行排序時,如果對應(yīng)(ORDER BY的列)列存在多行相同數(shù)據(jù),(Mysql服務(wù)器會按照任意順序返回這些行,并且可能會根據(jù)整體執(zhí)行計劃以不同的方式返回。

    簡單來說就是:ORDER BY查詢的數(shù)據(jù),如果ORDER BY列存在多行相同數(shù)據(jù),Mysql會隨機返回。這就會導(dǎo)致雖然使用了排序,但也會發(fā)生亂序的狀況。

    3、解決方案

    針對上述問題,基本的解決思路是:避免ORDER BY列的值出現(xiàn)重復(fù)。因此,可以加入其他維度,比如ID等其他排序列。

    select * from tb_order order by create_time ,id desc;

    這樣,在create_time相同時,會根據(jù)id進(jìn)行排序,而id肯定是不同的,就再不會出現(xiàn)上述問題了。

    4、拓展知識

    其實,上述內(nèi)容在Mysql的官網(wǎng)已經(jīng)有明確說明,而且還舉了例子。下面對官網(wǎng)的內(nèi)容和例子做一個簡單的匯總總結(jié)。

    4.1 limit查詢優(yōu)化

    如果我們只是查詢一個結(jié)果集的一部分,那么不要查詢所有數(shù)據(jù),然后再丟棄不需要的數(shù)據(jù),而是要通過limit條件來進(jìn)行限制。

    在沒使用having條件時,Mysql可能會對limit條件優(yōu)化:

    • 如果只查詢幾條數(shù)據(jù),建議使用limit,這樣Mysql可能會用到索引,而通常情況下Mysql是全表掃描;

    • 如果將limit row_countorder by結(jié)合使用,Mysql會在找到第一個row_count結(jié)果集后立刻停止排序,而不是對整個結(jié)果集進(jìn)行排序。如果此時基于索引進(jìn)行操作,速度會更快。如果必須進(jìn)行文件排序,在找到row_count結(jié)果集之前,會對部分或所有符合條件的結(jié)果進(jìn)行排序。但當(dāng)找到row_count結(jié)果之后,便不會對剩余部分進(jìn)行排序了。這種特性的一個表現(xiàn)就是我們前面提到的帶有l(wèi)imit和不帶limit進(jìn)行查詢時,返回的結(jié)果順序可能不同。

    • 如果將limit row_count和distinct結(jié)合使用,Mysql會在找到row_count結(jié)果集唯一行后立馬停止。

    • 在某些情況下,可以通過按照順序讀取索引(或?qū)λ饕M(jìn)行排序),然后計算摘要直到索引變化來實現(xiàn)group by。在這種情況下,limit row_count不會計算任何不必要的group by值。

    • 一旦MySQL向客戶端發(fā)送了所需數(shù)量的行,就會中止查詢,除非使用了SQL_CALC_FOUND_ROWS。在這種情況下,可以使用 SELECT FOUND_ROWS() 檢索行數(shù)。

    • LIMIT 0會快速返回一個空集合,通??捎糜跈z查SQL的有效性。還可以用于在應(yīng)用程序中獲得結(jié)果集的類型。在Mysql客戶端中,可以使用--column-type-info來顯示結(jié)果列類型。

    • 如果使用臨時表來解析查詢,Mysql會使用 limit row_count來計算需要多少空間。

    • 如果order by未使用索引,且存在limit條件,則優(yōu)化器可能會避免使用合并文件,而采用內(nèi)存filesort操作對內(nèi)存中的行進(jìn)行排序。

    了解了limit的一些特性,下面再回到本文的重點,limit row_countorder by結(jié)合使用特性。

    4.2 limit與order by結(jié)合使用

    在上面第二條中已經(jīng)提到,limit row_countorder by結(jié)合呈現(xiàn)的特性之一就是結(jié)果返回的順序是不確定的。而影響執(zhí)行計劃的一個因素就是limit,因此帶有limit與不帶有limit執(zhí)行同樣的查詢語句,返回結(jié)果的順序可能不同。

    下面示例中,根據(jù)category列進(jìn)行排序查詢,而id和rating是不確定的:

    mysql> SELECT * FROM ratings ORDER BY category;
    +----+----------+--------+
    | id | category | rating |
    +----+----------+--------+
    |  1 |        1 |    4.5 |
    |  5 |        1 |    3.2 |
    |  3 |        2 |    3.7 |
    |  4 |        2 |    3.5 |
    |  6 |        2 |    3.5 |
    |  2 |        3 |    5.0 |
    |  7 |        3 |    2.7 |
    +----+----------+--------+

    當(dāng)查詢語句包含limit時,可能會影響到category值相同的數(shù)據(jù):

    mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
    +----+----------+--------+
    | id | category | rating |
    +----+----------+--------+
    |  1 |        1 |    4.5 |
    |  5 |        1 |    3.2 |
    |  4 |        2 |    3.5 |
    |  3 |        2 |    3.7 |
    |  6 |        2 |    3.5 |
    +----+----------+--------+

    其中id為3和4的結(jié)果位置發(fā)生了變化。

    在實踐中,保持查詢結(jié)果的順序性往往非常重要,此時就需要引入其他列來保證結(jié)果的順序性了。

    當(dāng)上述實例引入id之后,查詢語句及結(jié)果如下:

    mysql> SELECT * FROM ratings ORDER BY category, id;
    +----+----------+--------+
    | id | category | rating |
    +----+----------+--------+
    |  1 |        1 |    4.5 |
    |  5 |        1 |    3.2 |
    |  3 |        2 |    3.7 |
    |  4 |        2 |    3.5 |
    |  6 |        2 |    3.5 |
    |  2 |        3 |    5.0 |
    |  7 |        3 |    2.7 |
    +----+----------+--------+
    
    mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
    +----+----------+--------+
    | id | category | rating |
    +----+----------+--------+
    |  1 |        1 |    4.5 |
    |  5 |        1 |    3.2 |
    |  3 |        2 |    3.7 |
    |  4 |        2 |    3.5 |
    |  6 |        2 |    3.5 |
    +----+----------+--------+

    可以看出,當(dāng)添加了id列的排序,即使category相同,也不會出現(xiàn)亂序問題。這正與我們最初的解決方案一致。

    到此,相信大家對“Mysql排序的特性是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI