最近 3 个月变化很多,离开呆了 5 年多的北京,开始英国的工作和生活。到这之后基本在做系统向亚马逊云平台的迁移,踩了不少坑,收获也很多。由于系统的迁移涉及各个常见的架构组件,边边角角的细节很多。和大部分系统一样,长时间野蛮成长积累了很多问题。这样的老系统迁移到新平台意味着你需要处理所有之前埋下的问题。公司之前聘请了亚马逊推荐的第三方咨询服务工作在做迁移,但是由于问题太多,拖了很长时间没有完成。

成熟老系统常见的问题:

1. 缺乏文档

这应该是大小公司都存在的问题。文档会极大降低开发效率,并且互联网项目的特点是易变和追求速度,详细文档不是很好的方案。这就要求方案和细节设计上的合理性和不要做 “精巧”方案。结构化设计,不要零散的组成,这样其他人即使没有文档也可以理解。

2. 项目中临时方案太多

导致后来看起来很别扭而且不容易理解;半截工程。系统中存在大量“精巧”的设计,导致后来者难以理解。这也告诉我们做设计的时候尽量简单通俗易懂,项目设计的可沟通性也是很重要的一方面。某位工程师说自己花了 1 周的时间才搞明白 Postfix 的收邮件并自动解析的过程是怎么运行的。

3. 代码质量参差不齐

代码质量问题每个大点的团队都没法保证,保持代码库的干净很重要。

4. 繁杂的业务

5. 代码的 BUG 和代码对环境的兼容性

之前的系统使用配置文件做主从读写分离,配置文件由其他系统控制。但是配置文件确保留在代码库中,这意味着假如代码回滚或者 check 分支出错,配置文件会发生改变。不该发生的全会发生,这样的事情确实发生了。导致部分操作写入从库,从库与主库同步失败,典型的脑裂问题。最后只好花了很长时间重做从库的同步。这样的问题处理并不复杂,复杂的在于如何发现这个问题的原因。业务系统各种奇怪的表现,有时候很难想到问题的根源。

迁移过程需要考虑的问题:

1. 完善测试

性能测试可以采取流量镜像复制,读操作有很多简单可靠的流量复制工具,有时候根本不需要一个高大上的流量复制系统。并且大部分系统都是读多写少,测试不是什么难题。

功能性测试只能尽量做足,让熟悉系统的用户进行。

2. 无缝迁移

整个过程基本实现了平滑无缝迁移,系统的没有停止 1 分钟运行。由于项目的特点,比较少写操作,重点是读,暂停写操作后作将 HaProxy 后端逐步指向新集群,等全部流量导入新集群后修改 DNS 指向新集群。这里还涉及到 DNS TTL 从长变短再变长的修改过程。

缓存预热很重要,尤其是数据库的预热,这就要求新集群流量导入逐步进行,防止对整站延迟的影响。

3. 回退方案

由于暂时停止写操作,即使流量导入到新集群后测试发现问题仍然可以指回旧集群。

4. 改进还是保持原状

由于架构组件的选择余地很大,之前的各个组件的配置是否合理需要很长时间 Review。这里就要权衡保持原状还是一次性做好优化。比较好的方案是如果不是 BUG 则保持原状,等系统完成迁移再进行改进。

5. 性能的持续监控和对比测试

性能监控工具已经非常成熟了,比如 AppNeta 和 New Relic , 基本可以把控各个组件的性能。在迁移之前也可以进行镜像流量复制对比测试新旧集群的性能。

迁移带来的收益

1. 重新设计的发布自动化

业务代码、系统配置、云架构配置的分离,任何操作的版本化,可回退。

2. 弹性扩展,总体成本的降低

迁移到亚马逊的主要原因就是高低峰流量差异很大。迁移后低峰期可以节约 1 半的机器成本。

3. 跨区域容灾,无单点故障

实现了 Multi-AZ,任意单点故障不影响业务运行。Web 前端服务器可以随手关掉,数据库的升级,配置改动也无任何影响,当然这归功于 RDS Multi-AZ 功能。

4. 运维难度的降低,无需运维

系统会自动根据负载进行增减机器,所以无需担心压力大把机器打垮,单机器的各种故障也无需人工处理。

关于亚马逊云平台的特点和各个组件以后还会有单独的文章详细解释。

