回答

收藏

如何优化此MySQL查询?数百万行

技术问答 技术问答 225 人阅读 | 0 人回复 | 2023-09-14

我有以下查询:
9 X# i6 y+ e* p( NSELECT , i8 i+ f; t$ f0 ^$ ~- y8 G
    analytics.source AS referrer, - p1 H% ]% h. `' Z4 R6 Y( m
    COUNT(analytics.id) AS frequency, , z0 z# G/ V: U( U4 d" C: r
    SUM(IF(transactions.status = 'COMPLETED', 1, 0)) AS sales  C! \7 u. i% W6 n& g
FROM analytics
9 l! \2 G8 d# u2 @1 ^' H9 ]1 RLEFT JOIN transactions ON analytics.id = transactions.analytics
; Y( V0 B2 T' t" ZWHERE analytics.user_id = 52094 . N; g5 u% b- E3 o# z* ~0 L6 l2 _# b
GROUP BY analytics.source 8 U8 o! G! |" M# W4 q
ORDER BY frequency DESC
, y6 A0 @* g+ b5 H3 f" p8 eLIMIT 10
  v* y: l) U/ Q6 P/ v" w分析表有6000万行,而交易表有3M行。# k; C: \; }7 f. n: Q
EXPLAIN在此查询上运行时,我得到:- Q( r# _6 N. j8 |# W2 c
+------+--------------+-----------------+--------+---------------------+-------------------+----------------------+---------------------------+----------+-----------+-------------------------------------------------+2 _$ j$ E, M7 `. X: ?& s
| # id |  select_type |      table      |  type  |    possible_keys    |        key        |        key_len       |            ref            |   rows   |   Extra   |                                                 |
# A/ S% e. e! j3 c3 n! m4 X, c+------+--------------+-----------------+--------+---------------------+-------------------+----------------------+---------------------------+----------+-----------+-------------------------------------------------+
/ Y. G, N  x! B$ `3 T5 ^| '1'  |  'SIMPLE'    |  'analytics'    |  'ref' |  'analytics_user_id | analytics_source' |  'analytics_user_id' |  '5'                      |  'const' |  '337662' |  'Using where; Using temporary; Using filesort' |
! `! u% ?- U5 Y  i* e+ e| '1'  |  'SIMPLE'    |  'transactions' |  'ref' |  'tran_analytics'   |  'tran_analytics' |  '5'                 |  'dijishop2.analytics.id' |  '1'     |  NULL     |                                                 |
" V* V( m6 h$ i4 z4 ]0 I- B+------+--------------+-----------------+--------+---------------------+-------------------+----------------------+---------------------------+----------+-----------+-------------------------------------------------+1 |; K% V, \. y* c( A
我已经不知道如何优化此查询了,因为它已经非常基础了。运行此查询大约需要70秒钟。+ ~% F; u" A0 J1 H) g$ ?8 f' l
以下是存在的索引:
; W. e* y+ j! q# r' Q+-------------+-------------+----------------------------+---------------+------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+
9 B5 c) ]" `, H* v9 A! c  k|   # Table   |  Non_unique |          Key_name          |  Seq_in_index |    Column_name   |  Collation |  Cardinality |  Sub_part |  Packed |  Null  |  Index_type |  Comment |  Index_comment |0 Z* A6 G  q, T
+-------------+-------------+----------------------------+---------------+------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+
2 ~0 ~6 F7 o- {8 e8 {2 U7 G  B| 'analytics' |  '0'        |  'PRIMARY'                 |  '1'          |  'id'            |  'A'       |  '56934235'  |  NULL     |  NULL   |  ''    |  'BTREE'    |  ''      |  ''            |, ~  b$ k9 k& s2 P$ ?. t; a
| 'analytics' |  '1'        |  'analytics_user_id'       |  '1'          |  'user_id'       |  'A'       |  '130583'    |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
- h) x# s8 }: M4 b5 P5 E$ @| 'analytics' |  '1'        |  'analytics_product_id'    |  '1'          |  'product_id'    |  'A'       |  '490812'    |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
( d/ K+ f/ w, L, o1 x| 'analytics' |  '1'        |  'analytics_affil_user_id' |  '1'          |  'affil_user_id' |  'A'       |  '55222'     |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |/ ^, x4 B9 x' v# G
| 'analytics' |  '1'        |  'analytics_source'        |  '1'          |  'source'        |  'A'       |  '24604'     |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |" \9 K7 ?$ w+ U: g/ v
| 'analytics' |  '1'        |  'analytics_country_name'  |  '1'          |  'country_name'  |  'A'       |  '39510'     |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
7 d, a6 k1 G/ B2 N. J2 `| 'analytics' |  '1'        |  'analytics_gordon'        |  '1'          |  'id'            |  'A'       |  '56934235'  |  NULL     |  NULL   |  ''    |  'BTREE'    |  ''      |  ''            |
& U( [+ ~* _8 R1 J| 'analytics' |  '1'        |  'analytics_gordon'        |  '2'          |  'user_id'       |  'A'       |  '56934235'  |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
( L0 v5 ]9 L) e/ c4 {7 x% E; @+ L- N| 'analytics' |  '1'        |  'analytics_gordon'        |  '3'          |  'source'        |  'A'       |  '56934235'  |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |8 a& a' l. I4 _; V0 U
+-------------+-------------+----------------------------+---------------+------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+
5 h3 d2 \* m$ Y: V1 t& ~7 @$ c$ `% P0 U# b3 G1 D/ W8 U
+----------------+-------------+-------------------+---------------+-------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+: {* Q2 A' O+ ?; y  J" X" r. _4 G  m. e" |
|    # Table     |  Non_unique |      Key_name     |  Seq_in_index |    Column_name    |  Collation |  Cardinality |  Sub_part |  Packed |  Null  |  Index_type |  Comment |  Index_comment |
9 ]& M- w% i3 L$ I8 E; S, t+----------------+-------------+-------------------+---------------+-------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+7 [- m* I4 S- Q; f
| 'transactions' |  '0'        |  'PRIMARY'        |  '1'          |  'id'             |  'A'       |  '2436151'   |  NULL     |  NULL   |  ''    |  'BTREE'    |  ''      |  ''            |
" b1 e9 `8 A" W' u/ @, T' T' w0 e) C| 'transactions' |  '1'        |  'tran_user_id'   |  '1'          |  'user_id'        |  'A'       |  '56654'     |  NULL     |  NULL   |  ''    |  'BTREE'    |  ''      |  ''            |$ g/ J& B& p  E
| 'transactions' |  '1'        |  'transaction_id' |  '1'          |  'transaction_id' |  'A'       |  '2436151'   |  '191'    |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
; G) d- E  H# l# B| 'transactions' |  '1'        |  'tran_analytics' |  '1'          |  'analytics'      |  'A'       |  '2436151'   |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |& K2 J: B$ U" R5 @: X! N3 w' r5 \4 o
| 'transactions' |  '1'        |  'tran_status'    |  '1'          |  'status'         |  'A'       |  '22'        |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |# J7 v6 L& B- b5 F% S
| 'transactions' |  '1'        |  'gordon_trans'   |  '1'          |  'status'         |  'A'       |  '22'        |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |- M! m: L- E" R7 Y; V
| 'transactions' |  '1'        |  'gordon_trans'   |  '2'          |  'analytics'      |  'A'       |  '2436151'   |  NULL     |  NULL   |  'YES' |  'BTREE'    |  ''      |  ''            |
* P9 I% e& K4 e) d: a. }+----------------+-------------+-------------------+---------------+-------------------+------------+--------------+-----------+---------+--------+-------------+----------+----------------+
7 m* ~- z# M' {1 U根据建议,在添加任何额外索引之前简化了两个表的架构,因为这并不能改善情况。
/ C8 L1 w+ J- Z: H. W9 T2 f9 ACREATE TABLE `analytics` (# ~  P1 }, k0 Z# X
  `id` int(11) NOT NULL AUTO_INCREMENT,
8 z. b# ~7 c9 E! J, p' y6 g  `user_id` int(11) DEFAULT NULL,( k7 M+ [. k- l6 Q$ Q/ m
  `affil_user_id` int(11) DEFAULT NULL,
9 X* a- \. n5 w" e! h  `product_id` int(11) DEFAULT NULL,
. Y' h/ J# B2 C+ S2 ^  `medium` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,0 {' O" n  s) ?, I
  `source` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
1 T9 K' R  j) h$ f/ D  `terms` varchar(1024) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
7 c  P. x* `) W  ^8 T  `is_browser` tinyint(1) DEFAULT NULL,5 G. ?  _2 c# R
  `is_mobile` tinyint(1) DEFAULT NULL,
1 i$ r: h9 F; x2 T" r* f  `is_robot` tinyint(1) DEFAULT NULL,) S* r+ h  `, R) t
  `browser` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,+ A* \2 \" Y0 Z
  `mobile` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
