没有不值得去解决的问题,也没有不值得去学习的技术!

Analyzing and Resolving 100% Redis CPU Usage at Runtime with Yii 2 ActiveRecord

Conclusion: For Yii 2's ActiveRecord, the ranking of related methods regarding Redis CPU computing pressure is: insert() < one()/exists() < count()/all(). When the number of keys in a specific Redis model exceeds 10000, if you need to perform a query (with a where condition) operation, it is recommended not to use ActiveRecord

1. At 19:36:00 on 2017-06-18, when the match started, the application response time was 51.8 ms, which is normal, as shown in Figure 1.

At 19:36:00 on 2017-06-18, when the match started, the application response time was 51.8 ms, which is normal
Figure 1

2. During the entire football match, the response time increased linearly. From 20:30:00 to 20:40:00 on 2017-06-18, the response time dropped because the match was in halftime, and the match data sent to the server remained basically unchanged, resulting in simple processing logic, as shown in Figure 2.

During the entire football match, the response time increased linearly. From 20:30:00 to 20:40:00 on 2017-06-18, the response time dropped because the match was in halftime, and the match data sent to the server remained basically unchanged, resulting in simple processing logic
Figure 2

3. At 21:25:00 on 2017-06-18, when the match was about to end, the application response time was 2320 ms. The average response time required by the business target is 150 ms, so the performance failed to meet the business requirements, as shown in Figure 3.

At 21:25:00 on 2017-06-18, when the match was about to end, the application response time was 2320 ms. The average response time required by the business target is 150 ms, so the performance failed to meet the business requirements
Figure 3

4. In the APM Transaction trace, it can be found that in a request with a response time of 5710 ms, Redis-related operations consumed 4390 ms, accounting for 77%. The httpclient called another interface, which also used Redis. Therefore, the analysis result indicates that Redis is the performance bottleneck, as shown in Figure 4.

In the APM Transaction trace, it can be found that in a request with a response time of 5710 ms, Redis-related operations consumed 4390 ms, accounting for 77%. The httpclient called another interface, which also used Redis. Therefore, the analysis result indicates that Redis is the performance bottleneck
Figure 4

5. In the Trace details of the APM Transaction trace, it was found that shujujiexi\models\redis\GamePlayerData::gamePlayerDataByShuJuJieXi accounted for 57.25% of the time consumed, as shown in Figure 5.

In the Trace details of the APM Transaction trace, it was found that shujujiexi\models\redis\GamePlayerData::gamePlayerDataByShuJuJieXi accounted for 57.25% of the time consumed
Figure 5

6. Continuing to expand until it could not be expanded further, yii\redis\Connection::parseResponse was found, and its running mechanism needs to be analyzed, as shown in Figure 6.

Continuing to expand until it could not be expanded further, yii\redis\Connection::parseResponse was found, and its running mechanism needs to be analyzed
Figure 6

7. In Alibaba Cloud monitoring, at 19:36:00 on 2017-06-18, when the match started, the Redis CPU usage was 1.9%, as shown in Figure 7.

In Alibaba Cloud monitoring, at 19:36:00 on 2017-06-18, when the match started, the Redis CPU usage was 1.9%
Figure 7

8. During the entire football match, in Alibaba Cloud monitoring, the Redis CPU usage increased linearly. From 20:30:00 to 20:40:00 on 2017-06-18, the Redis CPU usage dropped because the match was in halftime, and the match data sent to the server remained basically unchanged, resulting in simple processing logic. The entire curve is directly proportional to the response time, as shown in Figure 8.

During the entire football match, in Alibaba Cloud monitoring, the Redis CPU usage increased linearly. From 20:30:00 to 20:40:00 on 2017-06-18, the Redis CPU usage dropped because the match was in halftime, and the match data sent to the server remained basically unchanged, resulting in simple processing logic. The entire curve is directly proportional to the response time
Figure 8

