存档

‘Architecture’ 分类的存档

eBay 使用mysql 进行Personalization

2009年3月25日 没有评论

其实这篇文章Fenng 早写了(连接), 08年四月Mysql 大会上的, 讲的是ebay 使用mysql 的memory 引擎做其用户个性化的解决方案.

ebay 的有趣数据:
1.1亿商品出售
每秒2千美元的交易量
2.7亿注册用户
20亿页面点击每天
6千台应用服务器(apache Geronimo)
300 个数据库服务器和700 数据库实例 (oracle居多)
9PB 的OLTP 数据
1千3百万代码(Java 的)
由于ebay 还在快速的向其v4 架构转变,无论是后台的基于eclipse 的快速开发,还是前台基于Flex 的用户体验. 除了9PB 的数据量比以前多很多之外,其他的数据比其巅峰时期还是下降了不少(amazon 在技术上真是低调呀).

大型互联网站点的用户个性化还是比较重要的, 而cookie 和 普通的数据库储存上还是有很多限制的,比如
cookie 只能保存4k的数据,(虽然你可以通过多个站点的多个cookie 来扩展)
每次传输都需要浪费不必要的数据,不管你在cookie 中需要的还是不需要的.
OLTP 数据库的读写和扩展性都有限,尤其面对ebay 的平均每个用户40k 的个性化数据, 每秒处理能力有限.
面对ebay 这样的数据量,每一个字节都是宝贵的,尤其是ebay 从v4 开始,有能力把多个http request 压缩, 每一个字节都能为他们节省宽带和服务器.

使用mysql memory 作为其个性化引擎之后,所面临的需求:
每天要40 亿次读写,(读写基本上个半50%/50%开)
要能够至少支持1万两千个java 进程发起的connection.
使用低端硬件 (ebay 也是有钱的主,全世界最贵的it 硬件基本都能在他们机房找到,最贵的软件也是毫不含糊)
水平和垂直的无线扩展.
低延时,快速响应和低网络消耗.
我猜最后一点是其选用mysql memory 引擎的最优先考虑,不能使用嵌入式数据库和内存数据库主要就是不能无限扩展和容灾.

为什么选定mysql memory 引擎还是做了灰常多的测试的(ebay自己做的patch):
在一个50%/50% 读写环境的测试中,一个低端的SUN 4100 Solaris x86 能够支撑2w的并发连接,每秒1.3wTPS , 基本到极限了,普通的这种硬件即使是做web server 的http request connections, 到8w/s 基本就是极限了,更何况这是java 的jdbc connections .

整体架构图


ebay 也很清楚这个结构的好与坏:
5分钟之内数据库挂掉了,是可以接受的,只有重要的personalization 数据是一定会保存的,大部分not critical data 可以接受丢失.
mysql单复制线程如果挂了,可能会引起数据拥堵,可以通过两次写(第二次是程序框架),应用框架会自动从应用服务器异步执行第二次写.(应该是v4)
但节点失效或负载多大,应用程序会自动负载平衡.
写都是批量的
Special techniques used to minimize table lock duration during write back and eviction operations (这个很重要的信息被带过去了,批量操作还能通过特殊技术减小table lock 不知道怎么实现的)


结果:

每天40亿的用户请求,50%读/50%写,平均每个用户40k/Session 的数据量,以前可能要添加不同的域名来解决cookie 限制的,现在通过25台普通的Sun 服务器就能够解决了,成本得到了很好的控制. 而且最重要的是ebay 的这种方案充分发挥了数据库特性,需求以及成本之间的平衡.

后记
看完这篇文章之后,逛了一下一些大型互联网的站点,发现有很多站点对cookie 基本上是无限制的使用,一个页面可以同时使用8,9个cookie,如果是多个域名之间使用20,30个cookie 还可以理解,一个页面跳8,9个cookie ,的确是有钱的主呀. 有的站点甚至里面的cookie 有非常不解的东西,甚至用户名可以用两三个变量保存,浪费30,40字节的带宽,只能说有钱的主呀!!!

参考资料:
http://www.dbanotes.net/database/ebay_personalization_platform_mysql.html
Fenng 的ebay personalization platform mysql 案列

http://www.ibm.com/developerworks/cn/opensource/os-eclipse-ebay1/
http://www.ibm.com/developerworks/cn/opensource/os-eclipse-ebay2/
ebay 的v4 架构细节

