随笔-2020-09-26

  每个人不同时刻对同一个事物的感受应该是不同的,正如此刻凌晨无法入睡的我,坐在出租房小书桌前,听着钢琴曲《Kiss the Rasin》,想到的却是大约5年前北师大东门的台湾人开的咖啡厅。窗外下着大雪,屋内开着暖气,透过玻璃,阳光照在桌上的杯子和书本上面,暖烘烘的。而我就那么呆呆的坐着,时间好像就静止在那一刻。当时的我具体想点什么我也不记得了,可能是期末考的题目,可能是怀念高中、大学时候的时光,也可能是畅想毕业后的自己会去到哪个城市,是否会扎根北京。现在回头看看,有些问题已经有了答案,也有些问题已经没那么重要了,但有一点可以确定的是,自己变得越来越怀念过去,也越来越焦虑未来了。   我觉得,对于成年人来说,怀旧是一个各个年龄阶段,各个社会人群共有的情感。读大学我们会怀念高中,读研究生会怀念高中、大学,毕业了会怀念读书的时光,成家立业后可能又会怀念刚…

Read more

MongoDB removeShard的流程

向mongos发送removeShard命令 mongos向cs发送_configsvrRemoveShard命令 cs查询除了要remove的shard,是否还有别的shard在drain状态,是的话则返回失败。{"_id": {$ne: "shard1"}, "drainging": true}}} 表明同一时刻只能有一个removeShard操作。 cs查询是否要remove的shard是最后一个shard,是的话本次请求返回失败。 cs查询要remove的shard是否在drain状态,如果不是的话,则置draining状态为true,并且reload shardRegistry(缓存的shard的路由表)。日志层面记录removeShard开始 cs查询shard对应的chunk表,还遗留有多少条chunk cs查询shard对应的database表,还遗…

Read more

MongoDB中的集群选举

  本文首先介绍Raft协议的选举基本流程,然后记录mongodb主从选举的过程,主要参考Replication Internals,本文更相当于一个读书笔记性质,方便后面自己回顾。   说明:本文的step up表示节点成为primary,step down表示节点退出primary。 0. Raft协议中的选举流程   我觉得这里应该介绍一下Raft协议的基本选举内容,因为MongoDB的选举是根据Raft协议来的: 如果没有master,每个结点会主动发起选举,根据超时机制,超时时间到了才会发起。每个结点的超时时间在150ms~300ms之间随机。初始term都为0。 假设有个A结点先超时了,那么他将发起投票,将Term+1,对自己投票+1,然后发起选举给别的节点。 其他节点B, C收到消息后,发现对应的term还没…

Read more

MongoDB的主从复制之全量同步

  关于增量复制,我的上一篇博客:MongoDB主从复制之增量同步 已经大体介绍过了,本文介绍MongoDB主从复制的全量复制流程,参考官方主从复制文档和4.2的源码实现。第一部分,将会过一下大体流程;第二部分将会从代码层面大概讲一下主体流程。   说明:标题全量同步环节也叫做initial sync,但是下文提到的全量同步是全量数据的同步(data sync),其实initial sync本身除了全量数据的同步(data sync),还包括了并行的增量数据的同步(oplog sync)。这么说可能有点绕,看完这篇文章应该就懂了。 0. 前言   对于绝大多数数据库来说,数据的复制环节都是全量环节和增量环节,其中全量环节主要拷贝全量的数据,可能是打snapshot镜像复制,也可能是直接扫描表进行复制,比如mongodb里面就是挨个…

Read more

MongoDB主从复制之增量同步

  本文首先介绍MongoDB 4.2版本,MongoDB主从复制中增量同步的拉取和写入流程,然后介绍几个位点的区别:lastApplied,lastCommit,stableCkpt,appliedThrough。 1. 增量同步基本流程   secondary的OplogFetcher负责从源端拉取oplog,并写入到内存blocking队列OplogBuffer中;OplogApplier负责从oplog中拉取数据,并写入到目的端(也就是当前结点)。下图是增量同步的基本流图:   同步之前,secondary会选择一个sync source作为同步的源进行全量和增量的拉取,那么同步源是如何选择的? 1.1 同步源sync source的选择   每次开启全量intial sync,创建Background…

