自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

RabbitMQ處理過慢,原來是一個SQL緩存框架導(dǎo)致的GC頻繁觸發(fā)

存儲 存儲軟件
處理過慢原因有很多,可能是干活的人少了,也可能這些人摸魚去了,總之工作不飽和,接下來看看這幫人有沒有摸魚,從 線程池 看起。

[[434658]]

一、背景

1. 講故事

上個月底,有位朋友微信找到我,說他的程序 多線程處理 RabbitMQ 時過慢,幫忙分析下什么原因,截圖如下:

這問題拋出來,有點懵逼,沒說CPU爆高,也沒說內(nèi)存泄漏,也沒說程序卡死。。。鬼知道為啥 Rabbitmq 處理過慢哈?????? 

既然沒有一個緣由,那就往 freeze 這個方向去找吧,上 windbg 說話。

二、Windbg 分析

1. 尋找 freeze 原因

處理過慢原因有很多,可能是干活的人少了,也可能這些人摸魚去了,總之工作不飽和,接下來看看這幫人有沒有摸魚,從 線程池 看起。

  1. 0:000> !tp 
  2. CPU utilization: 81% 
  3. Worker Thread: Total: 187 Running: 172 Idle: 0 MaxLimit: 32767 MinLimit: 8 
  4. Work Request in Queue: 0 
  5. -------------------------------------- 
  6. Number of Timers: 0 
  7. -------------------------------------- 
  8. Completion Port Thread:Total: 1 Free: 1 MaxFree: 16 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 8 

從輸出中看,當(dāng)前的 CPU=81%, 大概率是這個程序引起的,既然 CPU 過高,可能是某些計算密集型操作,可能是大量鎖導(dǎo)致的上下文切換,也可能是 GC 頻繁觸發(fā)所致。

2. 是 GC 觸發(fā)嗎?

本系列到這里,我相信有很多朋友應(yīng)該知道如何從線程棧上看是否為 GC 觸發(fā)所致,用命令 ~*e !dumpstack 經(jīng)過一通尋找,發(fā)現(xiàn)是 214 號線程觸發(fā)了 GC,如下圖所示:

用 !dumpstack 輸出的信息太多,為了讓信息更加簡化,改用 !clrstack。

  1. 0:214> !clrstack  
  2. OS Thread Id: 0x290c (214) 
  3.         Child SP               IP Call Site 
  4. 000000bb2983c7d0 00007ffed8c3e335 System.Text.UTF8Encoding.GetString(Byte[], Int32, Int32) 
  5. 000000bb2983c810 00007ffe7aaecee9 StackExchange.Redis.RedisValue.op_Implicit(StackExchange.Redis.RedisValue) 
  6. 000000bb2983c860 00007ffe7aaecd92 xxxx.Util.Helper.RedisHelper.ConvertObj[[System.__Canon, mscorlib]](StackExchange.Redis.RedisValue) 
  7. 000000bb2983c8f0 00007ffe7abc3997 xxxx.Util.Helper.RedisHelper+c__DisplayClass29_0`1[[System.__Canon, mscorlib]].b__0(StackExchange.Redis.IDatabase) 
  8. 000000bb2983ca20 00007ffe7aaeaca0 xxxx.Util.Helper.RedisHelper.Do[[System.__Canon, mscorlib]](System.Func`2<StackExchange.Redis.IDatabase,System.__Canon>) 
  9. 000000bb2983caa0 00007ffe7abc3762 xxxx.Util.Helper.RedisHelper.HashGet[[System.__Canon, mscorlib]](System.String, System.String) 
  10. 000000bb2983cb40 00007ffe7abc2f3d xxxx.Implementation.xxx.GetCompany(System.String) 
  11. ... 
  12. 000000bb2983e170 00007ffe7ad2fadf xxx.xxx.MQ.xxx+c__DisplayClass21_0`1[[System.__Canon, mscorlib]].b__1() 
  13. 000000bb2983e1c0 00007ffed8c11862 System.Threading.Tasks.Task`1[[System.__Canon, mscorlib]].InnerInvoke() 
  14. 000000bb2983e220 00007ffed8c10937 System.Threading.Tasks.Task.Execute() 
  15. 000000bb2983e260 00007ffed8bc674e System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
  16. 000000bb2983e330 00007ffed8bc65e7 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
  17. 000000bb2983e360 00007ffed8c10bdd System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) 
  18. 000000bb2983e410 00007ffed8c10303 System.Threading.Tasks.Task.ExecuteEntry(Boolean) 
  19. 000000bb2983e450 00007ffed8bcfa10 System.Threading.ThreadPoolWorkQueue.Dispatch() 
  20. 000000bb2983e8e8 00007ffed9d465d3 [DebuggerU2MCatchHandlerFrame: 000000bb2983e8e8]  