http://www.infoq.com/cn/articles/ebay-scalability-best-practices
ebay 数据库伸缩最佳实践

http://blog.sina.com.cn/s/blog_466c66400100bi2n.html
sina 的杨建关于web server 优化的建议, 那种每个字节都计较,不比别人超过10倍,20倍的性能不罢休的精神值得每个技术人员学习呀.

分类: Architecture 标签:

hyperic mysql scaling 案例学习

2009年3月23日 没有评论

这是看了Sun 的communityone 上一篇介绍hyperic 在mysql 上scaling 的介绍写的笔记.

hyperic 是一个在大型数据中心用作服务器管理和监控的软件,Hyperic HQ 提供中心服务器来收集多台主机的状态和性能指标,并且在一个中心界面上管理…… 其他的好处看后面的参考资料
Hyperic 默认是支持三种数据库mysql,oracle,postgresql.

后面会用到的几个hyperic 内的术语:
Servers:  一台主机上一个特定的应用类型,比如mysql 服务,jboss 服务.
Services: 对应一个服务里面具体的某一个类型,比如mysql 里面的cpu 消耗,内存消耗,每个表的磁盘消耗.
Metrics: 每一个Services 一个时间点收集上来的数据,比如10:40 cpu 的消耗是60%.
Metrics data points(数据点): 一个时间点的某一个services 的具体数值, 比如上面的60%.

一种标准中等规模的度量数据:
远程管理300台主机,2100 Servers,21000 Services , 46万metric, 每分钟收集2万metric (平均值), 每天就是2千8百万数据点 (这个数据比较保守,性能收集间隔可以大一些.所以实际支持主机还可以多很多)

