传统项目消息中心的设计注意的地方--【未读消息】

今天有一条SQL占用系统负载很高,IO负载占用了30%。功能是用户登陆之后,显示未读消息的数量。由于只要点系统的功能都会查这条SQL。
导致上午4个小时调用了10万次。 
SELECT COUNT(1)
  FROM WORKBENCH_MES T1, WORKBENCH_MES_REL T2
 WHERE T1.MES_ID = T2.MES_ID
   AND T2.RECIPIENT_ID = '83DB7DD7505B4C90831717AB18881C69'
   AND T2.IS_READ = 'N' 
   AND T1.SEND_DATE >= TO_DATE('2017-08-04', 'YYYY-MM-DD')
   AND T1.SEND_DATE < TO_DATE('2018-02-04', 'YYYY-MM-DD') + 1;

 就在昨天,这条SQL执行计划改变了。由于之前消耗小,虽然执行次数多,问题也不大,执行计划从索引变成全表之后问题就来了。

-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                           |       |       |   258 (100)|          |
|   1 |  SORT AGGREGATE                       |                           |     1 |   113 |            |          |
|*  2 |   FILTER                              |                           |       |       |            |          |
|   3 |    NESTED LOOPS                       |                           |   100 | 11300 |   258   (0)| 00:00:04 |
|   4 |     NESTED LOOPS                      |                           |   100 | 11300 |   258   (0)| 00:00:04 |
|*  5 |      TABLE ACCESS BY INDEX ROWID      | WORKBENCH_MES_REL         |   100 |  6800 |    58   (0)| 00:00:01 |
|*  6 |       INDEX RANGE SCAN                | IND_WMR_RECIPIENT_ID_0905 |   128 |       |     4   (0)| 00:00:01 |
|*  7 |      INDEX UNIQUE SCAN                | PK_WORKBENCH_MES10        |     1 |       |     1   (0)| 00:00:01 |
|*  8 |     TABLE ACCESS BY GLOBAL INDEX ROWID| WORKBENCH_MES             |     1 |    45 |     2   (0)| 00:00:01 |

-------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                  | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                       |       |       |       |   161K(100)|          |
|   1 |  SORT AGGREGATE             |                       |     1 |   113 |       |            |          |
|*  2 |   FILTER                    |                       |       |       |       |            |          |
|*  3 |    HASH JOIN                |                       |   122K|    13M|  9544K|   161K  (1)| 00:32:17 |
|*  4 |     TABLE ACCESS FULL       | WORKBENCH_MES_REL     |   122K|  8111K|       |  46750  (1)| 00:09:21 |
|   5 |     PARTITION RANGE ITERATOR|                       |  2614K|   112M|       |   107K  (1)| 00:21:25 |
|*  6 |      TABLE ACCESS FULL      | WORKBENCH_MES         |  2614K|   112M|       |   107K  (1)| 00:21:25 |

-------------------------------------------------------------------------------------------------------------

开始诊断:
1.第一感觉就是两张表的索引是不是丢了。因为RECIPIENT_ID选择性还是不错的。检查了表,索引都在。
2.select count(1) from WORKBENCH_MES_REL where  RECIPIENT_ID = '83DB7DD7505B4C90831717AB18881C69';
  select count(1) from WORKBENCH_MES_REL where  RECIPIENT_ID = '83DB7DD7505B4C90831717AB18881C69' and IS_READ = 'N';
  查询这两条SQL结果是一样的,说明用户基本上是不读消息的。
3.问题找到了。是用户基本上不看未读消息,导致数据越来越多,在选择走索引还是全表的评估上,CBO评估之前数据量少就走了索引
随着数据量增大,CBO更倾向于走全表

CBO是Cost-Based Optimization的缩写,中文叫做“基于成本的优化。”
Oracle的优化器有两种优化方式,即基于规则的优化方式(Rule-Based Optimization,简称为RBO)
和基于代价的优化方式(Cost-Based Optimization,简称为CBO),
在Oracle8及以后的版本,Oracle强烈推荐用CBO的方式。



临时解决方案:
把一个月之前未读的消息都设置为已读,接收人和消息是否已读两个字段加索引,并生成直方图。
create index IND_WMR_RID_ISREAD on WORKBENCH_MES_REL(RECIPIENT_ID,IS_READ) nologging;
exec dbms_stats.gather_table_stats(user,'WORKBENCH_MES_REL',cascade => true,degree => 8,method_opt  => 'FOR ALL COLUMNS SIZE SKEWONLY FOR COLUMNS (RECIPIENT_ID,IS_READ)',no_invalidate=>FALSE);

深层次的思考:
此功能虽然做了,显然用户没有使用,有大量的用户没有查看未读消息的习惯。请问你会读5年前未读的消息吗
设计上需要优化的:
1.只保留最近1年的消息,因为5年时间已经累计了几千万的消息,查这个表没有加条件,很容易出性能问题。
2.一个月之前未读的消息,默认标记它为已读,因为失去了实效性。
3.用户点菜单就会查询这条SQL,改为把这个查询结果放到用户的session里面,会话有效期间不用再去查数据库,当然这也牺牲了实效性。这么做的原因是绝大部分用户没有看未读消息的习惯,从分析数据所得。
————————————————
版权声明:本文为CSDN博主「深圳gg」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/stevendbaguo/article/details/79287942

猜你喜欢

转载自www.cnblogs.com/softidea/p/11460624.html