Redis 内存分析方法

背景

线上经常遇到用户想知道自己 Redis 实例中数据的内存分布情况。为了不影响线上实例的使用,我们一般会采用 bgsave 生成 dump.rdb 文件,再结合 redis-rdb-tools 和 sqlite 来进行静态分析。总的来说,整个分析的过程简单而实用,是每一个 Redis 的用户都非常值得掌握的一个方法。

创建备份

自建 Redis 可在客户端执行 bgsave 生成 rdb 文件。云数据库 Redis 版可以在控制台上可以进行数据备份和下载的操作,下载后的数据为 rdb 格式文件。步骤详见下图:

生成内存快照

redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在分析内存的时候,我们主要用它生成内存快照。主要有以下三个功能:

  • 生成内存快照
  • 转储成 json 格式
  • 使用标准的 diff 工具比较两个 dump 文件

redis-rdb-tools 安装

redis-rdb-tools 有两种安装方式,任选其一即可。

使用 PYPI 安装

  1. pip install rdbtools

从源码安装

  1. git clone https://github.com/sripathikrishnan/redis-rdb-tools
  2. cd redis-rdb-tools
  3. sudo python setup.py install

使用 redis-rdb-tools 生成内存快照

生成内存快照的命令为:

  1. rdb -c memory dump.rdb > memory.csv

生成 CSV 格式的内存报告。包含的列有:数据库 ID,数据类型,key,内存使用量(byte),编码。内存使用量包含 key、value 和其他值。

注意:内存使用量是理论上的近似值,在一般情况下,略低于实际值。memory.csv 例子:

  1. $head memory.csv
  2. database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
  3. 0,string,"orderAt:377671748",96,string,8,8
  4. 0,string,"orderAt:413052773",96,string,8,8
  5. 0,sortedset,"Artical:Comments:7386",81740,skiplist,479,41
  6. 0,sortedset,"pay:id:18029",2443,ziplist,84,16
  7. 0,string,"orderAt:452389458",96,string,8,8

分析内存快照

SQLite 是一款轻型的数据库。我们可以将前面生成的 csv 导入到数据库中之后,就可以利用 sql 语句很方便的对 Redis 的内存数据进行各种分析了。

注意:SQLite版本必须是3.16.0以上。

导入方法

  1. sqlite3 memory.db
  2. sqlite> create table memory(database int,type varchar(128),key varchar(128),size_in_bytes int,encoding varchar(128),num_elements int,len_largest_element varchar(128));
  3. sqlite>.mode csv memory
  4. sqlite>.import memory.csv memory

数据导入以后,接下来想怎么分析就怎么分析了,举几个简单的例子:

查询key个数

  1. sqlite>select count(*) from memory;

查询总的内存占用

  1. sqlite>select sum(size_in_bytes) from memory;

查询内存占用最高的10个 key

  1. sqlite>select * from memory order by size_in_bytes desc limit 10;

查询成员个数1000个以上的 list

  1. sqlite>select * from memory where type='list' and num_elements > 1000 ;

总结

通过使用 redis-rdb-tools + sqlite 的方式,可以方便的对 redis 实例的内存情况进行静态的分析。整个过程也比较简单,获取到 rdb 之后即可。

  1. rdb -c memory dump.rdb > memory.csv;
  2. sqlite3 memory.db
  3. sqlite> create table memory(database int,type varchar(128),key varchar(128),size_in_bytes int,encoding varchar(128),num_elements int,len_largest_element varchar(128));
  4. sqlite>.mode csv memory
  5. sqlite>.import memory.csv memory

实际使用中,发现过一个 List 积攒了10多 GB 的内容,也发现过43 MB 以上的 string 类型的 value, 往往不仅能解答用户的疑惑,而且能够帮助用户排除业务中潜在的风险点,找到业务性能瓶颈。

游戏玩家排行榜

场景介绍

云数据库 Redis 版在功能上与 Redis 基本一致,因此很容易用它来实现一个在线游戏中的积分排行榜功能。

