blog icon indicating copy to clipboard operation
blog copied to clipboard

博客

Results 19 blog issues
Sort by recently updated
recently updated
newest added

使用mongodb有一段时间了,总体来说,mongodb还是非常给力的,但是有几个问题也是需要注意的。 ### 对非UTF8编码的KEY支持不好 - version: 1.8 - problem: 数据库中存在重复的非uft8编码的主键 理论上说在数据库中,一个主键(单一主键或者联合主键)在数据库中的值是唯一确定的。在一次倒数据时发现对于同一个主键(存在非UTF8编码字符)在数据库中存在多个值,并且值还是不完全一样的。使用数据库API查询的时候当然只能查到一个值,但查到的值不一定是最新的,可能会导致数据不一致现象。因此在数据库存储过程中最好不要使用非UTF8编码的KEY ### 主从数据库数据不一致 - version: 2.4, replset - problem: 主数据库和从数据库数据条目数不一致 mongodb使用复制集的时候,可以指定一个主数据库,接收所有的写请求(包括修改),然后记录相应的操作日志即oplog,从数据库从主数据库拉取oplog,再按先后顺序重放一下oplog,最终主数据库和从数据库数据应该是一致的。 最近在倒数据偶然发现主数据库和从数据库数据条目数不一致,表现为所有从数据库数据条目数比主数据库少并且所有从数据库数据条目数是一致的,数据正确性还需要进一步验证。主数据库在几天前已经停止写入和修改数据库了,从数据库已经同步完成了,从数据库条目数一致可以从反面印证。后来经过精确主从数据库数据对比发现有些数据只在主数据库有,有些数据只在从数据库有,有些数据在两个数据库都有但是内容不一致,最奇怪的一条是从数据库中的记录少了几个字段。后来查询数据库详细日志发现从数据库有恢复迹象,当数据文件损坏的时候数据库会进行数据修复。目前的问题是一个从数据库修复会影响所有的从数据库导致主从不一致。当磁盘出现问题的时候要格外关注,操作不当会导致丢数据,需要慎重。 ### 大量数据导入速度缓慢 - version: 2.4 当数据量达到亿级别以后,从数据源倒数据到mongodb性能会快速下降。有一张tokumx和mongodb的对比图可以看出来随着数据量的增加mongodb写入性能下降非常快。 ![http://www.tokutek.com/wp-content/uploads/2013/06/mongodb-blog-09-iibench-tps.png](http://www.tokutek.com/wp-content/uploads/2013/06/mongodb-blog-09-iibench-tps.png) 我曾经测试过几种导入方式,比如单条写入、批量写入、使用mongoimport或mongorestore等官方工具导入,速度都不理想,导入速度越来越慢,可能和需要创建索引有关。sharding数据库导入速度更慢,估计和mongodb在分片的时候平衡数据有关。 因此需要对数据的量最好预估,不要在磁盘满的时候再想换大磁盘,不要等数据放不下的时候再做分片。...

mongodb

一般HTTP请求或响应包含`Header`和`Body`,如果有些信息是在Body发完才知道,比如Body的校验、数字签名、后期处理结果等希望在同一个请求里面延后发送,就需要用到`Trailer`。 ## 传输格式 一个带`Trailer`的响应例子: ```http HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked Trailer: Expires 7\r\n Mozilla\r\n 9\r\n Developer\r\n 7\r\n Network\r\n 0\r\n Expires: Wed, 21 Oct 2015 07:28:00 GMT\r\n \r\n ```...

Go
HTTP

开发过程中经常会遇到这样的问题:我改了一个包,哪些服务受影响?我们期望是把所有相关的服务都升级,避免出现遗漏。 对于业务不复杂或者比较偏上层的包,大部分直接在代码库里面`grep`一下就知道了。如果的偏底层的包,业务不一定会直接引用,这种直接分析的话就比较复杂了。 之前有种比较麻烦但是也相对有效的方法。我们知道Go的编译是有缓存的,如果代码有变更的话就会触发包重新编译,通过 `go build -v`可以把重新编译的包的名字输出出来,当然也包含最终的服务程序。实际操作的时候需要避免ide编译影响。 后来研究了一下,实际有更好的办法来实现。`go list`命令有个`-json`的参数,会自动分析各个包的依赖关系,以json的格式输出出来,我们以官方库里面的`encoding/base64`包为例: 点击展开查看... ```bash # go list -json encoding/base64 { "Dir": "/usr/local/go/src/encoding/base64", "ImportPath": "encoding/base64", "Name": "base64", "Doc": "Package base64 implements base64 encoding as specified...

Go

当一个container起来之后,我们有时候希望能进入container内部去看看,比如查查日志,执行些操作等。目前有几种方式可以实现: ### 1. docker attach 这个是官方提供的一种方法。 测试,首先启动一个container: ``` bash $ docker run -i -t ubuntu bash root@4556f5ad6067:/# ``` 不要退出,打开另一个终端: ``` $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS...