Read more

mongodb change stream流程

  本文介绍mongodb内部对于change stream的实现逻辑,以4.0版本为准。   change stream走的是aggregate的pipeline模式,其加了一个特殊的$changestream字段,客户端发送的change stream会被翻译成对应的$changestream stage: "pipeline" : [ { "$changeStream" : { "fullDocument" : "default", "startAtOperationTime" : Timestamp(1580867312, 3) } } ], 其本质就是一个带$match的在oplog表上的$cursor,数据拉取后经过transform转换把oplog转成change stream的event…

Read more

MongoDB里面的混合逻辑时钟

  本文首先介绍一下混合逻辑时钟的基本概念,然后介绍MongoDB中的混合逻辑时钟是如何运用的。关于混合逻辑时钟的介绍大家可以参考http://vinllen.com/hun-he-luo-ji-shi-zhong/。 1. 逻辑时钟   逻辑时钟(LC)是由Lamport在time clock and the ordering of events in a distributed system这篇论文里面提出的,用于解决分布式场景下,时钟不一致无法定序的问题。其主要思想就是根据happened-before关系确定进程的逻辑时钟,从而确定进程的偏序关系: 如果a, b事件(消息)都是位于一个进程内(假设顺序发生,不考虑并发),且a位于b之前发生,那么a happened-before b, 记作:a hb b或者a->b,C(a) 如果a,b事件是…

Read more

redis主从复制

  本文介绍一下redis的主从复制机制,首先是大体原理,然后是4.0的复制入口代码分析,接着是master上的处理逻辑,再是slave的,最后有几个Q&A记录。 1. 大体原理   基本上大部分数据库主从原理都是先全量复制,传播当前master的全量数据,然后是增量复制,传播从全量复制开始积累的增量数据。   redis在2.8以前的版本,slave节点向master发送sync请求全量+增量数据,sync命令的缺点是一旦中间因为网络抖动等原因断开,重新连接需要重新同步全量,那么这个代价就很大。所以从2.8开始引入了psync,支持断点续传,从4.0开始,又引入了psync2(其redis命令还是psync),支持主从切换情况下的断点续传。下面介绍以4.0的psync2为例。   当在slave上执行…

Read more

MongoDump和MongoRestore源码解析

  mongodump和mongorestore都是mongodb官方提供的数据备份和恢复的工具,github地址。其本身实现都比较简单,本文会大概介绍一下原理,其实一开始我是想介绍一下细节的,但是看代码都比较简单,还是算了吧。 MongoDump   MongoDump用于把数据备份从MongoDB备份数据。MongoDump做的比较简单,按db.collection为粒度进行dump,dump的时候先dump meta信息,主要是索引、版本、用户等信息,然后是dump的表数据。其中meta信息以json形式进行dump,表的数据以gzip或者bson格式进行dump。另外,全量期间的oplog也会被dump(开始dump的时候记录一下最新的oplog时间戳),如果用户指定了--oplog参数,便于后面按时间点进行恢复。   d…

Read more

浅谈数据库同步和迁移

  本文将主要首先聊一聊数据库同步和迁移两个话题,之后将会围绕这2个话题介绍一下阿里云最近开源的基于MongoDB和Redis的数据同步&迁移工具MongoShake和RedisShake,最后介绍一些用户的使用案例。 1. 同步   现在大部分数据都支持集群版的数据,也就是说一个逻辑单元中有多个db节点,不同节点之间通常通过复制的方式来实现数据的同步,比如Mysql的的基于binlog的主从同步,Redis的基于sync/psync机制的aof主从同步,MongoDB基于oplog的主从同步等等。这些机制支撑了一个单元下的数据冗余高可用和读写分离负载分担。   但仅仅一个逻辑单元内的数据同步对于很多业务通常不够用,很多业务需要跨逻辑单元的数据同步的能力,例如同城多机房,异地多数据中心同步等。容灾,多活是最常见的两种业务场…