* r, U5 E1 M6 q6 ?  N2 [# }7 f  `robot` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,: y, L; n; X8 v
  `platform` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
% w( y2 D6 x4 \) z+ h  `referrer` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,6 W( H" E) C+ D9 o2 t/ [$ T
  `domain` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/ J" m5 \0 h6 j7 |! o9 j  `ip` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,) H& K; v  p9 o7 C3 v
  `continent_code` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,9 M9 C8 z7 S, p1 M# x  D
  `country_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
! w+ g/ }2 f1 e  `city` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,9 O' m4 K/ k9 E& Z+ S( O; Q6 _
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
5 u; v* f" u/ `2 b  PRIMARY KEY (`id`),# V+ E( M( T; n/ h
  KEY `analytics_user_id` (`user_id`),& y) I. Z% j  G8 L+ f, n( f
  KEY `analytics_product_id` (`product_id`),
" D) ?: E2 J! o5 J9 R8 J1 {  KEY `analytics_affil_user_id` (`affil_user_id`)/ z! [2 d# O1 A, U' C5 |. ?8 U% J
) ENGINE=InnoDB AUTO_INCREMENT=64821325 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;2 v. a9 Z; s: K) h
CREATE TABLE `transactions` (
' S. W( Y  z. X. {0 F  `id` int(11) NOT NULL AUTO_INCREMENT,/ f9 m& y1 J* R4 h, h6 h4 Q
  `transaction_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
3 T/ Y4 x4 a6 X# L1 B  `user_id` int(11) NOT NULL,4 N8 T+ A& l+ t2 @1 x# l" ]$ |
  `pay_key` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,9 R% k! q" F( O, {& N8 v
  `sender_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
$ J$ V* r0 K8 ~2 a3 S4 e1 q  `amount` decimal(10,2) DEFAULT NULL,
' F5 j; b* b6 A' o8 ]  `currency` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
7 v' s0 G8 r. K) S2 L  `status` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
5 u3 t  J3 c: `; r# J1 v/ H  `analytics` int(11) DEFAULT NULL,
7 V. @, D  [7 `" H- ^4 E0 A+ n  `ip_address` varchar(46) COLLATE utf8mb4_unicode_ci DEFAULT NULL,! j) i# Y/ a3 ?: B4 X( `
  `session_id` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