9. In Alibaba Cloud monitoring, at 21:27:00 on 2017-06-18, when the match was about to end, the Redis CPU usage was 100%, as shown in Figure 9.

In Alibaba Cloud monitoring, at 21:27:00 on 2017-06-18, when the match was about to end, the Redis CPU usage was 100%
Figure 9

10. In Alibaba Cloud monitoring, at 21:28:00 on 2017-06-18, when the match was about to end, the number of used Redis connections reached a peak of 54, as shown in Figure 10.

In Alibaba Cloud monitoring, at 21:28:00 on 2017-06-18, when the match was about to end, the number of used Redis connections reached a peak of 54
Figure 10

11. Now we need to analyze what exactly caused the Redis CPU usage to reach 100%. In the APM Transaction trace, it was found that yii\redis\ActiveRecord::deleteAll accounted for 78% of the time consumed, as shown in Figure 11.

Now we need to analyze what exactly caused the Redis CPU usage to reach 100%. In the APM Transaction trace, it was found that yii\redis\ActiveRecord::deleteAll accounted for 78% of the time consumed
Figure 11

12. In the Trace details of the APM Transaction trace, it was found that Application code (in yii\redis\ActiveRecord::deleteAll) consumed the most time. It can basically be confirmed that ActiveRecord-related operations are the root cause of the Redis CPU usage reaching 100%, as shown in Figure 12.

In the Trace details of the APM Transaction trace, it was found that Application code (in yii\redis\ActiveRecord::deleteAll) consumed the most time. It can basically be confirmed that ActiveRecord-related operations are the root cause of the Redis CPU usage reaching 100%
Figure 12

13. In the production environment, we prepared to implement two versions of a typical request. The ActiveRecord-related operations in the first version are basically query and update, while the second version’s are basically query and insert. This was to confirm whether an excessive number of keys in a specific ActiveRecord model would increase the Redis CPU usage. We first cleared Redis, and then the scheduled task generated Redis data with 3170 keys, as shown in Figure 13.

In the production environment, we prepared to implement two versions of a typical request. The ActiveRecord-related operations in the first version are basically query and update, while the second version's are basically query and insert. This was to confirm whether an excessive number of keys in a specific ActiveRecord model would increase the Redis CPU usage. We first cleared Redis, and then the scheduled task generated Redis data with 3170 keys
Figure 13

14. In the production environment, after the first version of the request ran for 2 hours, the Redis data had 3220 keys, an increase of only 50 keys, as shown in Figure 14.

In the production environment, after the first version of the request ran for 2 hours, the Redis data had 3220 keys, an increase of only 50 keys
Figure 14

15. In the production environment, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 2.6%. After 16:50:00 on 2017-06-27, the Redis CPU usage maintained at 0.6% because the match data sent to the server remained basically unchanged, resulting in simple processing logic, as shown in Figure 15.

In the production environment, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 2.6%. After 16:50:00 on 2017-06-27, the Redis CPU usage maintained at 0.6% because the match data sent to the server remained basically unchanged, resulting in simple processing logic
Figure 15

16. In the production environment, during the entire execution of the typical request, the response time remained basically stable, with a maximum response time of 266 ms. The average response time required by the business target for this request in the production environment is 200 ms, so the performance meets the business requirements. After 16:50:00 on 2017-06-27, the response time dropped because the match data sent to the server remained basically unchanged, resulting in simple processing logic, as shown in Figure 16.

In the production environment, during the entire execution of the typical request, the response time remained basically stable, with a maximum response time of 266 ms. The average response time required by the business target for this request in the production environment is 200 ms, so the performance meets the business requirements. After 16:50:00 on 2017-06-27, the response time dropped because the match data sent to the server remained basically unchanged, resulting in simple processing logic
Figure 16

17. First, we cleared Redis, and then the scheduled task generated Redis data with 2909 keys, as shown in Figure 17.

First, we cleared Redis, and then the scheduled task generated Redis data with 2909 keys
Figure 17

