@TOC
目前有一個sql,需要查出某個客戶的前100次消費記錄,訂單表有幾億資料,且只有主鍵id自增索引,不能改造表結構。如何解決?需要從資料庫層面與非資料途徑兩方面給出兩種解決方案。sql如下:
select * from order where user_id = 1 order by created_time desc limit 100
1.、 第一步優化:
將order by created_time替換為order by desc,原因為只有id有索引。
看一下區別:
看一下 order by created_time
可以看到題目給的sql是超級慢的,全表所描+檔案排序,上億級別資料幾分鐘都執行不完。
看一下 order by id desc
可以看出是使用到索引的,且沒有file sort,一定要避免file sort,超級慢的原因之一就是檔案排序
2、 第二步優化:
直接使用where user_id = 112233 order by id desc limit 10,雖然比原sql強,但由於使用了排序,依然會慢。
這時我們可以考慮利用id主鍵索引,從後往前查,先查到這個使用者的最後一筆訂單的id,然後使用where id < xxx and user_id = 112233。
3、 試一下效果:
找了一張300萬資料的表:
原sql處理速度 where user_id = 112233 order by created_time desc limit 1:
第一次優化:
where user_id = 112233 order by id desc limit 1
使用最大的id,查出剩餘9條最新消費訂單資料WHERE id < xxx AND user_id = 112233 LIMIT 9:
1、 思路
可以使用redis的list結構實現佇列,為每個使用者實時維護前N筆消費的order_id
redis結構:key:user_id value:list結構,儲存order_id
在儲存order_id時需要注意併發的情況,多個使用者同時下單,操作redis可能導致多插,考慮使用redis事物+樂觀鎖。redis事物可以保證一致性與隔離性,但由於沒有回滾機制,不能保證原子性,可以使用樂觀鎖來彌補。
2、 虛擬碼
// cas樂觀鎖變數
boolean cas = false;
// 失敗後自旋次數
in i = 0;
// 允許自旋10次
while(i <= 10){
if(
// 使用lua指令碼,並開啟redis事務
// 開啟事物
multi
// 儲存最新的10筆訂單號
redis.call(
if (redis.call(‘llen’, user_id) >10
then redis.call(‘rpop’, user_id)
else redis.call(‘rpush’, user_id,order_id)
)
// 提交事物
exec
)為真,cas = true
}
if(cas === false){
// 異常記錄,併發太大,需要繼續調優
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結