Memcache 内存分配策略

Page 是内存分配的最小单位

Memcached的内存分配以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定。

如果需要申请内存时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配(page ressign已经从1.2.8版移除了)

Chunk 是存放缓存数据的单位

启动时有一个成长因子,默认是1.25,按容量大小递增,所以会产生碎片空闲

Slab 是一堆相同大小 Chunk 的容器

Memcached在启动时通过 -m 指定最大使用内存,但不会启动就占用,是动态按需分配。

slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储数据。

Slab/Page/Chunk 之间的关系

为什么总内存没占满,还会触发LRU机制?

我的理解是,当全部内存已经被slab以page方式申请了,化为了chunk,内存容器全划分好后,不会回收。 例如:100字节的chunk所在slab中因为key过期释放了空间,但200字节的chunk所在的slab是满的。所以总和上看,内存没有占满。

现在有一个200字节的数据要存入,就会发现存不进200字节的chunk,此时就会触发LRU机制自动清理200字节chunk中的数据。即使100字节chunk所在slab还有不少内存空闲。

PHP-FPM 的进程数动态和静态

PHP-FPM 的进程数动态和静态

  • pm = static
  • pm.max_children:静态方式下开启的php-fpm进程数量
  • pm.start_servers:动态方式下的起始php-fpm进程数量
  • pm.min_spare_servers:动态方式下的最小php-fpm进程数量
  • pm.max_spare_servers:动态方式下的最大php-fpm进程数量

1. 如果 pm=static,那么其实只有pm.max_children这个参数生效

系统会开启设置数量的php-fpm进程。

2. 如果 pm=dynamic,那么pm.max_children参数失效,后面3个参数生效。

系统会在php-fpm运行开始的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

如何选择

事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。

  • 内存紧张的机器或VPS,用动态的。
  • 内存充裕的,完全可以用静态的,因为这样不需要进行额外的进程数目控制,会提高效率。因为频繁开关php-fpm进程也会有时滞。

并发量的估算

pm.max_children = 32G (机器物理内存) / 64MB (每个进程的 memory_limit) = 256

假设一个PHP请求耗费250ms,那一个进程1秒就可以处理4次请求

那一台32G的机器可支撑的QPS = 4 * pm.max_children = 2000~3000

带宽的预估

并发请求数 * 5K (单次请求耗费流量,单位字节) / 1000 * 8 = 带宽 (单位比特)

参考

(总结)Nginx使用的php-fpm的两种进程管理方式及优化

MySQL 配置文件 my.cnf 的读入顺序

首先查看当前运行 mysql 进程时有没有指定 my.cnf 文件:

ps aux | grep mysql | grep 'my.cnf'

如果上面的命令没有输出,表示启动 mysql 时没有指定 --defaults-file=/path/to/my.cnf

敲入以下命令,得知 my.cnf 的默认搜寻顺序:

mysql --help | grep 'Default options' -A 1

会显示如下信息:

Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf

我们依次到以上目录去找是否有对应的 my.cnf。

如果启动 mysql 既没有设置 --defaults-file,默认的读取目录也找不到 my.cnf 文件,那么表示 mysql 启动时没有任何加载配置文件,而是使用默认配置。

MacOS 里通过 homebrew 安装的 MySQL 5.7 缺省启动时就没有加载 my.cnf

敲入 locate my*.cnf 可列出所有 my.cnf 文件:

/usr/local/Cellar/mysql/5.7.14/support-files/my-default.cnf
/usr/local/Cellar/mysql/5.7.14/mysql-test/include/default_my.cnf
/usr/local/Cellar/mysql/5.7.14/mysql-test/suite/federated/my.cnf
/usr/local/Cellar/mysql/5.7.14/mysql-test/suite/ndb/my.cnf
.....

第一个 support-files/my-default.cnf 是我们要找的,把它复制到 /usr/local/etc/ 下。

cp /usr/local/opt/mysql/support-files/my-default.cnf /usr/local/etc/my.cnf

重启 mysql 后配置即可生效。

sudo launchctl unload -w /Library/LaunchDaemons/homebrew.mxcl.mysql.plist
或
launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

PHP 静态延迟绑定

简单复习一下 PHP 静态延迟绑定

发现有时候一些知识长久不用就容易生锈,比如“静态延迟绑定”,这个名词现在听上去竟有些陌生,其实这个特性在我们项目早已大量使用了 原理相当于就是在子类继承父类时,会把父类中的 self 关键字全文替换为父类的类名,实现绑定。

所以当我们在子类中调用 self::xxx 时,其实调用的还是父类的成员,要想调用子类自己的,需要使用 static::xxx 关键字

摘抄一段大航海项目中的代码做示例:

class Model_Npc_Abstract extends Core_Model_Abstract
{
    public function __construct($npcId)
    {
        if (! $npc = static::read($npcId)) {
            throws(_('指定NPC不存在。') . 'NpcId:' . $npcId);
        }

        $this->_prop  = $npc;
    }
}

class Model_Npc_Regular extends Model_Npc_Abstract
{
    /**
     * 加载 NPC 详情
     *
     * @param $npcId
     * @return array
     */
    public static function read($npcId)
    {
        if (! $npc = Dao('Static_NpcRegular')->get($npcId)) {
            return array();
        }

        return $npc;
    }
}

