首页 > 其他分享 >豆瓣网技术架构的发展历程

豆瓣网技术架构的发展历程

时间:2023-07-24 20:05:28浏览次数:28  
标签:架构 数据库 SATA 豆瓣 内存 服务器 历程 store subject



豆瓣网简介 

• 2005年3月上线
• 以分享和发现为核心的社区
• 读书、电影、音乐、小组、同城、九点
• 我的豆瓣、友邻

一些数据

• 2.8M注册用户,约1/4活跃用户
• 千万级非注册用户
• 20M动态请求/天,峰值500~600/sec
• 23台普通PC服务器(1U*15/2U*8)
• 12台提供线上服务
• 38G memcached

单服务器

• 单台1U服务器 (frodo)
• 单核AMD Athlon 64 1.8GHz
• 1G内存,160G SATA*2
• Gentoo Linux
• MySQL 5
• Quixote (a Python web framework)
• Lighttpd + SCGI (shire)
• Memcached (!) 

Gentoo Linux

• 容易维护
• emerge mysql
• ebuild 便于管理 patch
• 只安装需要的东西
• 安全性
• GLSA(Gentoo Linux Security Advisories)

MySQL

• The world’s most popular open source database
• 写少读多/写多读少 ==> MyISAM
• 读写并发高 ==> InnoDB
• Replicate for backup

Python


• 开发迅速

• Battery Included

• 第三方库成熟

• 社区成长中

• CPUG: http://python.cn/


Quixote


• 简单,轻量,易于实现REST风格的URL

• 当时还没有Django, TurboGears, Pylons这些选择,只有一

个笨重的ZOPE

• http://www.douban.com/subject/1000001


1. # luz/subject/__init__.py  
2. def _q_lookup(request, name):  
3. subject = get_subject(name)  
4. return lambda req: subject_ui(req, subject)  
5. # luz/subject/subject_ui.ptl  
6. def subject_ui [html] (request, subject):  
7. site_header(request)  
8. “<h1>%s</h1>” % subject.title  
9. site_footer(request)

Lighttpd


• 很好的动态和静态性能

• 原生SCGI支持

• SCGI: 一个简化版本的FastCGI,由Quixote开发者开发

• 所有的请求都通过80端口的lighttpd进程分发,动态内容走SCGI到localhost上的Quixote进程。


Memcache

• 从上线起就在使用,有效减轻MySQL负担

• 对libmemcache做了python封装(使用Pyrex),性能是纯python版的3x+


1. def get_subject(subject_id):  
2. subject = mc.get(‘s:’+subject_id)  
3. if subject is None:  
4. store.farm.execute(“select xxx, xxx from subject where id=%s”,  
5. subject_id)  
6. subject = Subject(*store.farm.fetchone())  
7. mc.set(‘s:’+subject_id, subject)  
8. return subject

问题出现

• 1.2M动态请求/天
• 磁盘IO成为瓶颈
• 需要寻找新机房

解决方案
• 购买两台1U服务器
• pippin 和 meriadoc (后改名merry)
• 双核, 4G内存,250G SATA*3
• 一台作为应用服务器,一台作为数据库服务器
• 迁移到双线双IP机房,使用DNS解析不同网段
IP -_-b
• 开始多人协作开发,frodo做为开发用机
(subversion, trac, etc...)

几点发现
• 数据库的内存分配对性能影响重大
• innodb_buffer_pool_size
• 磁盘随机寻道速度比吞吐量更重要
• 网上找来的IP段分布很不靠谱

问题出现

• 1.5M动态请求/天,尚未到性能瓶颈
• 机房不靠谱,频繁故障
• IP段分布数据不靠谱,用户反映访问缓慢

解决方案
• 换到靠谱的机房,多线单IP(BGP)
• 购买了一台新服务器 (arwen)
• 74G 1w转 SATA * 3
• 做为数据库服务器
• 开始使用专门的服务器作后台计算

 

问题出现

• 2M动态请求/天
• 静态文件服务磁盘IO成为瓶颈
• 上百万的小图片(用户头像、封面图
片, etc...)
• 数据库服务器接近瓶颈

解决方案
• 购买三台服务器,双核,4G,250G SATA*3
• 将图片从一个大目录切分成10000个文件一个目录
• mod_rewrite保持URL不变
• 独立的图片lighttpd进程,启用mod_memcache模块,缓存小图片
• 减小磁盘IO对页面访问的影响
• 将应用服务从web服务器独立出去
• 把更多的内存分配给静态文件服务
• 增加一个只读数据库 

只读数据库
• store增加farmr属性,为一个可用的只读数据库游标
• 头疼的replicate delay问题
• 辅库复制需要时间
• 更新主库后,下一个请求往往就是要读数据(更新数据后刷新页面)
• 从辅库读会导致cache里存放的是旧数据
• 灵异事件!
• 解决方法:更新数据库后,在预期可能会马上用到的情况下,主动刷新缓存
• ......不完美,but it works

