一.問題背景
最近在開發(fā)項(xiàng)目的時候禾锤,遇到了一個比較棘手的排序問題,在我的訂單列表中查看我的訂單摹察,產(chǎn)品要求按照先按照等級進(jìn)行排序恩掷,等級相同的情況下,按照時間倒序進(jìn)行排序供嚎。
假設(shè)表數(shù)據(jù)部分字段如下:
id,pin,type,cerat_time
其中pin上建了索引黄娘,type表示訂單的類型(已提交,已使用克滴,已取消逼争,已退款)等,暫定這四種狀態(tài)劝赔∈慕梗看到這里,大家肯定想着帽,按照簡單的排序不久行了杂伟,如下:
select id,type,create_time where pin = #{pin} order by type ,create_time desc
對吧移层,很簡單吧。但是需求是已提交=已使用>已取消=已退款稿壁,并且訂單創(chuàng)建的時間并不是順序的幽钢,是打亂的,這時候應(yīng)該怎么辦傅是?
二匪燕、解決辦法
1 集合排序
首先想到的就是绍绘,全部都查出來桂塞,然后在JVM內(nèi)存里面進(jìn)行集合排序操作候生,看起來很符合邏輯肠牲,但是深入一思考齿诞,因?yàn)檫@個涉及到數(shù)據(jù)庫的分頁操作迟隅,如果這么做啼止,你需要每次都把數(shù)據(jù)都取出來放在內(nèi)存中操作卸勺,然后在進(jìn)行集合截取浆劲,有點(diǎn)太麻煩了嫌术,并且,這種方法隨著數(shù)據(jù)量的增加牌借,性能消耗非常大度气,所以這種方案不合適。
2 添加字段
在數(shù)據(jù)表中加一個字段膨报,字段表示業(yè)務(wù)規(guī)定的級別磷籍,然后利用數(shù)據(jù)庫的排序語句就行,其實(shí)這種方案短期來說现柠,應(yīng)該算合適的方案院领,并且隨著數(shù)據(jù)量的增大,在性能方面也還可以扛得住够吩,但是唯一的缺點(diǎn)就是拓展性比較差比然,如果產(chǎn)品給你重新定一種順序你就傻了,除非你重新刷歷史數(shù)據(jù)废恋,更新你這個字段的值谈秫,刷數(shù)據(jù)有點(diǎn)小麻煩,但是這種方案是可行的哈鱼鼓。
3 sql語句解決
第二種方法相當(dāng)于采取了一種曲線的方式去解決拟烫,那么能不能用sql去直接按照這種規(guī)則去排序呢,雖然說看起來有點(diǎn)復(fù)雜迄本,但肯定是有的硕淑,它就是 CASE WHEN
CASE WHEN type in (1,2)
THEN 1
WHEN type in (3,4)
THEN 2
ELSE 3 END type_copy
這SQL語句的意思就是什么呢,當(dāng)type為1和2的時候,認(rèn)為是1等級置媳,當(dāng)type為3和4的時候于樟,認(rèn)為是2等級,然后按照這個進(jìn)行排序不久可以了嘛拇囊,如果需求要換迂曲,你把這個表達(dá)式給改一下不就得了嘛,對吧寥袭。
CASE WHEN THEN 簡單點(diǎn)說路捧,就像單于if else語句嘛,不同的情況進(jìn)行不同的賦值传黄,那么完整的SQL語句如下:
SELECT * FROM
(SELECT id,type,create_time,
CASE WHEN type in (1,2)
THEN 1
WHEN type in (3,4)
THEN 2
ELSE 3 END type_copy
from table_name)
where pin=#{pin} order by type_copy,create_time desc limit 0,10;
三 問題延伸
但是杰扫,這還是有個問題,如果數(shù)據(jù)量超過千萬膘掰,這會不會造成數(shù)據(jù)庫慢查章姓,形成性能瓶頸,并且這種直接面向客戶的話识埋,如果高并發(fā)凡伊,直接撈庫肯定是不行,容易直接崩(目前項(xiàng)目數(shù)據(jù)量和并發(fā)不大窒舟,方案并不考慮這個方面)窗声,這種情況,放在Redis中把辜纲,也不是太合適,存起來不太好存拦耐。
最后考慮耕腾,可以把數(shù)據(jù)放在ES進(jìn)行操作,利用ES的聚合能力進(jìn)行排序杀糯,應(yīng)該很容易的解決扫俺,
但是這還得考慮數(shù)據(jù)量大小,如果數(shù)據(jù)量幾十萬左右還是不要用ES固翰,性能和Mysql差不太多狼纬,還麻煩,當(dāng)數(shù)據(jù)量達(dá)到百萬級別的規(guī)模上骂际,就需要考慮ES了疗琉!