python 进程内存增长问题, 解决方法和工具

表现

运行环境:

# uname -a
Linux ** 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

# python2 --version
Python 2.7.5

# cat /etc/*-release
CentOS Linux release 7.2.1511 (Core)

python程序在长时间(较大负载)运行一段时间后, python 进程的系统占用内存持续升高:

# ps aux | grep python2
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     124910 10.2  0.8 5232084 290952 ?      Sl   Mar17 220:37 python2 offline.py restart
#                                 ~~~~~~
#                                 290M 内存占用

这里的python进程在经历大量请求处理过程中, 内存持续升高, 但最终负载压力下降之后, 内存个并没有下降.

解决方法

为了节省读者时间, 这里先给出结论, 后面再记录详细的排查步骤.

我们分几个步骤逐步定位到问题所在:

  • 首先确定当时程序在做什么, 是否有异常行为.
  • 排除行为异常之后, 查看python的内存使用情况, 是否所有该回收的对象都回收了.
  • 排除垃圾回收等python内部的内存泄漏问题后, 定位到时libc的malloc实现的问题.

而最后的解决方法也很简单, 直接替换malloc模块为tcmalloc:

LD_PRELOAD="/usr/lib64/libtcmalloc.so" python x.py

Continue reading >>>

xp的分布式系统系列教程之: Erasure-Code: 工作原理, 数学解释, 实践和分析.

文字版: Erasure-Code: 工作原理, 数学解释, 实践和分析

Continue reading >>>

xp的分布式系统系列教程之: Erasure-Code: 工作原理, 数学解释, 实践和分析.

在分布式系统中, 第1个要解决的问题是可靠性问题, 因为服务器会宕机,磁盘会掉,光纤会被挖掘机铲断, 机房会被大雨淹没. 数据存储多份才可以达到工业可用的可靠性.

一般还必须让数据的多个副本分布在不同的服务器, 机架或机房里才能真正达到高可靠.

数据可靠了之后才需要讨论数据的一致性和性能等问题. (也可能, 一致性和可靠性是并列第一的😄).

提高可靠性也很直接, 一般的解决方式就是 对一份数据存储多个副本.

副本数一般选择3:
3副本结合当前经验上的磁盘的损坏率(大约是年损坏率7%), 大约可以达到一个工业可接受的可靠性, 这个可靠性的预期大约是11个9以上(99.999999999%的概率不丢数据).

下图摘自 backblaze发布的硬盘故障率统计

如果我有2块数据(每块可能是1个1G大小的电影): $ d_1 $ 和 $ d_2 $, 为了达到高可靠性, 我需要对每个数据复制3份, 并放在不同的服务器上以保证足够分散, 数据在服务器上的保存的位置大概是酱:

第1列的$d_1, d_2$在第1个服务器上,
第2列的$d_1, d_2$在第2个服务器上,
第3列的$d_1, d_2$在第3个服务器上.

这种3副本的策略下, 总的存储冗余度是 300%
空间浪费是200%

当然有些时候为了降低成本, 只存储2个副本, 这时冗余度是200%, 也浪费了1倍的空间:

那么, 能否用较少的冗余, 来实现同样较高的可靠性, 就成了分布式存储的一个重要研发的方向.

这就是本文介绍的 EC 需要解决的问题. 接下来, 我们通过几个例子, 1步步介绍 EC 的原理和实现机制.

EC的基本原理

栗子🌰1: 实现k+1的冗余策略, 大概需要小学3年级的数学知识

Q: 有3个自然数, 能否做到再记录第4个数字, 让任何一个数字丢失的时候都可以将其找回?

这个问题很简单, 记录这3个数字的和: 假设3个数字是: $ d_1, d_2, d_3 $; 再存储一个数: $ y_1 = d_1 + d_2 + d_3 $.

  • 存储的过程就是存储这4个数字: $ d_1, d_2, d_3, y_1 $:

  • 数据丢失后要找回时:

    • 这样如果 $ d_1, d_2, d_3 $ 任意一个丢失, 例如 $ d_1 $ 丢失了, 我们都可以通过 $ d_1 = y_1 - d_2 - d_3 $ 来得到 $ d_1 $.

    • 如果 $ y_1 $ 丢失, 则再次取 $ d_1 + d_2 + d_3 $ 的和就可以将 $ y_1 $ 找回.

在上面这个简单的系统中, 总共存储了4份数据, 有效的数据是3份.
冗余是133%, 它的可靠性和2副本的存储策略差不多: 最多允许丢失1份数据.

看起来这是一个不错的解决方案:
我们用133%的冗余, 实现了和2副本的 200% 冗余 差不多的可靠性! 如下:

