Web Caching Architecture and Design

997 views

Published on

1. What web caching means and includes
2. Why use caching
3. File caching
4. Share caching
5. Local caching
6. Memcached and its usage
7. TTServer and KTServer
8. Redis VS SSDB
9. CDN cachings
10. Twemproxy
11. Programming skills and tips

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
997
On SlideShare
0
From Embeds
0
Number of Embeds
302
Actions
Shares
0
Downloads
15
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Web Caching Architecture and Design

  1. 1. 缓存架构与缓存设计 技术部 kim
  2. 2. 不讨论什么?
  3. 3. • 软件安装和介绍 • 数据结构和源码细节 • 性能报告
  4. 4. What is 缓存?
  5. 5. • 处理速度差异的技术方法 • 缓存的快,都是相对的!
  6. 6. 缓存技术包括
  7. 7. • 文件缓存(file cache) • 共享缓存(shared cache) • 软件内置缓存(local cache) • 缓存客户端(client cache) • CDN 缓存(cdn cache) • etc…
  8. 8. 重点说下 Client Cache
  9. 9. • 被动更新 • LRU机制 • 协议简单 • 基于 libevent 非阻塞 • 单点无冗余 • 独立分布式,散列,O(1)
  10. 10. Why use 缓存?
  11. 11. • 减小负载 • 减少阻塞、数据库查询、脚本执行 • 让机器顺畅工作
  12. 12. 缓存架构
  13. 13. 文件缓存
  14. 14. • 优点:部署快,安全不丢失,易于拓展 • 缺点:无法应付并发,iNode限制,磁盘短 命,管理麻烦
  15. 15. 应用(一) 好友动态日志目录 /queue/all
  16. 16. 共享缓存
  17. 17. • 优点:速度快,部署快、使用方便,运维 更新和管理容易 • 缺点:服务器间不共享
  18. 18. /dev/shm 共享 • 默认是真实内存的一半 • 进程间共享数据 • 物理内存缺乏时用swap交换数据 • 支持动态在线调整 • 无权限限制
  19. 19. 应用(一) 站内的登录秘钥:/dev/shm/secrectkey
  20. 20. 应用(二) 缓存 pfs 图片存储系统的查询结果:/dev/shm/pfs_domain
  21. 21. 内置缓存
  22. 22. • 优点:速度快,无第三方依赖,无需另开 端口 • 缺点:需要安装和配置,无法处理大缓存
  23. 23. 应用(一) • PHP 的 APC :http://php.net/apc/ • 目标文件缓存( apc_compiler_cache ) • 用户数据缓存( apc_user_cache)
  24. 24. 应用(二) -- Nginx 共享内存字典模块: lua_shared_dict -- 缓存黑词 local badwords local lcache = ngx.shared['lcache'] if lcache then badwords = lcache:get(‘KEY_BADWORDS’) end if not badwords then local hc = self:_getHttpObj() badwords = hc:get('api.xxx.com', '/API/black_word') if badwords then lcache:set(‘KEY_BADWORDS’, badwords, 600) end end
  25. 25. 缓存客户端
  26. 26. • 优点:服务期间共享,功能丰富,集群应 用,适合大缓存处理 • 缺点:Socket、带宽、内存消耗,数据丢失、 安全隐患,部署和管理成本高
  27. 27. Memcached
  28. 28. • 90%应用都有使用 • 历史悠久,性能、稳定性高
  29. 29. 应用(一) // 缓存状态值: $key = $account . '@reg_' . $regType; $userExist = $memd->Get($key); if (!$userExist) { $userLoginX = new UserLoginX(); $userExist = $userLoginX->checkExists($account); if ($userExist) { $memd->Put($key, "1", 10800); } }
  30. 30. 应用(二) // 缓存数据 $cacheKey = md5('api@db_promo@2012' . $num); $videoList = $memd->Get($cacheKey); if (!$videoList) { $data_json = Http::Get("api.xxx.com", ‘API/api_bd_list’); if ($data_json) { $videoList = json_decode($data_json, true); $memd->Put($cacheKey, $videoList, 1800); } }
  31. 31. 应用(三) // 计数器,发送限制,避免过于频繁发送信息 $data = array ("startTime" => time (), "num" => $msgNum ); $cache_key = "memKeyPreLimit" . $userid; $rs = $memd->Get ($cache_key); if (isset($rs) && is_array ( $rs )) { $startTime = $rs ["startTime"]; $num = $rs ["num"]; if ((time () - $startTime) < 900 && ($num + $msgNum < 30)) { $data = array ("startTime" => $startTime, "num" => $num + $msgNum ); } } $memd->Put($cache_key, json_encode( $data ), 900);
  32. 32. 应用(四) 保存session,比如站内验证码
  33. 33. 应用(五) // 缓存备份,多用于取接口数据的时候失败的时候不至于没数据显示 $cacheKey = 'key_list_fresh'; $backupKey = 'key_list_backup'; $videoList = $memd->Get($cacheKey); if (!$videoList) { $data_json = Http::Get(“api.xxx.com", 'API/list'); if ($data_json) { $videoList = json_decode($data_json, true); $memd->Put($cacheKey, $videoList, 1800); $memd->Put($backupKey, $videoList, 86400); // 更新备份 } else { $videoList = $memd->Get($backupKey); // 接口取失败就取备份 } }
  34. 34. TtServer
  35. 35. • 5%左右的应用在使用,comment、my、fav 在使用 • 支持 Memcached 协议的 DBM 数据库,数据 可持久化
  36. 36. 应用(一) // 取评论内容 protected function _fetchContent($key) { $result = ''; $localConfig = $GLOBALS['scfg']['tt_server']; if (!$this->_dbContent) { $this->_dbContent = new Mem($localConfig); } $content = $this->_dbContent->Get($key); $result = sprintf('%s', json_decode($content)); if (json_last_error() != JSON_ERROR_NONE) { $result = sprintf('%s', json_decode(json_encode($content))); } return $result; }
  37. 37. KtServer
  38. 38. • 10%左右的应用在使用,包括部分核心应用 • 高并发读写,持久化存储,故障恢复快
  39. 39. 应用(一) • 视频拓展信息缓存集合(类似 redis hash 的 实现)
  40. 40. Redis
  41. 41. • 50%左右的应用使用 • 数据结构丰富,极适合并发高的复杂应用。 极占内存,对开发人员要求较高。
  42. 42. 应用(一) // 用 incr、incrby、decr、decrby 实现计数器 // 限制每5秒投一票,每天投票总数是90票 $rc = $redis->get($rcKey); if (!$rc) { $redis->setex($rcKey, 1, 86400); } $rs = $redis->get($rKey); if ($rs === false && $rc < 90) { $redis->setex($rKey, 1, 5); $redis->incr($rcKey); } else { throw new Exception('留言速度太频繁啦!'); }
  43. 43. 应用(二) // 统计数字分段处理技术,对不同评论总数使用不同长度的缓存时间 $total = $redis->get($rKey); if ($total === false) { $total = $obj->getTotal($id); if ($total > 300000) { $res = $redis->set($rKey, $total); } else if ($total > 99999) { $res = $redis->setex($rKey, $total, 86400 * 72); } else if ($total > 9999) { $res = $redis->setex($rKey, $total, 86400 * 6); } else { $res = $redis->setex($rKey, $total, 3600); } }
  44. 44. 应用(三) // lpush/ltrim/lpop/lrange 模拟有限队列,用户点播单列表: $listData = getUserDianBoList($userId); foreach ($listData as $data) { $count = $redis->LPush($uKey, $data); if ($count > MAX_RECORD_NUM_PER_LIST) { $redis->LTrim($uKey, 0, MAX_RECORD_NUM_PER_USER - 1); } } …… $list = $redis -> lRange($uKey, 0, MAX_RECORD_NUM_PER_USER - 1);
  45. 45. 应用(四) -- 利用 hmset 实现实时记录最新更新的表: local rhkey = 'tb|updates' local hData = {} hData[tableIndex] = cjson.encode(currentTime) rds:hmset(rhkey, hData) …… local tableRecords = rds:hgetall($rhkey); if tableRecords then ....... end
  46. 46. 应用(五) -- 使用 hmget / hmset 实现正确的分页缓存 -- 清理所有分页:rds:hclear(hlkey) local list local hlkey = 'hashlist|' .. vid local hlfield = orderby .. '|' .. num local hResult = rds:hmget(hlkey, hlfield) if hResult and hResult[1] then list = cjson.decode(hResult[1]) else list = self:_getList(vid, orderby, num) local hData = {} hData[hlfield] = cjson.encode(list) rds:hmset(hlkey, hData) end
  47. 47. 应用(六) // 使用 zset 来实现自动排重 // 60秒内不能发表重复内容 public function checkDuplicatedCnt($vid, $content) { $redis = self::getRedis(); $md5Cnt = md5($content); $rKey = ‘cnt|‘ . $vid; $isDuplicated = !$redis->sAdd($rKey, $md5Cnt); $redis->setTimeoutOnce($rKey, 60); return $isDuplicated; }
  48. 48. 应用(七) // 使用 zIncrBy、 zRevRange 实现日排行榜功能:Top 20 $rKey = 'toplist|' . date('md', time()); $score = 1; $result = $redis->zIncrBy($rKey, $vid, $score); $redis->setTimeoutOnce($rKey, 86400); ...... $result = $redis->zRevRange($rKey, 0, 19);
  49. 49. 应用(八) -- 实时记录分表的最新更新时间及其顺序: local zsetData = {} local rzkey = ‘table|updated‘ table.insert(zsetData, updated_at) -- 更新时间 table.insert(zsetData, tablehash) -- 更新的表 rds:zadd(rzkey, unpack(zsetData)) …… local withScore = true; local tableRecords = rds:zrevrange(‘table|updated’, 0, 1000, withScore);
  50. 50. 应用(九) -- 使用 pipeline 快速生成数据: rds:init_pipeline() local zsetData = {} local hashData = {} for k,v in pairs(tblVinfo) do -- 存进有序集备用 table.insert(zsetData, v['times']) table.insert(zsetData, v['id']) -- 存进哈希集备用 hashData[tostring(v['id'])] = cjson.encode(v) end rds:zadd(rzkey, unpack(zsetData)) rds:hmset(rhkey, hashData) -- 用 mset/mget 一次设置/获取多个值,优化性能 rds:commit_pipeline()
  51. 51. 应用(十) // 用 MULTI 来实现事务,保证原子性 // 想象一下假如同一时刻刚好有2个进程都在执行 push 。。。 $pipe = $redis->multi(); foreach ($result['data'] as $value) { $len = $pipe->rPush($rKey, json_encode($value)); } $pipe->setTimeout($rKey, 600); $pipe->exec();
  52. 52. SSDB
  53. 53. • 弹幕在试用,视频人气重构也在试用 • 使用 leveldb 做存储引擎,占内存极少。支 持主主同步架构。绝大多数情况下可以替 换掉 redis ,性价比极高。
  54. 54. CDN 缓存
  55. 55. • 优点:速度快 • 缺点:成本高
  56. 56. 应用(一) // 播放页针对视频不同状态,设置不同的cdn缓存时间 if ($videoInfo['chk'] == 'n') { //未审核状态设置缓存时间600S $cdnCacheTime = 600; } else { $cdnCacheTime = 259200; } header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cdnCacheTime) . " GMT"); header("Cache-Control: max-age=" . $cdnCacheTime); header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT"); if (ob_get_length()) { header("Content-Length: " . ob_get_length()); } ob_end_flush();
  57. 57. 缓存代理 twemproxy
  58. 58. • 70% 的应用在使用,构筑在客户端缓存与 应用之间,充当桥梁 • 轻量级,配置简单,支持 memcached ascii 和 redis 协议,多点长连接,高可用性
  59. 59. 关于缓存的技巧
  60. 60. (一)区分对待查询结果
  61. 61. • 查询成功,且数据不为空 -- 长缓存 • 查询成功,但数据为空 -- 短缓存 • 查询失败 -- 不缓存
  62. 62. 应用 local res = db:findOne(sql) if type(res) == 'table' and res['id'] then rds:setex(key, cjson.encode(res), 3600) elseif db.lasterr == db.ERROR_NOT_FOUND then rds:setex(key, cjson.encode(res), 60) else -- 查询异常或者查询超时等情况 end
  63. 63. (二)缓存耦合
  64. 64. • 优点:提升系统实时性,减少系统间依赖, 增强系统稳定性 • 缺点:有违 RESTFUL 风格,修改和管理成本 增加
  65. 65. 松耦合
  66. 66. 紧耦合
  67. 67. (三)利用“用户”驱动
  68. 68. • 利用接口A的访问,生成页面B的缓存 • 能共用一份缓存的时候,绝不使用两份
  69. 69. 一般的做法
  70. 70. 更好的做法
  71. 71. 终于,到总结了。。。
  72. 72. 1. 每一分内存都是宝贵的资源
  73. 73. 2. 缓存读写不一定比磁盘快
  74. 74. 3. 会过期的缓存才是好缓存
  75. 75. 4. 生产环境禁用 flush
  76. 76. 5. 严禁长key
  77. 77. 6. 做好缓存校验
  78. 78. 最后你始终还有一个疑问。。。
  79. 79. 如何在适当的场合使用合适的 缓存技术?
  80. 80. 1. 靠经验
  81. 81. 2. KISS原则
  82. 82. 3. 数据重要程度以及访问频度
  83. 83. FAQ
  84. 84. 谢谢!

×