C++导致悬垂指针的一个场景

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 警告: 此文章对读者没有价值 遇到的问题 #include <iostream> #include <vector> using namespace std; class Car { public: int age; Car(int a) : age(a) { cout << "Car():" << age << endl; } ~Car() { cout << "~Car():" << age << endl; } }; int main() { vector<Car *> v; for (int i = 0; i < 10; i++) { Car tmp(i); v.push_back(&tmp); } for (auto c : v) { cout << c->age << endl; } } 执行输出: ...

August 17, 2023 · 1 min

数据迁移总结

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.前言 工作这么多年以来,萌叔参与过2次比较大的数据迁移。一次是将几十个T的舆情数据,从IDC-A迁移到IDC-B;一次是账号体系变更,处理100w+用户账号和权益变更。 在这中间遇到过很多问题,也踩过不少坑。本文稍作总结,希望对读者有所帮助。 2. 迁移流程的建议 建议你按照以下流程,来执行数据迁移流程 1)制定预案 需要充分考虑底量数据和增量数据的迁移问题 如果在数据迁移期间,有两套系统需要同时运行,如何保证数据的一致性 如何处理数据迁移引起的用户投诉? 数据迁移耗时? 像之前我们迁移几十个T的舆情数据,大概花费了1个月的时间 被迁移的数据是否要带上特殊标记(染色) 对内外部用户是否会造成影响?对其它第三方服务是否会造成影响? 1)如果需要调用上游服务的,数据迁移过程如果不限制对上游服务的访问,很可能导致上游服务崩溃 2)跨机房数据迁移,数据是走公网,还是走机房之间的专线。要不要限制网络带宽 3)数据迁移期间,可能会对数据库造成压力,会不会影响用户对系统的使用 详细列出数据迁移过程的多个Step 特别要注意多个Step之间有没有依赖关系 2)预案评估 组织会议,确保数据迁移可能影响的人员都能参与预案评估。 运营人员 产品经理 DBA 网络工程师 开发人员 客服 需要对与会人员说明: 整个迁移过程的耗时, 可能对内外部用户可能造成的影响 对上下游服务可能造成的影响,对网络带宽造成的影响 数据迁移的过程不引起问题是非常难的,关键是给相关人员一个预期,做到心理有数。 3)迁移脚本(程序)开发 是自己写脚本来执行数据迁移,还是使用Spark等大数据工具。这个也是需要好好考量的。 4)演练(演习) 由于数据迁移的总耗时,对内外部用户造成的影响, 对其他服务造成的影响很难评估, 尤其是如果整个数据迁移过程非常复杂,涉及多个步骤时。 比如萌叔在做账号体系相关的数据迁移,一共涉及15个步骤,这些步骤之间还有一定依赖关系。 所以萌叔强烈建议,要做数据迁移的演练。 mock数据测试 小样本集数据测试 5)按照预案实施 数据迁移的过程,要尽可能按照预案进行实施。 建议: 详细记录每个步骤开始和结束的时间点 6)数据校验 要考虑数据迁移完成之后,如何进行数据校验。(记录的数量,数据的状态一致等等) 对于非核心的数据甚至可以接受一定的差异,比如1% ~ 5%的数据差异。 7)事后总结 计划永远赶不上变化,有时候因为一些原因,数据迁移后的,数据的状态可能无法于预想的结果的一致,所以很有必要在数据迁移完成之后, 向相关方报告任务执行的结果。那些情况于计划的不一致,可能导致那些后果。

August 11, 2023 · 1 min

闲聊Kvrocks中List结构

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. List 结构 List中的数据会被存储在2个列族中 1) list metadata +----------+------------+-----------+-----------+-----------+-----------+ key =&gt; | flags | expire | version | size | head | tail | | (1byte) | (Ebyte) | (8byte) | (Sbyte) | (8byte) | (8byte) | +----------+------------+-----------+-----------+-----------+-----------+ 2) list sub keys-values +---------------+ key|version|index =&gt; | value | +---------------+ head和tail的初始值是UINT64_MAX / 2 redis_metadata.cc ListMetadata::ListMetadata(bool generate_version) : Metadata(kRedisList, generate_version) { head = UINT64_MAX / 2; tail = head; } 2. 使用 2.1 使用 LPUSH LPUSH mylist &quot;A&quot; subKey的序列如下 ...

July 13, 2023 · 1 min