Read more

混合逻辑时钟

  本文将首先依次简单介绍分布式系统下的物理时钟(Physical Time,也称PT),逻辑时钟(Logical Clock,也称LC),向量时钟(Vector Clock,也称VC),真实时钟(True Time,也称TT)的基本概念,然后着重笔墨介绍混合逻辑时钟(Hybrid Logical Clock,也称HLC),论文来自Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases。   说明:本文描述的时间戳和时钟是一个概念。 1. 物理时钟-Physical Time, PT   物理时钟即机器本地的时钟,而由于设备硬件不同,本身存在偏差,一天的误差可能有毫秒甚至秒级,所以需要对不同的机器时钟进行同步使得机器的时…

Read more

ssdb迁移数据到redis

  最近搞了个软件,把ssdb的数据迁移到redis上去,官方的ssdb只支持从redis到ssdb的迁移,而不支持从ssdb到redis的反向链路版权。于是,我自己搞了一个进行迁移。 原理   ssdb-port作为从节点(slave)与主节点(master)进行交互并同步数据,在数据同步过程中进行截取然后解析适配发送到配置文件制定的redis中。下面是基本的架构图。   关于ssdb和redis的命令对照可以参考ssdb的github命令对照文档。 下载链接   请参见我发布在阿里云官网的地址。…

Read more

go sync.Map存在bug

go的sync.Map有bug,并发量大的时候读取,已经store的数据,Load显示不存在,被这个坑搞了一天。改为读写锁加持的普通map解决了。具体原因不明。…

Read more

MongoShake开源

1.背景   在当前的数据库系统生态中,大部分系统都支持多个节点实例间的数据同步机制,如Mysql Master/Slave主从同步,Redis AOF主从同步等,MongoDB更是支持3节点及以上的副本集同步,上述机制很好的支撑了一个逻辑单元的数据冗余高可用。   跨逻辑单元,甚至跨单元、跨数据中心的数据同步,在业务层有时候就显得很重要,它使得同城多机房的负载均衡,多机房的互备,甚至是异地多数据中心容灾和多活成为可能。由于目前MongoDB副本集内置的主从同步对于这种业务场景有较大的局限性,为此,我们开发了MongoShake系统,可以应用在实例间复制,机房间、跨数据中心复制,满足灾备和多活需求。   另外,数据备份是作为MongoShake核心但不是唯一的功能。MongoShake作为一个平台型服务,用户可以通过对接Mongo…

Read more

tcmalloc浅析

  最近学习了tcmalloc机制,它是go里面用到的内存分配机制。本文参考tcmalloc,加上一部分自己的理解。 tcmalloc VS ptmalloc(glibc 2.3 malloc)   对于小内存来说,tcmalloc提供线程级别的内存分配,这样就减少了线程之间的竞争,ptmalloc2也提供线程级别分配,但是它的内存被分配到某个线程后就不能重新分配给别的线程,这造成了较大的资源浪费。对于大内存来说,tcmalloc也采用了细粒度且高效的分配策略。   在2.8 GHz P4环境下,tcmalloc执行小内存malloc/free的时间大约为50ns,小于ptmalloc2的300ns。   另外在空间利用上,tcmalloc额外空间比较少,N个8字节的对象占用的总空间大概为8N*1.01,而pt…

Read more

go并发需要注意的问题

  用go写并发的确很方便,协程相比于线程轻量很多,关于协程与线程的区别,可以参考我的这篇博客:go中的协程与线程的区别。本文主要讲一下一些并发中应该注意的坑,学习go没多久,可能有认知不到位的地方,欢迎指正。 1. channel   go中通过channel进行消息通信,channel函数传参通过引用实现。go中有2种channel,分别是无缓存的channel(unbuffered channel)和有缓存的channel(buffered channel),前者cap为0,后者大于0。无缓存的channel主要用于同步:生产者写入channel然后阻塞,直到消费者读取后,生成者才从阻塞状态返回;有缓存队列适用于异步:在队列为满的情况下(len < cap),生产者将消息塞入channel就返回。   由于go存在垃圾…