当产品经理告诉你,修改了产品的某个特性让销售提升了 10%;或者推荐引擎经理告诉你,我们的推荐引擎让销售提升了 5%。你会怎么想?我们的团队真 NB 还是这些数字真的该相信吗?

A/B 测试现在用得非常广泛,但是大部分时候却不一定让你得出正确的结论。最常见的就是对产品改进的评估,或者对推荐系统效果的评估。

  • A/B 测试经常犯的错误和问题
  • A/B 测试并不是最好的数据化产品设计方式

A/B 测试经常犯的错误和问题

错误 1. 没有考虑多影响因素:

A/B 测试最大的问题是,需要保证其他因素不变的情况下,改变并观察单因素带来的变化。不可以同时修改和测试多个不同的特性,因为他们之间有可能会相互影响;这种方式的前提是访客没有明显变化,比如在电视上大量投放广告后对产品的 A/B 测试结论就不得不考虑访客的明显变化,测试结论不可采信。

A/B 测试中值得考虑的几个影响因素:

  • 广告投放,季节性,节假日,用户来源渠道变化等引起的访客的变化和访客行为的变化
  • 同时进行的多个 A/B 测试特性的相互影响
  • 其他环境的变化

错误 2. 取用了不合理的样本量

新产品或者产品初期用户量并不是很大,可能会因为样本不足导致 A/B 测试结论不可信。

A/B 测试样本量计算器: http://www.evanmiller.org/ab-testing/sample-size.html

错误 3. 测试时间不足

测试时间不足导致的结果和样本量不足类似。

使用 GA 进行 A/B 测试统计的方法:

http://biasedbit.com/ab-testing-jekyll-ga/

假如不考虑这些因素只看 A/B 测试的结果,理论上可以得到任意结果,比如在结果好的时候停止测试,或者效果不好的时候延长测试时间。
个人认为推荐引擎的结果可以和随机展现的结果进行对比说明销售提升多少或者占比多少,而不是单纯看绝对值。
可能有些老板看到这里会恍然大悟,之前被推荐引擎和产品经理忽悠了多少次啊 :)

比 A/B 测试更好的数据化产品设计方式

A/B 测试的设计大致是这样:将用户分为两组,一组继续使用旧的产品特性 A;另一组使用新的产品特性 B (比如更大的按钮)。过一段时候后通过查看统计数据,看哪组人点击按钮的概率更大。

这种方式听起来确实不错,很多新药物的测试就是用的这种方式:一组人服用药物;另一组人服用糖水。

比 A/B 测试更好的方式:

1. 可以同时比较多于 2 个选择
2. 新的选择可以随时增加和减少

一种更有效的方式叫做 epsilon-greedy 方法:

我们只用 10% 的时间随机选择;90% 的时间使用期望值最高的选择:

def choose():
    if math.random() < 0.1:
        # exploration!
        # choose a random lever 10% of the time.
    else:
        # exploitation!
        # for each lever, 
            # calculate the expectation of reward. 
            # This is the number of trials of the lever divided by the total reward 
            # given by that lever.
        # choose the lever with the greatest expectation of reward.
    # increment the number of times the chosen lever has been played.
    # store test data in redis, choice in session key, etc..

def reward(choice, amount):
    # add the reward to the total for the given lever.

Epsilon-greedy 方法不仅可以用在产品设计上,还可以用在高可用集群的负载方式设计上,比如 bitly 就实现了 epsilon-greedy 的高可用集群池的管理组件。

这种方式的好处出来满足之前说的 2 点之外,可以得到每个设计的转化率;还实现自动化的产品迭代,当然这需要你的产品设计足够组件化。
但是之前这种方式和 A/B 测试一样,不可以同时修改和测试多个不同的特性。

曾经想过将这些做成产品或者服务,但是这的问题是:不了解的人,或者规模小的团队不需要这样的服务,了解或者需要并且规模大的团队自己就有实力将这实现。所以我将之前整理的想法分享出来,希望对你有用。

有用的链接:

http://stevehanov.ca/blog/index.php?id=132
https://github.com/bitly/go-hostpool
http://biasedbit.com/ab-testing-jekyll-ga/
http://www.evanmiller.org/ab-testing/sample-size.html
http://www.evanmiller.org/how-not-to-run-an-ab-test.html
http://nerds.airbnb.com/experiments-at-airbnb/

有些链接打不开,你需要买一个 VPS,参见 http://blog.eood.cn/digitalocean-vps-vpn