策略 冗余度 可靠性 存储策略示意
2副本 200% 允许丢1块: $ 1 \times 10^{-8} $ $ (d_1,d_1) $, $ (d_2,d_2)$, $(d_3,d_3)… $
3+1求和冗余 133% 允许丢1块: $ 6 \times 10^{-8} $ $ (d_1, d_2, d_3, y_1) $, $ (d_4, d_5, d_6, y_2)… $

这里只是差不多, 还并不是完全一样, 后面分析 1节会详细讨论可靠性的计算.
在讨论可靠性的时候, 一般数据丢失风险没有量级的差异, 就可以认为是比较接近的.

上面这个例子是和我们平时使用的 RAID-5 基本是一样的. RAID-5 通过对k个(可能是11个左右)数据块求出1份校验和的数据块. 存储这份校验块, 并允许1块(数据或校验)丢失后可以找回.

当然在工程上, RAID-5 的计算和自然数的求和还有些差异. 后面继续撩.

以上的这种求和冗余策略, 就是 EC 的核心思路.


如果你对存储感兴趣但不希望太多深入细节, 到这里差不多已经比较直观地了解 EC 的原理了.

如果你还有浓厚的兴趣继续读下去,好极!

接下来将上面的内容做些扩展, 后面章节会继续深入1点点, 逐步把上面提到的求和冗余 推广成1个生产环境可用的存储策略, 同时也会逐步引入更多的数学来完善这个策略.

Continue reading >>>

可靠分布式系统基础 Paxos 的直观解释

Paxos 已经逐渐被承认是分布式系统中不可缺少的核心算法, 越来越多的分布式系统都是以paxos或其变种来达到强一致性的.

本文是一篇paxos入门教程, 从基本的分布式中的问题: 主从复制,quorum-rw等算法出发, 通过逐步解决和完善这几个问题, 最后推导出paxos的算法.

本文分为2个部分:

  • 前1部分是分布式一致性问题的讨论和解决方案的逐步完善, 用比较通俗的语言得出paxos算法的过程. 如果你只希望理解paxos而不打算花太多时间深入细节, 只阅读这1部分就可以啦.

  • 第2部分是paxos算法和协议的严格描述. 这部分可以作为paxos原paper的实现部分的概括. 如果你打算实现自己的paxos或类似协议, 需要仔细了解协议细节, 希望这部分内容可以帮你节省阅读原paper的时间.

View it on slideshare.net!

Download the paxos.pdf

在线查看 html 版本 paxos.html

可靠分布式系统基础 Paxos的直观解释 from Yanpo Zhang

Continue reading >>>

socket关闭: close()和shutdown()的差异

对于一个tcp连接,在c语言里一般有2种方法可以将其关闭:

close(sock_fd);

或者

shutdown(sock_fd, ...);

多数情况下这2个方法的效果没有区别,可以互换使用。除了:

  • close() 是针对file的操作
  • shutdown() 是针对socket的操作

nix系统里socket是1个文件,但文件不1定是1个socket;

所以在进入系统调用后和达到协议层前(发出FIN包这一段), close()和shutdown()的行为会有1点差异。

Continue reading >>>

随手改变世界之 git-auto-squash

使用git的同学是不是经常纠结于在开发过程中是应该频繁提交, 还是仔细构造提交点之后再提交?

Numbers Programmers Should Know About Hash

There is a hash table:

  • It has b buckets.
  • It has n keys stored in it.
  • We assume that the hash function distributes keys uniformly.
  • A bucket can contain more than 1 keys.

If n b, the hash table would look like this:

  • 37% buckets are empty.
  • 37% buckets contain 1 key.
  • 26% buckets contain more than 1 key, which means collision occurs.

The following chart created by program simulation shows distribution of 20 keys over 20 buckets.

Continue reading >>>

Vim-tabbar: Simple, stupid and fast tab-bar for VIM

Simple, stupid and fast tab-bar for VIM.

Continue reading >>>

1% 慢请求优化

1个客户端同时向服务器发出100个请求,等待所有的请求都返回才算成功。
99%的请求10ms返回,1%的请求1000ms返回.

假设慢请求的概率是 ,请求总数是 . 能快速(10ms)返回的概率有多少?如何优化?

Continue reading >>>

Some useful resources

Google HTML CSS 代码风格指南

A Note About Git Commit Messages

Google’s Python Class

Continue reading >>>

jobq.py -- Queue processing engine

jobq.py processes serial of input elements with several functions concurrently and sequentially.

Check out on github: jobq.

Example:

import jobq

def add1( args ):
    return args + 1

def multi2( args ):
    return args * 2

def printarg( args ):
    print args

jobq.run( [ 0, 1, 2 ], [ add1, printarg ] )
# > 1
# > 2
# > 3

jobq.run( ( 0, 1, 2 ), [ add1, multi2, printarg ] )
# > 2
# > 4
# > 6

Continue reading >>>