rocketmq-client-go 消费MessageQueue消息失败

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引言 前几天突然收到线上rocketmq报警,rocketmq队列中的消息有堆积。 打开rocketmq的管理界面,发现broker-4:MessageQueue[4] 上没有消费者,所以消息一直在堆积。重启client,问题仍然无法解决。 查看client的日志,萌叔发现有如下可疑的日志 time=&quot;2023-07-01T23:03:28+08:00&quot; level=error msg=&quot;fetch offset of mq from broker error&quot; MessageQueue=&quot;MessageQueue [topic=account-to-redis, brokerName=broker-4, queueId=3]&quot; consumerGroup=CG-account underlayError=&quot;broker response code: 22, remarks: Not found, V3_0_6_SNAPSHOT maybe this group consumer boot first&quot; “fetch offset of mq from broker error” 这应该就是导致broker-4:MessageQueue[4]一直无法被消费的原因 2. 解决问题 通过网上搜索,萌叔发现了参考资料1.rocketmq-client-go注册消费者组的问题 根据这份资料,此问题是rocketmq-client-go的一个bug。并已经在最新版本中被修复。 offset_store.go func (r *remoteBrokerOffsetStore) fetchConsumeOffsetFromBroker(group string, mq *primitive.MessageQueue) (int64, error) { ... cmd := remote.NewRemotingCommand(internal.ReqQueryConsumerOffset, queryOffsetRequest, nil) res, err := r.client.InvokeSync(context.Background(), broker, cmd, 3*time.Second) if err != nil { return -1, err } // 增加判断逻辑 if res.Code == internal.ResQueryNotFount { return -1, nil // 从最小offset开始消费 } // 原先会从这里返回error if res.Code != internal.ResSuccess { return -2, fmt.Errorf(&quot;broker response code: %d, remarks: %s&quot;, res.Code, res.Remark) } off, err := strconv.ParseInt(res.ExtFields[&quot;offset&quot;], 10, 64) if err != nil { return -1, err } return off, nil } 也就是说当无法从broker 获取offset的时候,直接从最小Offset开始消费。 ...

July 4, 2023 · 3 min

从MySQL client使用引出的bug聊起

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.引子 前几天一个同事在MySQL实例上执行一些数据处理,程序大致如下: modify1.go package main import ( "context" "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "log" "time" ) func main() { db, err := sql.Open("mysql", "testdb_user:12345678@tcp(192.168.2.100:25037)/testdb?charset=utf8&loc=Asia%2FShanghai&parseTime=true") if err != nil { fmt.Println("===0===", err) } defer db.Close() tx, err := db.Begin() if err != nil { fmt.Println("===0===", err) } // 第1个SQL rows, err := tx.QueryContext(context.Background(), "select mtime, money from test where money > ?", 1) if err != nil { log.Fatal(err) } defer rows.Close() // 迭代查询结果 for rows.Next() { var mtime time.Time var money int if err := rows.Scan(&mtime, &money); err != nil { // Check for a scan error. // Query rows will be closed with defer. log.Fatal(err) } log.Println(mtime, money) // 第2个SQL result, err := tx.ExecContext(context.Background(), "insert into test2(`mtime`, `money`) values (?, ?)", mtime, money) if err != nil { log.Fatal(err) } rowsAffected, _ := result.RowsAffected() lastInsertIdresult, _ := result.LastInsertId() log.Println(rowsAffected, lastInsertIdresult) } tx.Commit() } 报错信息如下: ...

June 18, 2023 · 3 min

RocketMQ Filter机制探秘

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 本文基于rocketmq-all-4.8.0 1. client使用Filter RocketMQ消费者订阅消息时,可以指定以某种表达式进行过滤,目前支持2种类型–Tag和SQL92。 1.1 Tag 按照Tag进行过滤. 首先需要给消息打上tag,注意:1条消息只能包含最多1个tag 生产者代码示例1 package main import ( &quot;context&quot; &quot;fmt&quot; &quot;os&quot; &quot;strconv&quot; &quot;github.com/apache/rocketmq-client-go/v2&quot; &quot;github.com/apache/rocketmq-client-go/v2/primitive&quot; &quot;github.com/apache/rocketmq-client-go/v2/producer&quot; ) // Package main implements a simple producer to send message. func main() { p, _ := rocketmq.NewProducer( producer.WithNsResolver(primitive.NewPassthroughResolver([]string{&quot;192.168.12.100:9876&quot;})), producer.WithRetry(2), ) err := p.Start() if err != nil { fmt.Printf(&quot;start producer error: %s&quot;, err.Error()) os.Exit(1) } topic := &quot;test&quot; msg := &amp;primitive.Message{ Topic: topic, Body: []byte(&quot;Hello RocketMQ Go Client! &quot; + strconv.Itoa(i)), } // 打tag msg.WithTag(&quot;tagA&quot;) // broker会针对key,建立索引存储在indexFile中 msg.WithKeys([]string{&quot;key1&quot;, &quot;key2&quot;}) msg.WithProperty(&quot;age&quot;, &quot;10&quot;) msg.WithProperty(&quot;color&quot;, &quot;red&quot;) res, err := p.SendSync(context.Background(), msg) ... } Message中一些重要的属性 ...

May 17, 2023 · 3 min

LSM-Tree分享

参考资料 1.The Log-Structured Merge-Tree 2.一文带你看透基于LSM-tree的NoSQL系统优化方向 3.数据库存储与索引技术:分布式数据库基石——LSM树 4.WiscKey: Separating Keys from Values in SSD-conscious 5.dgraph-io/badger 6.badger源码分析 7.B树与LSM树读写和空间放大分析 8.大白话彻底搞懂 HBase Rowkey 设计和实现方式 9.Kvrocks: 一款开源的企业级磁盘KV存储服务 10.Kvrocks data structures design 11.布隆过滤器的原理及完整公式推导 1. LSM-Tree简介 1.1 LSM-Tree的概念 LSM-Tree 全称是Log Structured Merge Tree,是一种分层、有序、面向磁盘的数据结构,其核心思想是充分利用磁盘的顺序写性能要远高于随机写性能这一特性,将批量的随机写转化为一次性的顺序写。 1.2 应用场景 以NoSQL为代表的分布式数据库多采用LSM树用于构建底层的存储系统。 Apache Cassandra:Cassandra 是一个高可用性、高可扩展性的分布式 NoSQL 数据库,它使用了 LSM 树来存储数据。 LevelDB:由 Google 开发的高性能键值对数据库,使用 LSM 树来存储数据。 RocksDB:由 Facebook 开发的高性能嵌入式键值对数据库,它是 LevelDB 的改进版本,同样使用 LSM 树来存储数据。 HBase:HBase 是一个分布式列存储数据库,它是基于 Hadoop 的一个开源项目。HBase 的数据存储是基于 LSM 树实现的。 ScyllaDB:ScyllaDB 是一个高性能的分布式 NoSQL 数据库,使用 LSM 树来存储数据,并且兼容 Cassandra API。 Apache Lucene:一个开源的信息检索库,广泛用于各种搜索引擎和全文检索系统。虽然Lucene主要关注于索引结构,但其底层的数据存储也使用了LSM Tree的设计思想。 另外TiDB的本地存储使用的是RocksDB TiDB本地存储 ...

May 6, 2023 · 3 min

QPS、并发数与限流保护漫聊

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.引言 Google的网站可靠性工程师小组(SRE)定义了四个需要监控的关键指标。他们称之为“四个黄金信号”:延迟(Latency),流量(Traffic),错误(Errors)和饱和度(Saturation)。这些与微服务的RED度量密切相关:速率,错误和持续时间,以及关注利用率,饱和度和错误的USE方法。这四个信号应该是服务级别目标(SLO)的关键部分,因为它们对于提供高可用性的服务至关重要。 流量(Traffic) 其中流量可以很多指标来衡量,比如QPS(每秒处理的请求数)、Inbound Bandwidth(输入带宽)、Outbound Bandwidth(输出带宽)等。 2.Little’s law QPS: query per second,每秒处理的请求数 RT: response time,平均响应时间或平均处理时间 Concurrent:并发数 QPS、RT、Concurrent之间存在如下关系: Concurrent = QPS * RT 某个服务可以被想象成一条单向高速公路。 每一个需要处理的请求或者任务可以想象成正在这条公路上行驶的汽车。 汽车从入口驶入,到从出口驶出所需要的时间就是平均响应时间(RT) 当前公路上的所有车辆数就是并发数(Concurrent),它一个瞬时值 车辆流速稳定后(从入口驶入和从出口驶出的速率 相同),每秒从入口驶入的车辆数就是每秒处理的请求数(QPS) 3.定性分析 3.1 RT变大 如果服务处理任务的过程依赖其它服务,比如MySQL、或者第三方服务。如果第三方服务出现问题,处理时间变长(RT变大),在QPS不变的情况下,由Little’s law可知,Concurrent会变大 3.2 QPS 如果客户端调用量突然增大,即QPS增大,RT不变的情况下,由Little’s law可知,Concurrent也会变大 3.3 请求触达速率和请求完成速率不一致 这种情况对于处理长作业的服务尤为明显。如果任务到达的快,处理的慢,那么通常只有2个结局,要么提交任务的时候,任务被直接拒绝,要么任务被扔在某个等待队列中,等待执行。在外部的用户看来,这些任务都是在并发执行中,可以看做是并发数。 4. 限流保护 在很多服务中,针对每个请求,都会独立分配额外的资源。比如Golang的web框架在gin中,每个连接都会分配3个协程;MySQL会针对每个请求创建User Thread),如果并发数一直增大,则服务很可能会被OOM或者陷入某种假死状态。 Concurrent = QPS * RT 结论 由于并发数受到2个变量QPS和RT的影响,RT受到第三方服务的影响而不可控,因此仅对QPS限制是无法保护服务的,还需要对Concurrent进行限制。 参考资料 1.Little’s law 2.QPS和并发数,这次给你说清楚 3.QPS、TPS、RT、并发数、吞吐量理解和性能优化深入思考

February 28, 2023 · 1 min

少年,你知道tcp kill吗?

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.序言 有些读者在访问云服务器或者跳板机的时候,可能会发现如果一段时间不使用客户端,连接就自动断开了。 为什么要这么做呢?以跳板机举例,跳板机有时候被称作堡垒机,它类似于一个封闭的房子所开的窗户。跳板机负有身份验证,授权控制,安全审计等义务。 对机房内部任何主机的访问,都必须要访问跳板机。因此每个client都要与跳板机建立连接,跳板机的资源有限,所以通常会定期对闲置的client进行清理。 2.怎样清理空闲连接? TCP协议header中有一个RST标识。 RST一般出现于异常情况,归类为 对端的端口不可用 和 socket提前关闭。 只需要向连接中任意一方发送带有RST标识的TCP包就可以迫使连接断开 但是需要注意的是,接收方收到RST包,首先会看下TCP包的seq是否合法,即seq是否在合法接收窗口范围内。如果不在范围内,这个RST包就会被丢弃。 2.1 获取合法的Seq 经过上面的分析,我们已经现在的问题是如何获取一个合法的Seq。仔细观察TCP header, Acknowledgment Number 表示sender期待的下一个序列号(Seq). Window 表示sender还能够接收的数据大小 [Ack, Ack + Window] 一定是合法的Seq范围 通过抓包,并计算就可以得到一个Sender所能接受的Seq,从而成功的发起一次RST攻击。 2.2 Challenge Ack 但是如果这个连接非常空闲,一直无法抓到数据包怎么办?根据参考资料1的介绍,我们还可以使用Challenge Ack。使用任意的Seq,伪造一个带有SYN标识的TCP包,receiver会发送带有正确Ack的Ack包给sender, 这样我们也能快速获得合法的Seq。萌叔在项目vearne/grpcreplay 中也使用了这个技术。 3.网络工具Tcpkill 社区已经有开源工具tcpkill可以使用 3.1安装 Centos sudo yum -y install dsniff Ubuntu sudo apt install dsniff 3.2 用法 tcpkill [-i interface] [-1..9] expression 第1个参数 -i 指定网卡设备 第2个参数指定强度 第3个参数expression 是libcap抓包使用的过滤器 ...

November 15, 2022 · 1 min

docker compose简介

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.简介 你听说过docker-compose吗?在萌叔看来,docker-compose相当于单机且简化版的k8s。 可以简单的编写一个yaml文件,在yaml文件指定多个container。然后可以通过docker-compose,一次性启动和停止多个服务。 它有几个应用场景: 开发 自动化测试 持续集成 特别是自动化测试这个场景。比如有一个服务依赖数据库MySQL,要完成自动化测试,显然我们需要一个干净的MySQL。用k8s来完成服务和MySQL的部署成本太高,有点杀鸡用牛刀的感觉。并且多个人同时运行的话,还有可能产生冲突。docker-compose由于是单机运行,可以确保多人同时运行互相不冲突。 2.minio示例 该示例包含一个由4个container组成的minio集群,以及一个nginx container对外暴露minio集群的管理后台和API接口 Deploy MinIO on Docker Compose 创建本地目录 mkdir -p ~/mydata/minio/data1-1 mkdir -p ~/mydata/minio/data1-2 mkdir -p ~/mydata/minio/data2-1 mkdir -p ~/mydata/minio/data2-2 mkdir -p ~/mydata/minio/data3-1 mkdir -p ~/mydata/minio/data3-2 mkdir -p ~/mydata/minio/data4-1 mkdir -p ~/mydata/minio/data4-2 编写nginx的配置文件 ~/mydata/minio/proxy.conf upstream minio_console { server minio1:9001 max_fails=3 fail_timeout=5s; server minio2:9001 max_fails=3 fail_timeout=5s; server minio3:9001 max_fails=3 fail_timeout=5s; server minio4:9001 max_fails=3 fail_timeout=5s; } upstream minio_api { # 多个服务之间可以通过服务名称相互访问 server minio1:9000 max_fails=3 fail_timeout=5s; server minio2:9000 max_fails=3 fail_timeout=5s; server minio3:9000 max_fails=3 fail_timeout=5s; server minio4:9000 max_fails=3 fail_timeout=5s; } server { listen 9001; location / { proxy_pass http://minio_console; } } server { listen 9000; location / { proxy_pass http://minio_api; } } 创建一个文件夹minio-distributed mkdir minio-distributed 编写docker compose需要的YAML文件 在minio-distributed目录下编写文件docker-compose.yaml 在docker的体系中,以包含docker-compose.yaml文件的文件夹作为一个项目,文件夹的名称就是项目名称。 ...

October 27, 2022 · 2 min