代码示例

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Set;
  4. import java.util.UUID;
  5. import redis.clients.jedis.Jedis;
  6. import redis.clients.jedis.Tuple;
  7. public class GameRankSample {
  8. static int TOTAL_SIZE = 20;
  9. public static void main(String[] args)
  10. {
  11. //连接信息,从控制台可以获得
  12. String host = "xxxxxxxxxx.m.cnhz1.kvstore.aliyuncs.com";
  13. int port = 6379;
  14. Jedis jedis = new Jedis(host, port);
  15. try {
  16. //实例密码
  17. String authString = jedis.auth("password");//password
  18. if (!authString.equals("OK"))
  19. {
  20. System.err.println("AUTH Failed: " + authString);
  21. return;
  22. }
  23. //Key(键)
  24. String key = "游戏名:奔跑吧,阿里!";
  25. //清除可能的已有数据
  26. jedis.del(key);
  27. //模拟生成若干个游戏玩家
  28. List<String> playerList = new ArrayList<String>();
  29. for (int i = 0; i < TOTAL_SIZE; ++i)
  30. {
  31. //随机生成每个玩家的ID
  32. playerList.add(UUID.randomUUID().toString());
  33. }
  34. System.out.println("输入所有玩家 ");
  35. //记录每个玩家的得分
  36. for (int i = 0; i < playerList.size(); i++)
  37. {
  38. //随机生成数字,模拟玩家的游戏得分
  39. int score = (int)(Math.random()*5000);
  40. String member = playerList.get(i);
  41. System.out.println("玩家ID:" + member + ", 玩家得分: " + score);
  42. //将玩家的ID和得分,都加到对应key的SortedSet中去
  43. jedis.zadd(key, score, member);
  44. }
  45. //输出打印全部玩家排行榜
  46. System.out.println();
  47. System.out.println(" "+key);
  48. System.out.println(" 全部玩家排行榜 ");
  49. //从对应key的SortedSet中获取已经排好序的玩家列表
  50. Set<Tuple> scoreList = jedis.zrevrangeWithScores(key, 0, -1);
  51. for (Tuple item : scoreList) {
  52. System.out.println("玩家ID:"+item.getElement()+", 玩家得分:"+Double.valueOf(item.getScore()).intValue());
  53. }
  54. //输出打印Top5玩家排行榜
  55. System.out.println();
  56. System.out.println(" "+key);
  57. System.out.println(" Top 玩家");
  58. scoreList = jedis.zrevrangeWithScores(key, 0, 4);
  59. for (Tuple item : scoreList) {
  60. System.out.println("玩家ID:"+item.getElement()+", 玩家得分:"+Double.valueOf(item.getScore()).intValue());
  61. }
  62. //输出打印特定玩家列表
  63. System.out.println();
  64. System.out.println(" "+key);
  65. System.out.println(" 积分在1000至2000的玩家");
  66. //从对应key的SortedSet中获取已经积分在1000至2000的玩家列表
  67. scoreList = jedis.zrangeByScoreWithScores(key, 1000, 2000);
  68. for (Tuple item : scoreList) {
  69. System.out.println("玩家ID:"+item.getElement()+", 玩家得分:"+Double.valueOf(item.getScore()).intValue());
  70. }
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. }finally{
  74. jedis.quit();
  75. jedis.close();
  76. }
  77. }
  78. }

运行结果

