常见编程问题及对策

28
常常常常常常常常常 —— 常常常常 常常常常常 常常 、、

Upload: hoopchina

Post on 21-Dec-2014

2.201 views

Category:

Education


11 download

DESCRIPTION

虎扑常见编程问题集及其对策

TRANSCRIPT

Page 1: 常见编程问题及对策

常见编程问题及对策

—— 编码失误、并发竞争、雪崩

Page 2: 常见编程问题及对策

我们的目标是• 没有蛀牙 (defects)• 大并发、低延迟– 多 Web server– 多 Mysql M/S Replication– 缓存技术– NoSQL 存贮

Page 3: 常见编程问题及对策

案例研究

Page 4: 常见编程问题及对策

大家来找茬 I

[ 大家来找茬 I]-------------------------------$hitsKey = "hitsKey:$artid";$hitsWriteKey = "hitsTimestamp:$artid"; $currentTime = time();

$num = $tt->get($hitsKey);$lastWriteTime = $tt->get($hitsWriteKey);if (!$lastWriteTime){ $lastWriteTime = $currentTime; $tt->put($hitsWriteKey, $lastWriteTime);}

$num++;

Page 5: 常见编程问题及对策

大家来找茬 I// [-MASKED-]if (($currentTime - $lastWriteTime) > 1800){ $sql = 'UPDATE tbl1 SET hits='. $num. ' WHERE aid = '. $artid; $db->exec($sql); $tt->put($hitsWriteKey, $lastWriteTime);}

$tt->put($hitsKey, $num);

Page 6: 常见编程问题及对策

大家来找茬 II

[ 大家来找茬 II]-------------------------------//[-MASKED-]if (getgpc('-MASKED-') == $cache->get('-MASKED-'.getgpc('id'))) { exit('1');} elseif($var = $Model->foo(getgpc('id'), getgpc('-MASKED-'))) { exit('1');} else { exit('-2');}

Page 7: 常见编程问题及对策

编码失误• [ 大家来找茬 I]– 未正确更新缓存时间戳

• [ 大家来找茬 II]– 未正确更新缓存 -MASKED- 值

• 新版 GoalHi 中也出现一次失误– 从缓存中取数据却未使用,仍从数据库中再次

读取

Page 8: 常见编程问题及对策

编码失误 : 对策• 单元测试– 脚本化自己的测试– 功能测试– 回归测试

• Xhprof– 多执行几次,比较执行时间– 启、禁用缓存条件下的执行时间

Page 9: 常见编程问题及对策

缓存雪崩 I

• [ 大家来找茬 I]– 缓存时间戳放置得太晚 , 并发量大时会雪崩

Page 10: 常见编程问题及对策

缓存雪崩 I: 对策• [ 大家来找茬 I]– 尽量早地写入 , Renew

Page 11: 常见编程问题及对策

竞争条件• [ 大家来找茬 I]– 点击量写入太晚,并发量大时会出现竞争

Page 12: 常见编程问题及对策

竞争条件 : 对策• 如果可以,使用 incr/decr/incrby/decrby– 原子操作– Redis/TT/Memcache 都支持

• 否则 :– 乐观锁

• 适用场景– 悲观锁

• 适用场景• 否则 :– 读取后,尽快修改并写入

Page 13: 常见编程问题及对策

大家来找茬 III

[大家来找茬 III]------------------------------- $count1Id = 'count_1';if (! $count1= $cache->load ( $count1Id )) { $count1 = $model->foo1(); $cache->save ( $count1, $count1Id );}$count2Id= 'count_2';if (! $count2 = $cache->load ( $count2Id )) { $count2 = $model->foo2(); $cache->save ( $count2, $count2Id );}$count3Id= 'count_3';if (! $count3 = $cache->load ( $count3Id )) { $count3 = $model->foo3(); $cache->save ( $count3, $count3Id );}…

Page 14: 常见编程问题及对策

编码失误• [ 大家来找茬 III]– 缓存时间因失误都为 3600s ,导致很多缓存

项同时过期,引发冲击

Page 15: 常见编程问题及对策

缓存雪崩 II

• [ 大家来找茬 III]– 并发访问大时,大量缓存项的同时失效– 超级雪崩

Page 16: 常见编程问题及对策

缓存雪崩 II: 对策• GetOrLock 延迟过期• 延期删除

Page 17: 常见编程问题及对策

缓存雪崩 II: GetOrLock

• 服务器端方案– Cons:

• Fork 代码,增加维护成本• 请大家务必不要 Fork 上游代码

– Zend Framework– Hoop Framework

• 封装优于补丁• 客户端方案– 感谢 鹏鹏、亮亮 ( 排名不分先后 )– Pros– Cons

Page 18: 常见编程问题及对策

缓存雪崩 II: 延期删除• 步行街– 频繁更新

• Memcache– DELETE 改进

• Redis– EXPIRE

Page 19: 常见编程问题及对策

往返延迟• [ 大家来找茬 III]– 单个单个地从缓存中取值,往返性能低

Page 20: 常见编程问题及对策

往返延迟 : 对策• Multi GET ,避免往返时延

Page 21: 常见编程问题及对策

其他问题

Page 22: 常见编程问题及对策

文件缓存• GoalHi 项目– 新人未经培训,采用文件缓存技术

Page 23: 常见编程问题及对策

为什么文件缓存是个坏主意 ?

• 分布式环境– NFS 性能偏低

Page 24: 常见编程问题及对策

重复代码• 每个项目都有自己的一套“框架”拷贝– Zend Framework– 全站脚本、 CSS

• 部署 / 升级麻烦• 一致性问题

Page 25: 常见编程问题及对策

重复代码 : 对策• Photo 项目案例 :– Zend Framework 作为一个公共库• /path/to/zend/Zend• 已在 include path 中,可以直接包含

• Hoop Framework– 作为一个公共库部署

• 新人入职培训– 让新人符合 Hoop 编程习惯

Page 26: 常见编程问题及对策

长连接、持久连接• 多服务器多进程环境下– 通常立刻带来问题– 大量的连接挂在 Server 上• 10 * 500 = 5000 连接

– 对于多线程服务器是灾难• 保持 5000 个线程环境,以及其它资源

– 对于事件驱动的服务器也会影响性能• 同时 e/poll 大量的连接• TCP 连接本身在内核亦会占用非分页内存

Page 27: 常见编程问题及对策

长连接、持久连接 : 对策• 短连接– 使用时连接,用完立即关闭

• 连接池– 编码仍按”短连接”方式– SqlRelay– RedisProxy

Page 28: 常见编程问题及对策

谢谢 !