docker

首先从一个日志分析的Go程序说起,基本功能就是一行一行读取数据并处理。代码大体是这样的: ``` go func main() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() if err := deal(line); err != nil { log.Println(line) } } } ``` 因为数据是用hadoop直接cat出来的,通过管道传输到日志处理程序,所以这里输入源是`stdin`。 实际处理的时候发现一个比较奇怪的现象,每个日志文件的总行数差别不大,但是有的文件处理时间明显比其他短,并且还会能看到这样的日志信息:`cat:...

Go

近期网站访问量大增,随之而来出现一些莫名其妙的慢请求,表现为:有两个服务A和B,服务A要去服务B请求数据,从日志看,服务A有比较多的秒级的慢请求,根据日志判断慢请求是因为服务B变慢了,接下来查B的日志,发现B只有极少的慢请求的日志,最起码从某个慢请求跟踪来看,服务B的处理返回数据很快,并没有出现所谓的慢请求。这个就比较有趣了,A说B变慢了,B说我没有变慢。到底是A自己慢了,还是B慢了,或者是A到B之间的链路慢了? 首先要确认是否是链路变慢了,A和B两个服务在同一个局域网内,两个服务互ping时间在1ms以内。之前有发现因为丢包导致网络变慢的现象,但是查两台机器发现丢包率都很低,在正常范围内。这个基本可以排除网络问题。 因为之前日志记录不是很完善,接下来首先要做的是补日志,关键操作记录时间。随后分析日志发现,服务A从收到请求到将请求发送到B这个时间是毫秒级的,B从收到请求到处理完返回也是毫秒级的,也就是说两边处理请求都很快。后来详细对比服务A和B的时间戳发现一个惊人的现象:服务A发请求给B的时间和服务B收到请求的时间差了几秒!出现这样的情况首先要确认服务A和服务B所在是机器时间是一致的,经检查确实是一致的。会不会是网络丢包导致的?如果有比较严重的丢包问题的话,收发请求出现大量重传可能会导致比较大的网络延迟。但是之前已经分析过,两台机器之间的网络状况非常好,没有丢包现象。即使有丢包的话,出现丢包问题的时候,如果如果等待200ms没收到回复会进行重传,如果要达到秒级别的延时的话至少一个请求要丢包5次以上,这已经是很严重的丢包事故了,很容易被检测到。 这样看起来似乎问题没法继续分析解答了。就在这时,服务B出现了一条500的日志,看错误信息是`too many open files`,一般出现这种情况是因为服务B自己文件句柄数超了,当超过6000的时候就比较容易出现上面的错误。这里的`files`不一定是真实的文件,有可能是tcp连接,因为在linux下一切皆是文件。先写个程序监测一些服务B的文件句柄数吧,检查一个进程的文件句柄数可以用`lsof -n -p pid | wc -l`来观测,pid就是进程的pid。写个shell脚本循环观测: ``` bash #!/bin/sh while true do echo -n `date`"\t" lsof -n -p `pidof service_name` | wc -l...

Go

近期测试了一下snappy压缩算法,总体感觉是压缩、解压速度非常快,应用场景也很多。 官网: https://code.google.com/p/snappy/ ### 简介 > Snappy is a compression/decompression library. It does not aim for maximum compression, or compatibility with any other compression library; instead, it aims for very...

snappy