在输入了正确的云数据库 Redis 版实例访问地址和密码之后,运行以上 Java 程序,输出结果如下:

  1. 输入所有玩家
  2. 玩家ID9193e26f-6a71-4c76-8666-eaf8ee97ac86 玩家得分: 3860
  3. 玩家IDdb03520b-75a3-48e5-850a-071722ff7afb 玩家得分: 4853
  4. 玩家IDd302d24d-d380-4e15-a4d6-84f71313f27a 玩家得分: 2931
  5. 玩家IDbee46f9d-4b05-425e-8451-8aa6d48858e6 玩家得分: 1796
  6. 玩家IDec24fb9e-366e-4b89-a0d5-0be151a8cad0 玩家得分: 2263
  7. 玩家IDe11ecc2c-cd51-4339-8412-c711142ca7aa 玩家得分: 1848
  8. 玩家ID4c396f67-da7c-4b99-a783-25919d52d756 玩家得分: 958
  9. 玩家IDa6299dd2-4f38-4528-bb5a-aa2d48a9f94a 玩家得分: 2428
  10. 玩家ID2e4ec631-1e4e-4ef0-914f-7bf1745f7d65 玩家得分: 4478
  11. 玩家ID24235a85-85b9-476e-8b96-39f294f57aa7 玩家得分: 1655
  12. 玩家IDe3e8e1fa-6aac-4a0c-af80-4c4a1e126cd1 玩家得分: 4064
  13. 玩家ID99bc5b4f-e32a-4295-bc3a-0324887bb77e 玩家得分: 4852
  14. 玩家ID19e2aa6b-a2d8-4e56-bdf7-8b59f64bd8e0 玩家得分: 3394
  15. 玩家IDcb62bb24-1318-4af2-9d9b-fbff7280dbec 玩家得分: 3405
  16. 玩家IDec0f06da-91ee-447b-b935-7ca935dc7968 玩家得分: 4391
  17. 玩家ID2c814a6f-3706-4280-9085-5fe5fd56b71c 玩家得分: 2510
  18. 玩家ID9ee2ed6d-08b8-4e7f-b52c-9adfe1e32dda 玩家得分: 63
  19. 玩家ID0293b43a-1554-4157-a95b-b78de9edf6dd 玩家得分: 1008
  20. 玩家ID674bbdd1-2023-46ae-bbe6-dfcd8e372430 玩家得分: 2265
  21. 玩家ID34574e3e-9cc5-43ed-ba15-9f5405312692 玩家得分: 3734
  22. 游戏名:奔跑吧,阿里!
  23. 全部玩家排行榜
  24. 玩家IDdb03520b-75a3-48e5-850a-071722ff7afb 玩家得分:4853
  25. 玩家ID99bc5b4f-e32a-4295-bc3a-0324887bb77e 玩家得分:4852
  26. 玩家ID2e4ec631-1e4e-4ef0-914f-7bf1745f7d65 玩家得分:4478
  27. 玩家IDec0f06da-91ee-447b-b935-7ca935dc7968 玩家得分:4391
  28. 玩家IDe3e8e1fa-6aac-4a0c-af80-4c4a1e126cd1 玩家得分:4064
  29. 玩家ID9193e26f-6a71-4c76-8666-eaf8ee97ac86 玩家得分:3860
  30. 玩家ID34574e3e-9cc5-43ed-ba15-9f5405312692 玩家得分:3734
  31. 玩家IDcb62bb24-1318-4af2-9d9b-fbff7280dbec 玩家得分:3405
  32. 玩家ID19e2aa6b-a2d8-4e56-bdf7-8b59f64bd8e0 玩家得分:3394
  33. 玩家IDd302d24d-d380-4e15-a4d6-84f71313f27a 玩家得分:2931
  34. 玩家ID2c814a6f-3706-4280-9085-5fe5fd56b71c 玩家得分:2510
  35. 玩家IDa6299dd2-4f38-4528-bb5a-aa2d48a9f94a 玩家得分:2428
  36. 玩家ID674bbdd1-2023-46ae-bbe6-dfcd8e372430 玩家得分:2265
  37. 玩家IDec24fb9e-366e-4b89-a0d5-0be151a8cad0 玩家得分:2263
  38. 玩家IDe11ecc2c-cd51-4339-8412-c711142ca7aa 玩家得分:1848
  39. 玩家IDbee46f9d-4b05-425e-8451-8aa6d48858e6 玩家得分:1796
  40. 玩家ID24235a85-85b9-476e-8b96-39f294f57aa7 玩家得分:1655
  41. 玩家ID0293b43a-1554-4157-a95b-b78de9edf6dd 玩家得分:1008
  42. 玩家ID4c396f67-da7c-4b99-a783-25919d52d756 玩家得分:958
  43. 玩家ID9ee2ed6d-08b8-4e7f-b52c-9adfe1e32dda 玩家得分:63
  44. 游戏名:奔跑吧,阿里!
  45. Top 玩家
  46. 玩家IDdb03520b-75a3-48e5-850a-071722ff7afb 玩家得分:4853
  47. 玩家ID99bc5b4f-e32a-4295-bc3a-0324887bb77e 玩家得分:4852
  48. 玩家ID2e4ec631-1e4e-4ef0-914f-7bf1745f7d65 玩家得分:4478
  49. 玩家IDec0f06da-91ee-447b-b935-7ca935dc7968 玩家得分:4391
  50. 玩家IDe3e8e1fa-6aac-4a0c-af80-4c4a1e126cd1 玩家得分:4064
  51. 游戏名:奔跑吧,阿里!
  52. 积分在10002000的玩家
  53. 玩家ID0293b43a-1554-4157-a95b-b78de9edf6dd 玩家得分:1008
  54. 玩家ID24235a85-85b9-476e-8b96-39f294f57aa7 玩家得分:1655
  55. 玩家IDbee46f9d-4b05-425e-8451-8aa6d48858e6 玩家得分:1796
  56. 玩家IDe11ecc2c-cd51-4339-8412-c711142ca7aa 玩家得分:1848

 

posted on 2018-07-02 16:47  一天不进步,就是退步  阅读(3359)  评论(0编辑  收藏  举报