关于xxx的慢查改进建议:

1、注意不要使用 *,查表时应具体指明列名

2、避免使用子查询

3、联表/分步查询时,先将范围缩小

4、可以在应用内存中完成的运算,不要交给数据库

这是两个比较急需解决的,后续下次再沟通

一、

select

t.*,

CASE c.card_type

WHEN 'debit' THEN 'xxx'

WHEN 'prepaid' THEN 'xxx'

WHEN 'credit' THEN 'xxx'

WHEN 'semiCredit' THEN 'xxx' END 'card_type',

c.cardbin_name, c.issuer_name, a.name acquirer_name, m.merchant_name,am.merchant_name

from ((((

trans_history t

join dict_cardbin c on t.cardbin_id = c.id )

left join cm_merchant m on t.merchant_id=m.id)

left join acquirer a on t.acquirer_id = a.id )

left join acq_merchant am on t.acq_merchant_id = am.id )

left join agency ag on m.agency_id = ag.id

order by t.id desc

limit 0, 20;

trans_history这个表非常大,有5G, 整表扫描代价也是非常大的。可否根据这个表的访问需求,将老数据分出来一部分。


其他几个表比较小。但是,这个查询只需要trans_history中最新的20条id,而联表动作会将整表连接,再排序。所需的时间代价是整扫trans_history的基础上再翻数倍,类似于O(M*N*T...)


dict_cardbin、cm_merchant、acquirer、acq_merchant、agency的id均为主键的情况下,我们每个表只检索20条记录是非常快的。相当于O(M+N+T...)


建议改为:


第一步查主表,20条记录

sql = select id, col1, col2,..., cardbin_id, merchant_id, acquirer_id, acquirer_id, acq_merchant_id from trans_history order by id desc limit 20;

rowset = sql_query(sql)

组合下一步查询所需id字符串

cardbin_id_str = ',',join(str(row.cardbin_id) for row in rowset) //形式为 "id1, id2, id3...., id20"

第二步查分支表:

cardbin_sql = select id, card_type, cardbin_name, issuer_name from dict_cardbin where id in ($cardbin_id_st)

cardbin_rowset = sql_query(cardbin_sql)

依次类推

card_type比较少,直接放在程序内存

card_type_map = {"debit":"借记卡", ...}


最终结果:

for row in rowset:

     print row.id, row.col1, row.col2..., card_type_map[cardbin_rowset[id].card_type], cardbin_rowset[id].cardbin_name, cardbin_rowset[id].issuer_name, acquirer_rowset[id].name, cm_merchant_rowset[id].merchant_name , acq_merchant_rowset[id].merchant_name

二、

SELECT temp.id, temp.acq_merchant_id, temp.enabled, temp.batch_no, temp.trace_no,temp.terminal_no,temp.merchant_no

from (

SELECT c.*,b.merchant_no,ks.key_alias

from acq_merchant_terminal c

     LEFT JOIN acq_merchant b on b.id=c.acq_merchant_id

     JOIN acquirer a on a.id=b.acquirer_id

     LEFT JOIN key_store ks on ks.key_alias = CONCAT(%s,c.terminal_no,%s) OR ks.key_alias = CONCAT(%s,c.terminal_no,%s) 

WHERE a.enabled=%n and b.enabled=%n and and a.`code`= %s) temp

WHERE temp.key_alias is NULL;

这几个表数据量都不算太大,可以联表

我们在联表/分步时,应注意先使用限制条件,将检索范围缩小,因此,将限制主表行数的条件提前。

select c.id, c.acq_merchant_id, c.enabled , c.batch_no c.trace_no, c.terminal_no, b.merchant_no, b.key_alias //注意不要使用*,明确列出列名

from acq_merchant_terminal c

     left join acq_merchant b on b.id=c.acq_merchant_id

     join acquirer a on a.id=b.acquirer_id

     left join key_store ks on ks.key_alias in (concat(%s,c.terminal_no,%s)) //为什么原sql写了两遍这个条件?注意应尽量避免使用OR连接检索条件,如果有多个值,使用 in (值列表)

where c.enabled=%n and a.enabled=%n and a.code= %s and b.enabled=%n

mysql> select count(c.id) from acq_merchant_terminal c left join acq_merchant b on b.id=c.acq_merchant_id  join acquirer a on a.id=b.acquirer_id left join key_store ks on ks.key_alias in ("acq.ryxpay.88779g648160001.79g60001.zmk")  where c.enabled=1 and a.enabled=1 and a.code= "ryxpay" and b.enabled=1;

+-------------+

| count(c.id) |

+-------------+

|       90617 |

+-------------+

1 row in set (0.33 sec)

外层检索逻辑key_alias是否为null,可用程序完成:

for row in rowset:

     if row.key_alias != NULL:

          ......



--同事写的。