一次Redis内存排查记录

1. 起因 萌叔手上有一个测试用的Redis集群,集群是Master-slave模式, 平常也不怎么使用。 于是打算清理释放一下内存,打算降低一下配置。 使用FLUSHALL FLUSHALL 可以看到所有的key都已经被清空 > info Keyspace # Keyspace 查看内存开销情况 > info memory # Memory used_memory:537921192 used_memory_human:513.00M used_memory_rss:544067584 used_memory_rss_human:518.86M used_memory_peak:650406864 实例的内存开销超过500MB,内存让狗吃了? 2. 排查 # Memory used_memory:537921192 used_memory_human:513.00M used_memory_rss:544067584 used_memory_rss_human:518.86M used_memory_peak:650406864 used_memory_peak_human:620.28M used_memory_peak_perc:82.71% used_memory_overhead:537863648 used_memory_startup:791600 used_memory_dataset:57544 used_memory_dataset_perc:0.01% allocator_allocated:537984728 allocator_active:538705920 allocator_resident:548462592 total_system_memory:201219264512 total_system_memory_human:187.40G used_memory_lua:40960 used_memory_lua_human:40.00K used_memory_scripts:216 used_memory_scripts_human:216B number_of_cached_scripts:1 maxmemory:1073741824 maxmemory_human:1.00G maxmemory_policy:allkeys-lru allocator_frag_ratio:1.00 allocator_frag_bytes:721192 allocator_rss_ratio:1.02 allocator_rss_bytes:9756672 rss_overhead_ratio:0.99 rss_overhead_bytes:-4395008 mem_fragmentation_ratio:1.01 mem_fragmentation_bytes:6229608 mem_not_counted_for_evict:0 mem_replication_backlog:536870912 // 约为512MB mem_clients_slaves:16922 mem_clients_normal:183998 mem_aof_buffer:0 mem_allocator:jemalloc-5.1.0 active_defrag_running:0 lazyfree_pending_objects:0 # Replication role:master connected_slaves:1 slave0:ip=192.168.88.29,port=9981,state=online,offset=23482674189,lag=0 master_replid:b58677283938996fdae00f7692d24d11d88ff488 master_replid2:effdd3cd4ab2030a2d49273e0cf2ed74df4bccb9 master_repl_offset:23482674347 second_repl_offset:17046600529 repl_backlog_active:1 repl_backlog_size:536870912 // 约为512MB repl_backlog_first_byte_offset:22945803436 repl_backlog_histlen:536870912 # Cluster cluster_enabled:0 # Keyspace 将INFO命令返回的所有内容都交给chatGPT分析,笔者找到了答案,主要原因是复制积压缓冲区设置的过大。 ...

September 11, 2024 · 1 min

REDIS-CLUSTER集群slot迁移过程分析

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.前言 在前面的文章中, 《REDIS-CLUSTER集群创建内部细节详解》 萌叔创建一个Redis集群。 这篇文章,我会为集群添加2个节点,并介绍slot的迁移过程。 2.集群扩容 添加主节点127.0.0.1:7006 redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 添加从节点127.0.0.1:7007(并指定其Master节点) redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id 86f3cb72813a2d07711b56b3143ff727911f4e1e 新添加的节点上并没有slot分布,需要通过命令让slot重新分布 root@BJ-02:~/cluster-test/7007# redis-cli --cluster reshard 127.0.0.1:7000 >>> Performing Cluster Check (using node 127.0.0.1:7000) S: 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000 slots: (0 slots) slave replicates be905740b96469fc6f20339fc9898e153c06d497 M: 86f3cb72813a2d07711b56b3143ff727911f4e1e 127.0.0.1:7006 slots: (0 slots) master 1 additional replica(s) M: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: 9e29dd4b2a7318e0e29a48ae4b37a7bd5ea0a828 127.0.0.1:7007 slots: (0 slots) slave replicates 86f3cb72813a2d07711b56b3143ff727911f4e1e S: 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003 slots: (0 slots) slave replicates 784fa4b720213b0e2b51a4542469f5e318e8658b M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004 slots: (0 slots) slave replicates 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. How many slots do you want to move (from 1 to 16384)? 0 How many slots do you want to move (from 1 to 16384)? 1 What is the receiving node ID? 86f3cb72813a2d07711b56b3143ff727911f4e1e Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1: all Ready to move 1 slots. Source nodes: M: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005 slots:[0-5460] (5461 slots) master 1 additional replica(s) M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) Destination node: M: 86f3cb72813a2d07711b56b3143ff727911f4e1e 127.0.0.1:7006 slots: (0 slots) master 1 additional replica(s) Resharding plan: Moving slot 5461 from 784fa4b720213b0e2b51a4542469f5e318e8658b Do you want to proceed with the proposed reshard plan (yes/no)? yes Moving slot 5461 from 127.0.0.1:7001 to 127.0.0.1:7006: 3. Slot迁移过程 迁移过程在clusterManagerMoveSlot() 中 ...