從卦中信息看,程序在 RedisHelper.HashGet 操作的過程中觸發(fā)了 GC,我去,難道是從 redis 中讀了什么大數(shù)據(jù)?接下來用 !clrstack -a 看看到底從redis中讀了個啥?

  1. 0:214> !clrstack -a 
  2. OS Thread Id: 0x290c (214) 
  3.         Child SP               IP Call Site 
  4. 000000bb2983c860 00007ffe7aaecd92 xxx.Util.Helper.RedisHelper.ConvertObj[[System.__Canon, mscorlib]](StackExchange.Redis.RedisValue) 
  5.     PARAMETERS: 
  6.         this (0x000000bb2983c8f0) = 0x000000bb182da3d0 
  7.         value (0x000000bb2983c960) = 0x000000bb18440778 
  8.     LOCALS: 
  9.         0x000000bb2983c8c4 = 0x0000000000000000 
  10.         0x000000bb2983c8a0 = 0x0000000000000000 
  11.         0x000000bb2983c898 = 0x0000000000000000 
  12.  
  13. 0:214> !do 0x000000bb18440778 
  14. Name:        System.Byte[] 
  15. MethodTable: 00007ffed8db93d0 
  16. EEClass:     00007ffed87f4dc8 
  17. Size:        6679(0x1a17) bytes 
  18. Array:       Rank 1, Number of elements 6655, Type Byte (Print Array) 
  19. Content:     {"ID":104x,"Code":"130x","xxx":"1304","xxx":"8888","Name":"...............","xxx":"...............","MqConnStr" 

可以看到,redis 讀了大概 6.6 k 的數(shù)據(jù),才這么點數(shù)據(jù),我還以為 幾十M 呢 ??????, 要說這點東西就能觸發(fā)GC,我是不相信的 。

3. 到底是什么誘使了 GC 觸發(fā) ?

那這個 罪魁禍?zhǔn)?到底是誰呢?其實仔細(xì)想一想,GC觸發(fā)無非就是將對象從 gen0 推到 gen1,然后由 gen1 推到 gen2,那朋友的這個 RabbitMQ 處理慢,肯定是觸發(fā)了多次GC,才有了這個肉眼可見的處理慢。

這里就有一個突破點,既然觸發(fā)多次GC,那gen2上肯定有很多帶根的對象,所以從這里入手比較好,用 !dumpgen 命令查看。

  1. 0:214> !dumpgen 2 -stat 
  2.        Count      Total Size      Type 
  3. ------------------------------------------------- 
  4.       87,738     24,925,316   System.Int32[] 
  5.      509,643     56,399,716   System.String 
  6.       83,077     65,760,888   System.Reflection.Emit.__FixupData[] 
  7.    2,158,346     69,067,072   System.Reflection.Emit.GenericMethodInfo 
  8.       92,388    172,765,392   System.Object[] 
  9.      796,870    179,587,796   **** FREE **** 
  10.   14,030,441    336,730,584   System.RuntimeMethodHandle 
  11.      428,376    348,743,274   System.Byte[] 