父类 Model_Npc_Abstract 中如果写的是 self::read() 则会提示找不到 read 方法

使用一款语法高亮插件 highlight.js

这款语法高亮插件叫做 highlight.js 使用非常简单,支持多种主题,其中有我们非常亲切的 monokai_sublime

我们现就来试试吧

class Model_MainTask_Abstract extends Core_Model_Abstract
{
    public function __construct($taskId)
    {
        if ($taskId < 1) {
            throws403('Invalid MainTaskId');
        }

        if (! $task = Dao('Static_MainTask')->get($taskId)) {
            throws(_('主线任务静态配置缺失。') . 'MainTaskId:' . $taskId);
        }

        $this->_prop = $task;
    }
}

很酷吧,使用方法如下:

1、先引入JS并初始化

<link rel="stylesheet" href="/res/js/highlight/styles/monokai_sublime.css" />
<script src="/res/js/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

2、这样括起代码

或者这样括起:

<pre><code class="html">...</code></pre>

搭建 Github 博客小记

搭建小记

终于是把 Github 博客搭建起来了,第一次用 Markdown 写文章,记录一下过程,也不枉白折腾这几天。

前几个月听孙大力同学说起 Markdown 这东西好酷好炫,我当时还没什么感觉,认为只是一个语法转换器,就像以前BBS发帖的UBB代码一样。这几天我才知道,原来 Markdown 在程序员的世界里已经风靡了好多年,就像 Github 的 Readme.md 就是这个语法,我 ….. 我竟然不知道,我实在太落伍了,赶紧动手看看吧。

准备编辑器

1. Sublime 安装 Markdown 编辑插件

我用的是一款叫 MarkdownEditing 的插件,它可以让你在 Sublime 里写 Md 格式文件时有部分的所见所得的功能,前提是先要保存为 md 后缀的文件。

另外一个插件叫 MarkdownPreview,可以直接把 md 转换为 html 格式。

2. 学习 Markdown 的语法

其实 Markdown 语法特别简单,也就十几种,更多的请猛戳这里

3. 学习 Jeklly 的语法

Jekyll 是一款 Github 自带的页面转换器,相当于是我们本地按照他的语法和规则写好代码,提交后,GitHub 会自动运行 Jeklly 把我们的代码转换成可读的 HTML

它的中文官网在这里:http://jekyllcn.com/ 一些约定的文件位置、文件名规则、系统变量详情、判断的写法、循环的写法,文档里都有写,或者谷歌搜索。

基本文件夹结构如下:


|-- _config.yml
|-- _includes
|-- _layouts
|   |-- default.html
|   |-- post.html
|-- _posts
|   |-- 2007-10-29-why-every-programmer-should-play-nethack.textile
|   |-- 2009-04-26-barcamp-boston-4-roundup.textile
|-- index.html

构建环境

1. 第1种机制,让 silverd.github.io 为博客地址

本意是针对个人或组织提供的页面

在 GitHub 里新建一个名称为 silverd.github.io 的版本库(系统约定,不能改) 然后本地检出,往它的 master 分支里提交代码。

目前我采用的就是这种机制,缺点是整个项目就这能有一个主页。 要注意的是 _config.yml 里的 baseurl 要设为根目录 /

1. 第2种机制,让 silverd.github.io/blog 为博客地址

本意是针对 Project 提供的项目主页

在 GitHub 里新建一个名称为 blog 的版本库(相当于二级目录) 然后本地检出,往它的 gh-pages 分支里提交代码。

这里要注意 _config.yml 里的 baseurl 要设为根目录 /blog,同时页面中引入的一些 css/js 的路径,也必须为 /blog 或者直接写相对路径

主题风格

博客的页面一定要简单明晰,因为关键是内容,所以我想要一款清新大气的,找了一圈,这款还算不错,感谢作者,想要的同学可以 fork 他:

评论系统

使用第三方评论系统,国内的有 duoshuo,国外的有 disqus。 原理都很简单,就是用 js 生成一个 iframe 内嵌第三方的评论页,独立标识为当前页面的 windows.location.href

这些让我想起了3年前自己写的享乐SNS通用评论回复组件,原来小功能还可以做这么大。

独立域名绑定

在项目根目录建立一个 CNAME 文件(全大写),里面内容为 silverd.cn,不要多余文字,而且只能填一个域名

如果是顶级域名

把 silverd.cn 绑定A记录到 192.30.252.153 和 192.30.252.154(IP是官方提供的,可能有变化,点这里查看最新

如果是二级域名

把 blog.silverd.cn 绑定CNAME到 silverd.github.io

TortoiseGit 的使用

Windows 上使用 Github 手记

简而言之,先安装 msysgit,再安装 TortoiseGit

提交免输入密码的办法

  1. 开始菜单-打开PuTTYgen(密钥生成器),鼠标滑动,生成秘钥并保存为 ppk
  2. 把生成的公钥贴到 GitHub 网站上去 Account Setting -> SSH Public Key
  3. 开始菜单-打开Pageant,把刚才保存的 ppk 添加进去
  4. 重新 git clone 一份代码,勾上 Auto load putty key

参考文章