June 4, 2022 · 3 min

Redis-Cluster集群创建内部细节详解

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 本文基于redis 6.2.7 1.引言 Redis-Cluster集群的搭建非常简单。搭建过程见参考资料1。 执行 redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \ 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1 root@BJ-02:~/cluster-test# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \ > 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ > --cluster-replicas 1 >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 127.0.0.1:7004 to 127.0.0.1:7000 Adding replica 127.0.0.1:7005 to 127.0.0.1:7001 Adding replica 127.0.0.1:7003 to 127.0.0.1:7002 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000 slots:[0-5460] (5461 slots) master M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master S: 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003 replicates 784fa4b720213b0e2b51a4542469f5e318e8658b S: 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004 replicates 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 S: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005 replicates 7eb7ceb4d886580c6d122e7fd92e436594cc105e Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join .. >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004 slots: (0 slots) slave replicates 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 S: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005 slots: (0 slots) slave replicates 7eb7ceb4d886580c6d122e7fd92e436594cc105e S: 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003 slots: (0 slots) slave replicates 784fa4b720213b0e2b51a4542469f5e318e8658b [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. 验证 root@BJ-02:~# redis-cli -h 127.0.0.1 -p 7000 127.0.0.1:7000> cluster nodes 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001@17001 master - 0 1652672999293 2 connected 5461-10922 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000@17000 myself,master - 0 1652672998000 1 connected 0-5460 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002@17002 master - 0 1652673000333 3 connected 10923-16383 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004@17004 slave 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 0 1652673000126 3 connected be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005@17005 slave 7eb7ceb4d886580c6d122e7fd92e436594cc105e 0 1652673001373 1 connected 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003@17003 slave 784fa4b720213b0e2b51a4542469f5e318e8658b 0 1652673000000 2 connected 一行命令就完成了集群的部署,redis-cli --cluster create到底做了什么。这篇文章萌叔将带你探寻其中的秘密。 ...

May 16, 2022 · 3 min