避免replicate delay引起的灵异事件


1. def get_subject(sid):  
2. sbj = mc.get(‘s:’+sid)  
3. if sbj is None:  
4. sbj = flush_subject(sid, store.farmr)  
5. return sbj  
6. def flush_subject(sid, cursor=None):  
7. cursorcursor
8. cursor.execute(“select ... from subject”)  
9. subject = Subject(*cursor.fetchone())  
10. mc.set(‘s:’+sid, subject)  
11. return subject  
12. def update_subject(subject, props):  
13. store.farm.execute(“update subject ...”)  
14. store.farm.connection.commit()  
15. flush_subject(subject.id, store.farm)

问题出现

• 2.5M动态请求/天
• 数据库磁盘空间不够了
• 我上/九点数据量庞大
• SATA盘故障率高
• 数据库压力增大

解决方案
• Scale Up,购买四台1U服务器
• 16G内存,147G SCSI *2 + 500G SATA
• SCSI 做 RAID-0
• 用MySQL Slave来保证冗余
• 增加memcached节点数目
• 所有的MyISAM表都改为InnoDB表
• 提高内存利用效率
• 将全文搜索移至Sphinx 

问题出现

• 5.2M动态请求/天
• 图片流量费用成为最大成本
• Web服务器的磁盘IO还是会影响动态页面性能
• 应用服务器进程数不够了
• 机柜空间不够了

解决方案
• 天津的机房便宜一些 :)
• 承担图片流量
• 后台数据挖掘计算
• 容灾备份
• 购买3台1U服务器:4核,32G内存,1T SATA * 3
• 优化前端,启用 otho.douban.com 和 lotho.douban.com 域名
• lighttpd 1.5 with aio support
• 部署LVS
• Scale Up: 应用服务器内存升级 4G -> 8G 

 

 

问题出现

• 6.4M动态请求/天 (5M PV)
• 应用服务器成为瓶颈
• 内存:占用总是增长,似乎有内存泄露
• CPU:memcache对象序列化/反序列化

解决方案
• 第二台应用服务器上线
• lighttpd的mod_scgi只能round-robin
• lighttpd 1.5不稳定
• mod_proxy
• proxy.balance = fair (load based, passive balancing)
• 当进程占用内存超过阈值,当前请求完成后自杀
• 使用spread聚合日志 

 

问题出现

• 11M动态请求/天(3台应用服务器)
• 跨机房写入成为后台计算的瓶颈
• Sphinx的定制性不够
• 相册产品开发中,需要解决图片存储问题
• 灵异现象: 网站变慢,积攒了大量连接得不到处理,load却不高

解决方案
• 数据库分库
• 九点相关表独立出来
• 数据挖掘相关表独立出来,主库放在天津,北京只读
• Sphinx -> Xapian
• 使用MogileFS
• libmemcache -> libmemcached,使用consistent hash降低memcache调整代价
• 修正libmemcached的consistent hash相关bug
• 应用服务器升级至四核CPU
• 修正libmemcached的failover相关bug
• 用nginx替代lighttpd做load balance
• 最后发现罪魁祸首是spread,囧
• 改成用nginx记录日志  

多数据库连接
• 表名全局唯一,维护一个表名至数据库的映射
• store.farm[r] -> store.get_cursor(table=‘xxx’,ro=True/False)

  1. def flush_subject(sid, ro=False):  
  2. cursor = store.get_cursor(table=‘subject’, roro=ro)  
  3. cursor.execute(“select ... from subject”)  
  4. subject = Subject(*cursor.fetchone())  
  5. mc.set(‘s:’+sid, subject)  
  6. return subject  

• 在数据库间挪表变得容易,在主辅库间平衡负载也变得容易 

 

 

问题出现

• 13M动态请求/天
• 计划将所有静态图片都导入MogileFS
• 文件小,数量大,Tracker DB可能成为瓶颈
• 相册产品很受欢迎,存储空间开始紧张

解决方案
• 购买8台新服务器
• 32G内存,四核CPU
• (300G SCSI×2 + 1T SATA) × 3
• (1T SATA × 3) × 5
• 6台北京,2台天津
• 开发DoubanFS

DoubanFS
• 没有中心数据库,直接按照文件名hash查找所在节点,可伸缩性更好
• 按照hash值存成目录树,每个节点负责一组hash值区域
• 定时同步,基于文件修改时间的Merkle Tree
• 利用consistent hash减少增删节点带来的数据移动量
• WebDAV作为读写接口
• 读性能为MogileFS的3倍,写性能为50倍

Consistent Hash

Merkle Tree

 

 

问题出现

• 16M动态请求/天

• 数据库大文本字段严重影响了数据库性能

• DoubanFS中小图片导致IO增高

• 数据库可用性要求提高