Read more

go中的协程与线程的区别

  对用户来说,协程与线程几乎没什么区别,但是实际上还是有一些区别的。   说明:本文对协程和goroutine,OS线程和内核线程都是一个概念,未加区分。 1. 栈大小区别   我们知道,线程是有固定的栈的,基本都是2MB,当然,不同系统可能大小不太一样,但是的确都是固定分配的。这个栈用于保存局部变量,用于在函数切换时使用。但是对于goroutine这种轻量级的协程来说,一个大小固定的栈可能会导致资源浪费:比如一个协程里面只print了一个语句,那么栈基本没怎么用;当然,也有可能嵌套调用很深,那么可能也不够用。   所以go采用了动态扩张收缩的策略:初始化为2KB,最大可扩张到1GB。 2. goroutine没有id   每个线程都有一个id,这个在线程创建时就会返回,所以可以很方便的通…

Read more

golang net/rpc源码分析

  为什么需要rpc框架?一次rpc需要指定调用的方法,参数,接收返回值。如果没有rpc框架,裸写tcp,什么时候知道报文传递完毕的界限。最简单我们可以搞个私有协议,TLV格式指定:T(type)指定类型,L(length)指定长度,V(Value)指定值,但是这个也会带入一些问题,比如规范问题,不同服务提供不同协议,这不乱套了吗;另外还有效率问题,比如我要传递一个数组怎么传?基于以上几个问题,rpc框架出现了,rpc框架采用序列化操作将请求和返回在发送端进行序列化,然后在接收端进行解序列化达到目的,如下图所示,图片来自博客。 服务调用流程如下: client调用client stub,这是一次本地过程调用 client stub将参数打包成一个消息,然后发送这个消息。打包过程(序列化)也叫做 marshalling client所在的系统将消息发送给serve…

Read more

golang实现mapreduce单进程版本

  元旦放假的第一天,在家没事干,用golang实现了一下mapreduce的单进程版本,github地址。处理对大文件统计最高频的10个单词,因为功能比较简单,所以设计没有解耦合。   本文先对mapreduce大体概念进行介绍,然后结合代码介绍一下,如果接下来几天有空,我会实现一下分布式高可用的mapreduce版本(1月6日update:这个主要看需求,1.如果master挂了,当前执行的任务丢了就丢了,接下来下发的任务还能执行,那么这个很好实现,可以做多个Master,每次任务分发给一个master执行,北向搞一个负载均衡器就可以,或者弄一个注册中心;2.如果要高可用指的是已经执行的mapreduce任务在主master挂掉后仍然继续执行,那么多个master之间需要做数据同步,可以用Redis或者kafka做消息同步,但是有种过度设计的感觉…

Read more

go https example

  本文首先大概介绍一下https的协议原理(其实是TLS协议原理),然后给出构建https的go代码例子。 1.HTTPS协议   网上已经有很多资料介绍HTTPS协议了,我归纳一下,加深自己的记忆。   HTTPS是HTTP通过SSL/TLS加密的方式进行通讯,TLS是SSL的改进版本。TLS协议可以分为握手阶段和对话阶段,握手阶段采用非对称加密方式(密钥由第三方机构提供),对话阶段采用对称加密方式(密钥由握手阶段协商得出)。   握手阶段主要分为4个步骤(细分来说,可以有多个步骤,具体查看RFC文档): 1.1 Client Hello   客户端向服务器端发起hello请求,同时带入以下信息: 客户端支持的加密方式 客户端支持的SSL/TLS协议版本号 客户端生成的随机数1,该随机数…

Read more