如果不清楚一致性哈希的原理,先移步这里: [http://blog.codinglabs.org/articles/consistent-hashing.html](http://blog.codinglabs.org/articles/consistent-hashing.html) 项目初期,为了节省成本,可能只用3台机器做缓存,通过一致性哈希方式将请求分摊到3台机器上面。后面随着业务量扩大,3台机器遇到性能瓶颈就需要扩容。假设我们扩容一倍到6台机器,如果直接将三个新节点加入一致性哈希环里面,就意味着一半的缓存会失效,这无疑会给后端服务造成巨大冲击。为了减小冲击,可以每次新增一个节点,第一次有25%的缓存失效,为了减小对后端的冲击可能需要在凌晨操作,过一段时间,等缓存新节点里面有足够多数据之后,再增加第二个节点,理想情况下第二次添加节点只有20%的缓存失效。同样的方式再增加第三个节点,16.7%的缓存失效。后面再新增节点失效的缓存会逐渐减少。 回顾一下上面的的扩容方式,有几个地方不太优雅: 1. 扩容导致缓存失效。如果服务器压力已经很大,25%的缓存失效意味着对后端服务会有较大冲击。 2. 每次新增一个节点,不能立即增加下一个节点,需要等缓存填充,这个时间不可控,有可能导致扩容的周期非常长,增加运维人员负担。 有没有什么方式可能一次性扩容多个节点,并且缓存不失效呢?这个想法很美好,其实也是可以做到的。 对应原先三台机器A,B,C组成一个哈希环H1,三台新机器D,E,F和A,B,C组成一个新的哈希环H2,我们需要实现一个特殊的读逻辑:先去H2读,当H2不存在的时候去H1读,两个都不存在意味着缓存不存在。写操作全部在H2,一段时间后当D,E,F三台机器缓存被填充之后移除哈希环H1,只保留H2,也就是我们希望的扩容的最终结果。 再来分析一下上面扩容方式的优缺点 - 优点: 一次性扩容,可扩容任意多机器数,扩容期间缓存不失效,对后端无冲击。 - 缺点:读逻辑变复杂,3台扩6台之后,有一半的缓存需要读两次才能读到,并且所有缓存不存在的情况都需要读两次才能最终确认。 大部分情况下,读缓存的时间远小于读后端的时间,可以推测,两次读缓存的时间也是远小于读后端的时间的,这种情况下读两次带来的额外开销可以忽略。 新的扩容方式还有优化的空间,当在H2读不到的时候,会再去H1读,因为我们H2集群里面已经包含了H1集群,如果在H2读的是A,B,C三台机器,那么就不需要再去H1集群读,因为即使去H1读,也是同一台机器,结果是一样的。优化之后每台机器的读写请求数不会比扩容前高。 对于简单哈希,也可以使用上面的方法进行优雅扩容。

Other

为了不让树莓派继续在家吃灰,周末又拿出来折腾了一下。apt-get升了个级,300M+的更新之后竟然没挂!可以继续折腾。。 家里电信宽带,路由器admin密码有待破解,在外网如何方便控制家里的树莓派成了一个问题。 目前有个小vps,可以作为中继连接树莓派,有几种方案选择: ### 1. 首选VPN 这个很简单,vps上搭建一个vpn,然后树莓派和控制端都连上vpn,那么树莓派和控制端就在同一个局域网内部了,可以直接ssh连接的。 网上找到[这样的方法](http://lukin.cn/201312/Ubuntu_command_How_to_connect_VPN/)连vpn:`sudo pptpsetup --create vpnname --server ip --username test --password test --encrypt --start`,但是树莓派一执行这个命令就断网,原因未知,只能重启。 ### 2. 通过ssh隧道 原理也很简单,假设vps地址是`1.1.1.1`,树莓派通过ssh连接到vps,同时将vps上某个端口比如`1234`映射到树莓派的ssh端口比如`22`,这样在vps上访问`1234`端口就相当于访问树莓派的`22`端口,命令很简单: ``` bash $ ssh -f -N -R...

ssh
树莓派

一直觉得IFTTT是一个很伟大的产品,它将互联网的世界串了起来,用 `if (this) then {that}` 这样简单又强大的语句对世界编程,我们的生活将更加丰富多彩。 最初的IFTTT Channel比较少,我们能做的比较有限,今天登陆[IFTTT](https://ifttt.com/channels)发现Channel数竟然达到了121个之多,有几个是非常好玩的。 最明显的是增加了移动客户端,使用安卓或IOS安装IFTTT客户端之后,我们不仅能在客户端上创建和修改`recipes`,客户端也成了一个`trigger`或者`Action`。比如对安卓客户端来说,我们可以使用以下触发器: 1. wifi的连接和断开,我们可以指定当连接某个wifi,比如公司wifi或家庭wifi的时候,做一些事情,比如进入公司wifi自动将手机调成静音,断开家庭的wifi的时候自动将电源关闭等。 2. 位置信息,通过定位,当你进入或离开某个区域的时候自动做一些事情。 3. 通话记录和短信,未接电话,收到短信等都可以触发一些其他事情,比如自动发邮件提醒有未接电话,自动备份所有短信等。 4. 拍照或截图之后自动分享或保存 移动设备可以触发的`Action`: 1. 设备提醒,包括响铃,震动等 2. 修改系统设置,比如调节音量,切换壁纸等。 3. 发送短信,拨打电话 智能硬件最近很火,IFTTT上也有一些关于智能硬件的,比如说谷歌眼镜,安卓智能手表(手环),智能开关等有这些硬件的支持,可以做更多有趣的事情,比如下班回家离开公司自动打开家里的电源开始煮饭,比如晚上十点自动打开热水器烧水洗澡,比如晚上回家手机连接家里wifi之后自动打开客厅的灯,有智能开关,一切变得简单。 关于提醒的Channel更丰富了,比如`pushover`,`pushbullet`,`instapush`等,有了这些专业消息推送工具,可以让信息提醒更加简单和实时。比如gmail收到某人的一封重要邮件自动推送到手机或其他客户端,比如社交网站有动态自动提醒,比如当天气突变的时候发送提醒等。 关于文章、RSS、阅读、笔记等Channel也是非常高效和实用。比如我们可以用`feedly`来订阅感兴趣的文章,`if (feedly有更新) then (自动推送到pocket)`,然后我们就可以在pocket上阅读文章,`if...

IFTTT