Redis-Cluster集群模式下Redis客户端如何获得slot的路由信息

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 我们都知道在Redis-Cluster集群模式下,集群中有18634个slot,slot分布在集群多个实例上,当执行一个Command时,Redis客户端会提取Command中的key 根据下面的算法得出key所属的slot slot=CRC16(key)&16383 在根据客户端中的路由表,找到slot所在的Redis实例 这里的路由表存储的就是 slot -> redis-instance 那么问题来了redis客户端是如何得到这个路由表的呢? 2. 分析 下面以go-redis/redis的代码为例,谈谈Redis客户端如何获取和维护slot路由信息。 2.1 存储结构 type clusterClient struct { opt *ClusterOptions nodes *clusterNodes state *clusterStateHolder // 在这里 cmdsInfoCache *cmdsInfoCache //nolint:structcheck } type clusterState struct { nodes *clusterNodes Masters []*clusterNode Slaves []*clusterNode slots []*clusterSlot // 路由信息存储在这里 generation uint32 createdAt time.Time } type clusterSlot struct { start, end int nodes []*clusterNode } 2.2 命令执行过程 1)通过key计算出对应slot 2)通过路由表查找到对应的node信息 3)向node发送CMD 其实第2步根据slot从clusterState中查询对应clusterNode ...

May 14, 2022 · 2 min

聊聊GeoHash

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 本文基于 Redis 3.2.0 前言 鲁班门前弄大斧,笔者今天要聊一下geohash。 redis/elasticsearch/mongoDB 中地理位置的搜索,比如某个位置,附近1公里内的POI点,类似这样的功能都是基于geohash算法实现的。 首先看看维基百科的定义 Geohash is a public domain geocode system invented in 2008 by Gustavo Niemeyer[1], which encodes a geographic location into a short string of letters and digits. It is a hierarchical spatial data structure which subdivides space into buckets of grid shape, which is one of the many applications of what is known as a Z-order curve, and generally space-filling curves. ...

November 12, 2019 · 2 min

玩转Prometheus(5)-监控Redis和MySQL的工具包(业务层)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 对于高可用的服务,监控的粒度往往都会非常细。如果恰好你也在使用 Prometheus, 也需要在业务层对Redis连接池和MySQL连接池进行监控。那么此篇文章对你而言将是一种福利。 Redis Client go-redis/redis MySQL Client jinzhu/gorm 2. 样例代码 go get github.com/vearne/golib main.go package main import ( "github.com/go-redis/redis" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/vearne/golib/metric" "log" "net/http" "time" ) func main() { // init redis client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", PoolSize: 100, }) // ***监控Redis连接池*** metric.AddRedis(client, "car") // init mysql DSN := "test:xxxx@tcp(localhost:6379)/somebiz?charset=utf8&loc=Asia%2FShanghai&parseTime=true" mysqldb, err := gorm.Open("mysql", DSN) if err != nil { panic(err) } mysqldb.DB().SetMaxIdleConns(50) mysqldb.DB().SetMaxOpenConns(100) mysqldb.DB().SetConnMaxLifetime(5 * time.Minute) // ***监控MySQL连接池*** metric.AddMySQL(mysqldb, "car") // do some thing for i := 0; i < 30; i++ { go func() { for { client.Get("a").String() time.Sleep(200 * time.Millisecond) mysqldb.Exec("show tables") } }() } http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":9090", nil)) log.Println("starting...") } func AddRedis(client RedisClient, role string) func AddMySQL(client *gorm.DB, role string) role 仅用于区分不同的Redis实例 ...

July 1, 2019 · 2 min

packetbeat 初探

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 packetbeat是elastic公司开发的网络抓包、嗅探以及分析工具。 和tcpdump一样,它的底层依赖libpcap。但它比tcpdump、tcpcopy功能强大的多。 它能够直接解析以下的网络协议 ICMP (v4 and v6) DHCP (v4) DNS HTTP AMQP 0.9.1 Cassandra Mysql PostgreSQL Redis Thrift-RPC MongoDB Memcache TLS 将网络包转换成JSON字符串,然后导出到以下output File Console Elasticsearch Logstash Kafka Redis 简单描述过程 event -> filter1 -> filter2 … -> output 让我非常吃惊的是它能够捕获MySQL、Redis等的二进制通讯协议,能够从捕获的记录中,清晰的看到每一条SQL查询语句,以及每一条Redis命令 2. 我们能拿它做什么? 据笔者的了解。 以前做线上的流量复制和重放,大致有这么几种方法 (1) 使用tcpcopy (2) 在服务中引起流量复制模块 (3) 服务打印特殊格式的日志 供后期解析,并做重放 (1)是二进制数据流,人无法阅读,(2)、(3)对服务有入侵,不够友好。 JSON格式的数据,对程序优化,对人来说阅读的障碍也不大。个人认为是个不错的选择 有了这些捕获的数据,我们可以用来做 线上排障 服务功能测试/压力测试 对请求(HTTP请求,MySQL、Redis请求等)进行统计分析,为服务优化提供必要的数据支持。 3. 安装&配置&使用 3.1 安装 见参考资料4 ...

December 17, 2018 · 2 min