我去,gen2 上居然有 1.4 kw 的對象,這太讓人好奇了,接下來隨便抽幾個 address ,看看它的引用根是個啥?

  1. 0:214> !dumpgen 2 -type System.RuntimeMethodHandle 
  2. Object             MT                    Size   Name 
  3. ------------------------------------------------------------------- 
  4. 000000bac9f8fe68   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  5. 000000bac9f8fe80   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  6. 000000bac9f8fe98   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  7. 000000bac9f8feb0   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  8. 000000bac9f8fec8   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  9. ... 
  10. 000000baca1af510   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  11. 000000baca1af548   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  12. 000000baca1af560   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  13. 000000baca1afd90   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  14. 000000baca1afde0   00007FFED8DB9978        24   System.RuntimeMethodHandle 
  15.  
  16. 0:214> !gcroot 000000baca1afde0 
  17. Thread 187c0: 
  18.     000000baeeb5cb30 00007ffed8ba9c60 System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon) 
  19.         rsi:  
  20.             ->  000000baca4c6ef0 System.Collections.Generic.Dictionary`2[[System.Tuple`4[[System.String, mscorlib],[System.String, mscorlib],[System.Int32, mscorlib],[System.Int32, mscorlib]], mscorlib],[System.Delegate, mscorlib]] 
  21.             ->  000000badab3b8a0 System.Collections.Generic.Dictionary`2+Entry[[System.Tuple`4[[System.String, mscorlib],[System.String, mscorlib],[System.Int32, mscorlib],[System.Int32, mscorlib]], mscorlib],[System.Delegate, mscorlib]][] 
  22.             ->  000000baca1b5210 System.Func`2[[System.Data.IDataReader, System.Data],[xxx.BAR_AccountDC, xxxx]] 
  23.             ->  000000baca1ae8b0 System.Reflection.Emit.DynamicMethod+RTDynamicMethod 
  24.             ->  000000baca1ae7f8 System.Reflection.Emit.DynamicMethod 
  25.             ->  000000baca1b51b0 System.RuntimeMethodInfoStub 
  26.             ->  000000baca1b4a18 System.Reflection.Emit.DynamicResolver 
  27.             ->  000000baca1aea60 System.Reflection.Emit.DynamicScope 
  28.             ->  000000baca1aea78 System.Collections.Generic.List`1[[System.Object, mscorlib]] 
  29.             ->  000000baca1b3b20 System.Object[] 
  30.             ->  000000baca1afde0 System.RuntimeMethodHandle 

從引用鏈看,它是被 Dictionary 所持有,那我們就 dump 一下這個 dictionary,看看是個啥。

  1. 0:214> !do 000000baca4c6ef0 
  2. Name:        System.Collections.Generic.Dictionary`2[[System.Tuple`4[[System.String, mscorlib],[System.String, mscorlib],[System.Int32, mscorlib],[System.Int32, mscorlib]], mscorlib],[System.Delegate, mscorlib]] 
  3. MethodTable: 00007ffe7a937f18 
  4. EEClass:     00007ffed87f7cb8 
  5. Size:        80(0x50) bytes 
  6. File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll 
  7. Fields: 
  8.               MT    Field   Offset                 Type VT     Attr            Value Name 
  9. 00007ffed8dc3e30  400182a        8       System.Int32[]  0 instance 000000badaaa2c10 buckets 
  10. 00007ffed9874d38  400182b       10 ...non, mscorlib]][]  0 instance 000000badab3b8a0 entries 
  11. 00007ffed8dc3e98  400182c       38         System.Int32  1 instance            83000 count 
  12. 00007ffed8dc3e98  400182d       3c         System.Int32  1 instance            83000 version 
  13. 00007ffed8dc3e98  400182e       40         System.Int32  1 instance               -1 freeList 
  14. 00007ffed8dc3e98  400182f       44         System.Int32  1 instance                0 freeCount 
  15. 00007ffed8d9a430  4001830       18 ...Canon, mscorlib]]  0 instance 000000baca2ec958 comparer 
  16. 00007ffed8d9c550  4001831       20 ...Canon, mscorlib]]  0 instance 0000000000000000 keys 
  17. 00007ffed8dcef28  4001832       28 ...Canon, mscorlib]]  0 instance 0000000000000000 values 
  18. 00007ffed8dc1c98  4001833       30        System.Object  0 instance 0000000000000000 _syncRoot 
  19.  
  20. 0:214> !objsize 000000baca4c6ef0 
  21. sizeof(000000baca4c6ef0) = 1116325152 (0x4289c520) bytes (System.Collections.Generic.Dictionary`2[[System.Tuple`4[[System.String, mscorlib],[System.String, mscorlib],[System.Int32, mscorlib],[System.Int32, mscorlib]], mscorlib],[System.Delegate, mscorlib]]) 

可以看到,當(dāng)前dict 的 size= 8.3w,總大小為 1.1G, 然后用 !mdt 抽查一下字典內(nèi)容。

可以看出,字典中的item大概都是 key= select * from xxx where AccountNo= xxxx, value = Func ,

4. 查看源碼