18. In the production environment, after the second version of the request ran for 2 hours, the Redis data had 89840 keys, an increase of 86931 keys, as shown in Figure 18.

In the production environment, after the second version of the request ran for 2 hours, the Redis data had 89840 keys, an increase of 86931 keys
Figure 18

19. The number of keys in the Redis model operated on by the second version of the request was 79527, as shown in Figure 19.

The number of keys in the Redis model operated on by the second version of the request was 79527
Figure 19

20. In the production environment, for the second version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 66.8%, as shown in Figure 20.

In the production environment, for the second version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 66.8%
Figure 20

21. Based on the current analysis results, it can be confirmed that when the number of keys in a model operated on by ActiveRecord becomes too large, the Redis CPU usage will increase significantly. However, whether this is caused by queries or inserts needs further confirmation. The code is shown in Figure 21.

Based on the current analysis results, it can be confirmed that when the number of keys in a model operated on by ActiveRecord becomes too large, the Redis CPU usage will increase significantly. However, whether this is caused by queries or inserts needs further confirmation. The code is shown
Figure 21

22. We modified the query approach, changing count(‘id’) to exists(), which means changing from querying all keys that meet the conditions in the model to querying just 1 key that meets the conditions. The code is shown in Figure 22.

We modified the query approach, changing count('id') to exists(), which means changing from querying all keys that meet the conditions in the model to querying just 1 key that meets the conditions. The code is shown
Figure 22

23. Without clearing Redis, in the production environment, the number of keys in the Redis model operated on by the third version of the request was 81277, as shown in Figure 23.

Without clearing Redis, in the production environment, the number of keys in the Redis model operated on by the third version of the request was 81277
Figure 23

24. In the production environment, for the third version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 11.8%, as shown in Figure 24.

In the production environment, for the third version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 11.8%
Figure 24

25. Based on the current analysis results, it can be confirmed that when the number of keys in a model operated on by ActiveRecord becomes too large, the Redis CPU usage will increase significantly, and this is most closely related to queries. However, the performance comparison between queries and inserts needs further confirmation. In the code, we commented out the queries and kept only the insert operations, as shown in Figure 25.

Based on the current analysis results, it can be confirmed that when the number of keys in a model operated on by ActiveRecord becomes too large, the Redis CPU usage will increase significantly, and this is most closely related to queries. However, the performance comparison between queries and inserts needs further confirmation. In the code, we commented out the queries and kept only the insert operations
Figure 25

26. Without clearing Redis, in the production environment, the number of keys in the Redis model operated on by the fourth version of the request was 81947, as shown in Figure 26.

Without clearing Redis, in the production environment, the number of keys in the Redis model operated on by the fourth version of the request was 81947
Figure 26

27. In the production environment, for the fourth version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 2.2%, as shown in Figure 27.

In the production environment, for the fourth version of the request, during the entire execution of the typical request, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 2.2%
Figure 27

28. Change count() to exists() to improve query performance, as shown in Figure 28.

Change count() to exists() to improve query performance
Figure 28

29. We refactored some ActiveRecord implementations into native Redis command implementations. During the entire football match, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 21.1%, so the response time was normal, as shown in Figure 29.

We refactored some ActiveRecord implementations into native Redis command implementations. During the entire football match, in Alibaba Cloud monitoring, the Redis CPU usage peaked at 21.1%, so the response time was normal
Figure 29

30. Conclusion: For Yii 2’s ActiveRecord, the ranking of related methods regarding Redis CPU computing pressure is: insert() < one()/exists() < count()/all(). When the number of keys in a specific Redis model exceeds 10000, if you need to perform a query (with a where condition) operation, it is recommended not to use ActiveRecord, as shown in Figure 30.

Conclusion: For Yii 2's ActiveRecord, the ranking of related methods regarding Redis CPU computing pressure is: insert() < one()/exists() < count()/all(). When the number of keys in a specific Redis model exceeds 10000, if you need to perform a query (with a where condition) operation, it is recommended not to use ActiveRecord
Figure 30

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系