解决方案

• 开发DoubanDB

• 更好的伸缩性

• 大文本字段移出后,MySQL的性能得到增强

• MySQL双Master方案

• failover更简单

• 解决replicate delay问题


DoubanDB

• 分布式Key-Value数据库

• 从Amazon Dynamo获得灵感,做了一些简化

• 三个接口:set(key, value), get(key), delete(key)

• memcache协议作为读写接口

• 真正的Merkle Tree,基于内存,后改为基于磁盘文件

• Consistent Hash

• 用TokyoCabinet做为底层存储

• 数据库中的大文本字段进入DoubanDB

• DoubanFS 2.0 基于 DoubanDB 实现  


最新的一些改动

• 将DoubanFS和数据挖掘程序移回北京机房

• 简化文件上传流程和代码

• 更好的利用硬件资源

• 使用ngnix作为最前端

• www.douban.com也部署LVS

• 使用RabbitMQ代替spread


一些经验

• 把钱花在内存上是值得的

• 建立良好的profile工具,并利用之

• memcache并不廉价,仔细控制cache的对象大小和访问方式

• 避免数据库的join操作

• 在产品上做出限制以避免过慢的查询

• 仔细拆分前后台运算

标签:架构,数据库,SATA,豆瓣,内存,服务器,历程,store,subject
From: https://blog.51cto.com/u_16203469/6838260

相关文章

  • k8s架构
    核心组件etcd保存整个集群的状态apiserver提供资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制controllermanager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上kubelet负责......
  • 直播架构图
    直播架构图解析及代码示例随着直播行业的快速发展,直播架构成为了直播系统的核心组成部分。本文将对直播架构图进行解析,并提供代码示例来帮助读者更好地理解直播架构的实现。直播架构图概述直播架构图是对直播系统中各组件和其相互关系的可视化表示。它展示了直播系统的核心功能......
  • sqlserver:拒绝了对对象 'QualityChxxx' (数据库 'xxx',架构 'dbo')的 SELECT 权限
    选择数据库(xxxx)—>安全性—->架构—->dbo(属性)—>权限—>添加—>浏览–>QualityChxxx  ......
  • android usb 驱动架构
    AndroidUSB驱动架构简介AndroidUSB驱动架构是在Android系统中实现USB设备与应用程序之间通信的一种连接方式。通过USB接口,Android设备可以与外部设备进行数据交互,比如连接手机和电脑进行文件传输。USB驱动层次结构AndroidUSB驱动架构分为以下几个层次:USBStack......
  • 火山引擎DataLeap如何解决SLA治理难题(三): 平台架构与未来展望
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群平台架构总结火山引擎DataLeapSLA平台整体主要分为基础组件、规划式治理服务、响应式治理服务三大块,系统组件架构图如下:规划式治理服务所谓“规划式治理”,即在问题发现前治理,通过主动规划约定SL......
  • 火山引擎DataLeap如何解决SLA治理难题(三): 平台架构与未来展望
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群平台架构总结火山引擎DataLeapSLA平台整体主要分为基础组件、规划式治理服务、响应式治理服务三大块,系统组件架构图如下: 规划式治理服务所谓“规划式治理”,即在问题发现前治理,......
  • c#三层架构实现登录
    C#三层架构实现登录1.流程概述在使用C#三层架构实现登录功能时,我们需要分为三个层次:数据访问层(DataAccessLayer,DAL)、业务逻辑层(BusinessLogicLayer,BLL)和表示层(PresentationLayer,UI)。下面是实现登录功能的整体流程:步骤描述1用户输入用户名和密码2UI层调用BLL......
  • 高级系统架构师学习(一)系统工程与信息系统基础
    一、前言已经有一段时间没有写博客了,最近事情比较多,工作和家里的事情也比较忙,慢慢的就没写了,不写博客以后人就懒了,故今天下定决心,重新开始强迫自己提升,挤时间学习一些新的东西!这次的专题就不写应用技术了,写一写我学习高级系统架构师的一些心得吧。我是去年报名学习高级......
  • 对比 5 个开源网关项目,这家 SaaS 企业如何统一网关架构
    作者:古建国关于美洽美洽作为全球智能云客服服务商,10年来深耕智能客服领域,旗下拥有在线客服、呼叫中心、客服机器人、工单系统、语音机器人等智能客服系列产品矩阵,覆盖不同行业客户服务场景,致力于帮助企业获客、销售和服务场景的效率提升。目前,美洽全链路产品已经服务超过40万......
  • 物联网PaaS架构
    物联网PaaS架构实现流程1.确定需求和功能在实现物联网PaaS架构之前,首先需要明确具体的需求和功能。这包括确定要连接的物联网设备类型、数据采集和处理需求、云端服务功能等。2.设计系统架构在确定需求和功能后,需要设计物联网PaaS的系统架构。这包括确定云端服务的组件、设备......