Hyperic Mysql 版本Scaling 技巧:

  1. 批量插入

    INSERT INTO TABLE (a,b,c) values (0, 0,0), (1,1,1),(2,2,2),(3,3,3),…,…

    减少和数据库来回次数,注意设置mysql 参数max_allowed_packet
    其他可选的提高插入速度的方法Set unique_checks=0, insert, set unique_checks=1 .
    Set foreign_key_checks=0, insert, set foreign_key_checks=1

  2. Batch Aggregate Inserter

    实际的数据点的数据一般很少看到,真正给监控人员看到的都是聚合后的数据(用线性图展现出来的)

    HQ agent 将收集到的聚合数据发送给HQ Server , 然后Server 并不马上保存这些数据,而server将几百个agent 的各种service 数据都放在queue 里面,然后批量插入.

    好处就是减少插入语句次数和connection 数. 减少CPU 消耗和服务器负载.调优之后的配置一般能达到700 agent . 3个workers , 每次批量插入BatchSize:2000 ,  QueueSize :4,000,000 , 每分钟可以插入2.2M 数据点. (还算是很保守的)

    这个技巧我觉得最厉害,这跟它表的结构也有关系, 它表储存原始数据都是三个列(time,measurement_id,value), 所以任何监控类型数据都可以存在这个表,所以它把数据放在queue 里面,然后批量的2000个才插入一次, 每次queue 里面总有几百万的数据,但是连接数却总能控制在个位数下,如果几百个agent 每种metric 都要从数据连接池里面取连接,而且还是永久性的连接,最少需要上千甚至上万个connection, 使用这种queue + batch 的方式,只有几个connection , 数据库跟hyperic server 服务器(它用的jboss) 性能都伸缩了不少. 数据中心非要这种方式的收集方法.

  3. 控制数据增长

    hyperic 监控的主机越多,service 越多,每分钟收集的数据量也就越多,控制详细数据和聚合数据的大小对于磁盘消耗显得很重要. 对于每个数据点收集是在hq_metric_data_xh_ys ,  一共有18个这样的表只保存2天的详细数据,每个表只保存一个小时的详细数据,然后循环,每个小时进行一次数据压缩,压缩的表已经不会进行insert 操作了,把数据压缩之后保存在eam_measurement_data_1h 中 (5列 $MEASUREMENT_ID , $TIMESTAMP , $VALUE , $MIN , $MAX ) , 每个小时只有一条数据(简单认为缩小了60倍吧),这种每个小时的数据保存14天,然后再把数据压缩到6个小时保存一条数据放在MEASUREMENT_DATA_6H 表中,保存31天,表结构完全一样,最后保存到MEASUREMENT_DATA_1D 表中,一天只保存一条数据.这个表你想保存多少年都可以了. 按照一台主机监控800台服务器上的16,000个metric 来看,一天也就16,000条数据,多少年都不成问题了.

    由于里面每个表的功能基本都是独立,一层一层的级别关系,所以进行很多操作的时候都没有锁,实时收集数据的表只有insert 操作,删除的表都是直接truncate 掉,压缩的表一个小时才做一次,用户查看的都是一个小时几个点的数据,而你又很容易看到几个月甚至几年的综合数据,比如像是可用性,CPU ,磁盘使用. 每种应用都找到自己的特殊表来用. 性能,负载,容量,历史数据都得到了很好的保存。 这个想法主要来自RRDtool ,一个主要用来时间系列的画图框架,主要用在nagios, cacti 等监控后台的图表展现上.

  4. 分区

    所有细节的表由hq_metric_data_xh_ys 表示,每两个表代表一天,一共9天,

    所有的数据归档压缩之后直接truncate ,而不是delete (这是针对mysql 5.1 之前没有partition 的功能而做,不知道以后会不会针对mysql 5.1 之后出个特别版), 应用程序上会自动计算应该insert 那个表,每个时间段该取那个表的数据. 所以insert 的表不会跟select 表和truncate 的表有冲突,3种表都自己做自己的工作.

    truncate 和 delete 的好处也有提到,这种partition 的设计还是要好好学学.

  5. 索引的选择

    选用InnoDB 是因为其基于主键的cluster index (和oracle 的IOT 一样), 优点就是select 更快,insert 也更快,索引占的磁盘也更少. 由于它全部都是基于时间点的顺序加入的,所以其cluster index, leaf index, data 都是基于同一个顺序在磁盘上访问,所以即使它不需要事务和锁,InnoDB 还是其最佳选择.

  6. SQL 的选择

    由于mysql 的view 不能够把子查询的sql 执行计划和外层的查询计划统一来计算,所以数据过滤操作要放在里面,外面再把表连接起来, 像是select xxx from (select *** from innertable1 union innertable2) as bigtable  where bigtable.xxx <??? 这种sql 执行起来就很慢, 而hyperic 要通过程序计算从那些表取数据,然后在外层连接起来. 像是这样

    SELECT begin AS timestamp, AVG(value) AS value, MAX(value) AS peak, MIN(value) AS low
    FROM
    (SELECT 1207631340000 + (2880000 * i) AS begin FROM EAM_NUMBERS WHERE i < 60) n,
    (SELECT * FROM HQ_METRIC_DATA_2D_1S
    WHERE timestamp between 1207767600000 and 1207804140000 AND
    measurement_id = 600332 UNION ALL
    SELECT * FROM HQ_METRIC_DATA_2D_0S
    WHERE timestamp between 1207724400000 and 1207767599999 AND
    measurement_id = 600332 UNION ALL
    SELECT * FROM HQ_METRIC_DATA_1D_1S
    WHERE timestamp between 1207681200000 and 1207724399999 AND
    measurement_id = 600332 UNION ALL
    SELECT * FROM HQ_METRIC_DATA_1D_0S
    WHERE timestamp between 1207638000000 and 1207681199999 AND
    measurement_id = 600332 UNION ALL
    SELECT * FROM HQ_METRIC_DATA_0D_1S
    WHERE timestamp between 1207631340000 and 1207637999999 AND
    measurement_id = 600332) EAM_MEASUREMENT_DATA
    WHERE timestamp BETWEEN begin AND begin + 2879999 AND measurement_id = 600332
    GROUP BY begin ORDER BY begin;

    不用管里面多复杂,简单来说就是里面要尽量多过滤数据(过滤条件都是程序通过时间计算的),外层得到最少的值才开始group 和order . (它从最差性能的sql -> 中等性能的sql -> 程序配合的最好性能的sql , 我花了几天时间还是只能理解个大概)

  7. ID 生成策略

    ID都是从10001开始生成,为hard-code 的ID 保留10000个空位,不使用auto-increment , 使用myisam 类型的hq_sequence 表来表示sequence , 

  8. 后面还有几个关于hibernate id 生成策略,建议性能参数和统计值,以及服务器建议配置等,不一一介绍了.

参考资料:

  1. http://www.scribd.com/doc/2909846/Scaling-MySQL-A-Case-Study-of-Hyperic-HQ

    scribd 上的文档大部分可以下载,也可以在线观看,很好的文档分享站点.