聊聊go-redis的一些高级用法

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 说到Golang的Redis库,用到最多的恐怕是 redigo 和 go-redis。其中 redigo 不支持对集群的访问。 本文想聊聊go-redis 2个高级用法 2. 开启对Cluster中Slave Node的访问 在一个负载比较高的Redis Cluster中,如果允许对slave节点进行读操作将极大的提高集群的吞吐能力。 开启对Slave 节点的访问,受以下3个参数的影响 type ClusterOptions struct { // Enables read-only commands on slave nodes. ReadOnly bool // Allows routing read-only commands to the closest master or slave node. // It automatically enables ReadOnly. RouteByLatency bool // Allows routing read-only commands to the random master or slave node. // It automatically enables ReadOnly. RouteRandomly bool ... } go-redis 选择节点的逻辑如下 ...

November 18, 2018 · 2 min

基于redis的分布式限频库

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 总述 这个库的目标是为了能够简单粗暴的实现分布式限频功能, 类似于ID生成器的用法, client每次从Redis拿回-批数据(在这里只是一个数值)进行消费, 只要没有消费完,就没有达到频率限制。 English README 优势 依赖少,只依赖redis,不需要专门的服务 使用的redis自己的时钟,不需要相应的服务器时钟完全一致 线程(协程)安全 系统开销小,对redis的压力很小 安装 go get github.com/vearne/ratelimit 用法 1. 创建 redis.Client 依赖 “github.com/go-redis/redis” client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "xxx", // password set DB: 0, // use default DB }) 2. 创建限频器 limiter, _ := ratelimit.NewRedisRateLimiter(client, "push", 1 * time.Second, 200, 10, ratelimit.TokenBucketAlg, ) 表示允许每秒操作200次 limiter, _ := ratelimit.NewRedisRateLimiter(client, "push", 1 * time.Minute, 200, 10, ratelimit.TokenBucketAlg, ) 表示允许每分钟操作200次 ...

August 24, 2018 · 2 min

redigo提示connection pool exhausted

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 线上的某个服务(Golang开发)使用Redis作为消息队列,使用的redis库是garyburd/redigo, 这两天出现如下错误 connection pool exhausted 2. 产生原因 阅读源码pool.go 阅读get()即可 type Pool struct { // Dial()方法返回一个连接,从在需要创建连接到的时候调用 Dial func() (Conn, error) // TestOnBorrow()方法是一个可选项,该方法用来诊断一个连接的健康状态 TestOnBorrow func(c Conn, t time.Time) error // 最大空闲连接数 MaxIdle int // 一个pool所能分配的最大的连接数目 // 当设置成0的时候,该pool连接数没有限制 MaxActive int // 空闲连接超时时间,超过超时时间的空闲连接会被关闭。 // 如果设置成0,空闲连接将不会被关闭 // 应该设置一个比redis服务端超时时间更短的时间 IdleTimeout time.Duration // 如果Wait被设置成true,则Get()方法将会阻塞 Wait bool ... ... } 以上异常的原因是这样的,在garyburd/redigo中,得到连接的步骤如下 尝试从空闲列表中,获得一个可用连接;如果成功直接返回,失败则尝试步骤2 如果当前的Active 连接数 < MaxActive,则尝试创建一个新连接;如果成功直接返回,失败则尝试步骤3 判断Wait为true则等待,否则报出异常 // ErrPoolExhausted is returned from a pool connection method (Do, Send, // Receive, Flush, Err) when the maximum number of database connections in the // pool has been reached. var ErrPoolExhausted = errors.New(&quot;redigo: connection pool exhausted&quot;) 3. 解决方法 设置MaxActive,设MaxActive=0(表示无限大)或者足够大。 设置Wait=true,当程序执行get(),无法获得可用连接时,将会暂时阻塞。 4. 完整的初始化连接池的代码 func NewRedisPool(redisConf context.RedisConf) *redis.Pool { address := fmt.Sprintf(&quot;%v:%v&quot;, redisConf.Host, redisConf.Port) dbOption := redis.DialDatabase(redisConf.Db) pwOption := redis.DialPassword(redisConf.Password) // **重要** 设置读写超时 readTimeout := redis.DialReadTimeout(time.Second * time.Duration(redisConf.ConTimeout)) writeTimeout := redis.DialWriteTimeout(time.Second * time.Duration(redisConf.ConTimeout)) conTimeout := redis.DialConnectTimeout(time.Second * time.Duration(redisConf.ConTimeout)) redisPool := &amp;redis.Pool{ // 从配置文件获取maxidle以及maxactive,取不到则用后面的默认值 MaxIdle: redisConf.MaxIdleConn, MaxActive: redisConf.MaxActiveConn, // **重要** 如果空闲列表中没有可用的连接 // 且当前Active连接数 &lt; MaxActive // 则等待 Wait: true, IdleTimeout: time.Duration(redisConf.IdleTimeout) * time.Second, Dial: func() (redis.Conn, error) { c, err := redis.Dial(&quot;tcp&quot;, address, dbOption, pwOption, readTimeout, writeTimeout, conTimeout) if err != nil { return nil, err } return c, nil }, } return redisPool }

June 4, 2018 · 1 min