從 !gcroot 中知道當(dāng)前根在 187c0 號線程,然后我們通過線程棧去尋找下源碼。

  1. 0:089> ~~[187c0]s 
  2. ntdll!NtWaitForSingleObject+0xa: 
  3. 00007ffe`e9ea06fa c3              ret 
  4. 0:089> !clrstack  
  5. OS Thread Id: 0x187c0 (89) 
  6.         Child SP               IP Call Site 
  7. 000000baeeb5caa0 00007ffed9718dfe System.Tuple`4[[System.__Canon, mscorlib],[System.__Canon, mscorlib],[System.Int32, mscorlib],[System.Int32, mscorlib]].System.Collections.IStructuralEquatable.GetHashCode(System.Collections.IEqualityComparer) 
  8. 000000baeeb5cb00 00007ffed8ba90a7 System.Collections.Generic.ObjectEqualityComparer`1[[System.__Canon, mscorlib]].GetHashCode(System.__Canon) 
  9. 000000baeeb5cb30 00007ffed8ba9c60 System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon) 
  10. 000000baeeb5cba0 00007ffed8bc53a4 System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].TryGetValue(System.__Canon, System.__Canon ByRef) 
  11. 000000baeeb5cbe0 00007ffe7abd8069 xxx.Internal.Cache`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].Get(System.__Canon, System.Func`1<System.__Canon>) 
  12. 000000baeeb5cc50 00007ffe7abd6d52 xxx.Database+d__49`1[[System.__Canon, mscorlib]].MoveNext() 
  13. 000000baeeb5ccd0 00007ffed733768e System.Linq.Enumerable.FirstOrDefault[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>) 
  14. 000000baeeb5cd40 00007ffe7af5489e xxx.xxx.ParsePapPayDebitRecord(xxx, xxx.BAR_AccountDC ByRef, System.String ByRef, xxx ByRef, Boolean, xxx, Boolean) 
  15. ... 

從線程???程序做了一個 db 查詢的操作,源碼大概如下:

  1. public static InvalidTxnCode ParsePapPayDebitRecord(xxx) 
  2.     sql = "select * FROM  [dbo].[xxx]  where AccountNO = '" + transRecord.AccountNO + "'"
  3.  IEnumerable<BAR_AccountDC> enumerable3 = new YiBll<BAR_AccountDC>(transRecord.AccountNO).QuerySql(sql); 

問題就出現(xiàn)在這個 sql 拼接上,底層的框架為了提高 mapping 速度,將 sql 和與之對應(yīng)的 Func<DataReader,Data>  做了緩存處理,由于每次sql都不一樣,這就導(dǎo)致底層的 dict越來越大,直到目前的 1.1 G。

三、總結(jié)

總的來說,本次事故是由于不斷增長的 dict 在不斷的擠壓小對象堆,加之程序多線程火力全開分配對象導(dǎo)致gc在小對象堆上的頻繁回收造成的 rabbitmq 處理過慢。

修改辦法就是將 sql 參數(shù)化。

  1. sql = "select * FROM [dbo].[xxx] where AccountNO = '" + transRecord.AccountNO + "'"

改成:

  1. sql = "select * FROM [dbo].[xxx] where AccountNO = @AccountNO";  

最后提一下,這個dump有一定的迷惑性,因為觸發(fā)GC的線程并不是罪魁禍?zhǔn)?,它只不過是壓死駱駝的那最后一根稻草罷了。

 

責(zé)任編輯:武曉燕 來源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2020-11-12 07:32:53

JavaScript

2017-04-17 11:07:19

GC數(shù)組動態(tài)擴(kuò)容

2025-02-17 09:22:16

MySQLSQL語句

2022-12-10 22:52:54

Redis緩存數(shù)據(jù)

2016-02-02 10:31:03

華三/大安全

2017-06-06 15:13:07

2022-12-14 07:32:40

InnoDBMySQL引擎

2025-03-20 14:18:57

AI算法模型

2017-06-27 08:41:04

JVM設(shè)計缺陷GC

2021-02-07 08:13:18

@DateTimeFo@NumberFormSpring

2024-02-06 09:30:25

Figma矩形矩形物理屬性

2022-05-05 08:55:12

工業(yè)物聯(lián)網(wǎng)IIoT

2023-05-22 15:58:11

2018-07-02 10:07:08

2018-04-02 15:13:21

網(wǎng)絡(luò)

2024-04-30 08:22:51

Figma圖形編輯變換矩陣

2023-02-15 08:17:38

2021-02-02 09:13:11

索引SQL數(shù)據(jù)庫

2020-05-26 08:52:36

Java JVM多態(tài)

2020-03-23 08:30:12

程序員男友感受
點贊
收藏

51CTO技術(shù)棧公眾號