( q" q! ~+ v' t5 q  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
* Q* K6 q/ ~4 L0 A  `eu_vat_applied` int(1) DEFAULT '0',
. v+ U0 u3 m: @% R* b: o9 B* z  PRIMARY KEY (`id`),
1 {1 V% Y! t- z. f  KEY `tran_user_id` (`user_id`),
* Q8 p+ G- F/ u5 K! t1 T  KEY `transaction_id` (`transaction_id`(191)),: r, ?' L' q; r% i& T* W( N
  KEY `tran_analytics` (`analytics`),
+ A* \% m! W1 v9 ]  KEY `tran_status` (`status`)1 \3 C# ]0 C7 N
) ENGINE=InnoDB AUTO_INCREMENT=10019356 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;" o* H; n/ ~1 x/ S) I9 W( ]
如果以上无法进一步优化。关于汇总表的任何实施建议都将非常有用。我们正在AWS上使用LAMP堆栈。上面的查询正在RDS(m1.large)上运行。9 |- Z: X6 ~3 g3 z2 p& R/ W
                $ W/ L7 E9 e) ]- x" J& V5 s
解决方案:
4 n6 L/ h7 i8 `( z! L9 B               
: V3 {0 Q4 f& p9 Z" b1 N5 M1 X7 H0 r0 `  H* {, K/ K8 ^& {
' C5 [( Z( n. H: m( Y
                我将创建以下索引(b树索引):
, k/ T: t$ l6 U2 p' Z) Sanalytics(user_id, source, id)
% a, b' {% J( Q$ e" Ctransactions(analytics, status)
$ e9 ?- `* ^. r2 \/ m这与戈登的建议不同。
& h  v1 B; |2 ^; M, \索引中列的顺序很重要。
( G# i! M. i* D& g您要按特定条件进行过滤analytics.user_id,因此该字段必须是索引中的第一个字段。然后,您按分组analytics.source。为避免source以此排序,索引的下一个字段应该是。您还参考analytics.id,因此最好将此字段作为索引的一部分,放在最后。MySQL是否能够只读取索引而不接触表?我不知道,但是测试起来很容易。. x$ X7 z' }8 f9 W/ i' A7 ^' t
索引transactions必须以开头analytics,因为它会在中使用JOIN。我们还需要status。! y4 R" Q. s9 t) D0 I9 z
SELECT : {  e3 {$ |) n- k- P! p$ z# p9 a7 c
    analytics.source AS referrer,
- m+ w/ x* }, t) y. u    COUNT(analytics.id) AS frequency, 1 ]. g+ n/ Q1 D) [' E- b, H
    SUM(IF(transactions.status = 'COMPLETED', 1, 0)) AS sales
( D- g- }7 W& s. WFROM analytics
8 u. G! `# z" c. O: k( ZLEFT JOIN transactions ON analytics.id = transactions.analytics
) f1 @0 o6 t: UWHERE analytics.user_id = 52094 ; E3 e' \# L- J$ d, q5 J
GROUP BY analytics.source % F2 C3 W2 V: p# P( h: ], S1 B4 r
ORDER BY frequency DESC # A, ~; W8 z5 x0 n  _- X  l
LIMIT 10
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则