互联网创业的准备——数据库:硬盘iops、mysql
《互联网创业的准备》系列文章——http://cnblogs.com/sink_cup/
硬盘常识:
机械硬盘HDD | 固态硬盘SSD | |
最小单位 | 1个扇区为512B,或4K(2012年民用普及) | 1个分页为4K、8K或更高(与密度有关) |
性能因素 | 转速(rpm):5400、7200、1w、1.5w | 层数:SLC(单层快)、MLC(双层慢)、TLC(三层更慢,SSD暂未采用,U盘大量采用) |
接口 | SATA 3G、SATA 6G(2012年民用普及)、SAS 6G | SATA 3G、SATA 6G、SAS 6G、PCI-E |
尺寸 | 2.5英寸、3.5英寸 | 2.5英寸、PCI-E版型 |
常见品牌 | 西部数据、希捷 | Intel、镁光(Crucial)、浦科特(PLEXTOR) |
硬盘性能指标:
连续读写(常用单位为MB/s):文件在硬盘上存储位置是连续的,适用场景:大文件拷贝(比如视频音乐)。速度即使很高,对数据库性能也没有参考价值。
4K随机读写(常用单位为iops):在硬盘上随机位置读写数据,每次4KB,适用场景:操作系统运行、软件运行、数据库。(图片静态服务器、视频静态服务器是大文件,测试64K随机或更大)
常用硬盘性能测试软件:
Windows:AS SSD Benchmark、CrystalDiskMark、HD Tune Pro、iometer
Linux:iometer
Align I/Os:硬盘IO大小。测试设备时根据硬盘最小单位进行选择,机械硬盘上选512B或4K,SSD上选4K、8K等。测试分区时受分区sector size影响。由于Linux ext3的sector size为4096,所以在扇区为512B的机械硬盘上也无法选择Align I/Os on 512B进行测试,测试效果不佳。vps无法进行设备测试,如果是自购服务器,应使用设备测试。
Seq 即 Sequential 即连续读写。AS SSD会先以16MB的尺寸为单位,持续向受测分区写入生成1个达到1GB大小的文件,然后再以同样的单位尺寸读取这个,最后计算平均成绩而给出结果。
4K 即 Random 4k, Queue Depth=1 即 随机4K并发1个队列。AS SSD会以512KB的单位尺寸生成1GB大小的测试文件,然后在其地址范围(LBA)内进行随机4KB单位尺寸进行写入及读取测试,直到跑遍这个范围为止,最后同样计算平均成绩给出结果。
4K QD32 即 Random 4k, Queue Depth=32 即 随机4K并发32个队列。
4K-64Thrd 即 4K, 64 Thread 即 随机4K并发64个线程,和 4K QD64是一个意思。AS SSD会生成64个16MB大小的测试文件(共计1GB),然后同时以4KB的单位尺寸,同时在这64个文件中进行写入和读取测试,最后依然以平均成绩为结果。
通过AS SSD可以看出,iops与MB/s可以直接换算,比如4K读取是6227iops,即每秒钟可以读取6227个4K的文件,即 6227 * 4K / 1024 = 24.3 MB/s。
Intel的SSD性能数据采用iometer 4K QD32的测试结果:http://www.intel.cn/content/www/cn/zh/solid-state-drives/solid-state-drives-520-series.html#footnotes
价格与速度:
型号 | 容量 | 2012价格 | 4K QD32随机读/写(iops) | 4K QD64 | 连续读写(MB/s) | |
民用 机械7200rpm 3.5英寸 SATA 6G | 希捷Barracuda 7200.14 | 3TB | ¥1.1k | 409/365 | 386/291 | 200/180 |
企业级 机械10000rpm 2.5英寸 SAS 6G | 希捷Savvio 10K.5 | 300GB | ¥1k | 750/700 | 170/170 | |
企业级 机械15000rpm 2.5英寸 SAS 6G | 希捷Savvio 15K.3 | 300GB | ¥2.2k | |||
企业级 固态SLC 2.5英寸 SATA 3G | Intel X25-E | 32G | ¥2.5k | 3.5w/3.3k | 250/170 | |
企业级 固态MLC PCI-E | Intel 910 | 400G | ¥14w | 9w/3.8w | 1000/750 | |
企业级 固态MLC PCI-E | Intel 710 | 100G | ¥2.5k | 3.8w/2.3k | 270/170 | |
民用 固态MLC 2.5英寸 SATA 6G | Intel 520 | 120G | ¥840 | 2.5w/8w | 550/500 | |
民用 固态MLC 2.5英寸 SATA 6G | 镁光 M4 | 128G | ¥800 | 7.8w/4.2w | 7w/4w | 500/175 |
为什么民用SSD的iops很高价格却很低,而企业级SSD的iops有的很低而价格却很高?因为企业级SSD的耐用性高,比如Intel 710 100G寿命为4K写入500TB,即5000次全盘写入。
Intel SSD寿命指标:smart中的“E8:Avai lable Reserved Space”:可用的预留闪存数量、“E9:Media Wearout Indicator”:闪存磨耗指数。其他厂商的SSD类似,比如镁光的wear leaving count。
SSD新盘的剩余磨损为100,当低于10时,应更换,报废。
todo:数据库的选择
关系型数据库用Mysql还是PostgreSQL,或者全用NoSQL?mongo还是hbase?
Mysql 和 PostgreSQL都可以。mysql用的人多,但是oracle收购sun以后,把mysql限制的更加封闭,正在衰落,和可能和OpenOffice一样被oracle整死,衍生出LibreOffice。但不用很担心,即使mysql被oracle整死,也会衍生出开源版本,使用方式一样。
关系型数据库和NoSQL搭配使用较好,关系型适合底层业务,NoSQL适合上层业务。关注淘宝hbase的使用。
Mysql性能与硬盘iops的关系:
mysql可以把读取结果放在内存中,即query cache,所以db server安装大内存即可实现只读内存、不读硬盘。
当预计数据量会增长到超过内存大小时,进行分表(把一个表中的数据拆分),放到多个大内存服务器上,保证每个服务器上的数据都小于内存大小,即可实现全部缓存。
2012年内存价格:UDIMM no ECC DDR3 1600民用内存 ¥270/8GB,RDIMM ECC DDR3 1600服务器内存 ¥440/8G。但一台服务器能安装的内存有限,2012年典型的Dell服务器有24个插槽,主板芯片支持768G内存,2012年DDR3内存生产工艺最高是单条8G(DDR4已实现单条16G,但主板尚未支持DDR4),所以一台服务器最大内存192G(¥440 * 24条 = 1w)。
数据库写入时必须写硬盘(否则就不叫持久化存储了……),¥2.5k的Intel 710企业级SSD的写入iops为2.3k,而万转硬盘的iops为700,如果要达到SSD的性能,需要多块万转硬盘组RAID。如果使用iops为3w的SSD,性能比机械硬盘提升了30倍。
如果数据量不大,远低于192G,就不需要做分表了吗?
仍然要分表,原因有2个:
1、大内存会提升mysql读取性能,但并发读取能力也是有上限的,这时受CPU性能限制,2012年典型的服务器是双路,即2个8核CPU(mysql内存并发待测试)。
2、内存很大,对并发写入能力没有作用,写入能力完全依赖于硬盘的iops。一台服务器的写入性能很有限,请看下面的测试。
为什么不进行分库(把不同的整个表放到不同的库中,也就是不同的机器)?分库简单,但是一个表只增不减难于维护(比如评论表),大小总会超过内存,又要改成分表;或者随着用户量增大,这个表的写入量会逐渐增大,单台机器必然无法承受,参考淘宝等网站的架构演进历史就会发现这个问题。
为什么不用主从设计?因为每个“从”上面都有所有数据,数据量太大,同步压力大,要做设计从每个“从”上面读取不同的数据,以免内存query cache不够用,这增加了复杂度;即使“主”只进行写,写入性能还有限,无法承受大并发。
所以最好在项目初期就使用分表设计,而不是分库或主从设计,把分表设计做成一个成熟的方案(各个大型互联网公司都已经实现),各个项目通用,免得后期数据量大了拆分表,需要导数据,需要暂停线上服务。
mysql测试环境:
服务器:vr.org VR512、阿里云512、盛大云512
mysql版本:5.5.27
连接方式:unix domain socket
表:phpbb 3.0.7的users表,含有76个字段,28个char,43个int,1个主键,1个自增,1个unique,3个index。
CREATE TABLE `phpbb_users` ( `user_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `user_type` tinyint(2) NOT NULL DEFAULT '0', `group_id` mediumint(8) unsigned NOT NULL DEFAULT '3', `user_permissions` mediumtext COLLATE utf8_bin NOT NULL, `user_perm_from` mediumint(8) unsigned NOT NULL DEFAULT '0', `user_ip` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '', `user_regdate` int(11) unsigned NOT NULL DEFAULT '0', `username` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `username_clean` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_password` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '', `user_passchg` int(11) unsigned NOT NULL DEFAULT '0', `user_pass_convert` tinyint(1) unsigned NOT NULL DEFAULT '0', `user_email` varchar(100) COLLATE utf8_bin NOT NULL DEFAULT '', `user_email_hash` bigint(20) NOT NULL DEFAULT '0', `user_birthday` varchar(10) COLLATE utf8_bin NOT NULL DEFAULT '', `user_lastvisit` int(11) unsigned NOT NULL DEFAULT '0', `user_lastmark` int(11) unsigned NOT NULL DEFAULT '0', `user_lastpost_time` int(11) unsigned NOT NULL DEFAULT '0', `user_lastpage` varchar(200) COLLATE utf8_bin NOT NULL DEFAULT '', `user_last_confirm_key` varchar(10) COLLATE utf8_bin NOT NULL DEFAULT '', `user_last_search` int(11) unsigned NOT NULL DEFAULT '0', `user_warnings` tinyint(4) NOT NULL DEFAULT '0', `user_last_warning` int(11) unsigned NOT NULL DEFAULT '0', `user_login_attempts` tinyint(4) NOT NULL DEFAULT '0', `user_inactive_reason` tinyint(2) NOT NULL DEFAULT '0', `user_inactive_time` int(11) unsigned NOT NULL DEFAULT '0', `user_posts` mediumint(8) unsigned NOT NULL DEFAULT '0', `user_lang` varchar(30) COLLATE utf8_bin NOT NULL DEFAULT '', `user_timezone` decimal(5,2) NOT NULL DEFAULT '0.00', `user_dst` tinyint(1) unsigned NOT NULL DEFAULT '0', `user_dateformat` varchar(30) COLLATE utf8_bin NOT NULL DEFAULT 'd M Y H:i', `user_style` mediumint(8) unsigned NOT NULL DEFAULT '0', `user_rank` mediumint(8) unsigned NOT NULL DEFAULT '0', `user_colour` varchar(6) COLLATE utf8_bin NOT NULL DEFAULT '', `user_new_privmsg` int(4) NOT NULL DEFAULT '0', `user_unread_privmsg` int(4) NOT NULL DEFAULT '0', `user_last_privmsg` int(11) unsigned NOT NULL DEFAULT '0', `user_message_rules` tinyint(1) unsigned NOT NULL DEFAULT '0', `user_full_folder` int(11) NOT NULL DEFAULT '-3', `user_emailtime` int(11) unsigned NOT NULL DEFAULT '0', `user_topic_show_days` smallint(4) unsigned NOT NULL DEFAULT '0', `user_topic_sortby_type` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 't', `user_topic_sortby_dir` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 'd', `user_post_show_days` smallint(4) unsigned NOT NULL DEFAULT '0', `user_post_sortby_type` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 't', `user_post_sortby_dir` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 'a', `user_notify` tinyint(1) unsigned NOT NULL DEFAULT '0', `user_notify_pm` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_notify_type` tinyint(4) NOT NULL DEFAULT '0', `user_allow_pm` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_allow_viewonline` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_allow_viewemail` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_allow_massemail` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_options` int(11) unsigned NOT NULL DEFAULT '230271', `user_avatar` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_avatar_type` tinyint(2) NOT NULL DEFAULT '0', `user_avatar_width` smallint(4) unsigned NOT NULL DEFAULT '0', `user_avatar_height` smallint(4) unsigned NOT NULL DEFAULT '0', `user_sig` mediumtext COLLATE utf8_bin NOT NULL, `user_sig_bbcode_uid` varchar(8) COLLATE utf8_bin NOT NULL DEFAULT '', `user_sig_bbcode_bitfield` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_from` varchar(100) COLLATE utf8_bin NOT NULL DEFAULT '', `user_icq` varchar(15) COLLATE utf8_bin NOT NULL DEFAULT '', `user_aim` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_yim` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_msnm` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_jabber` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', `user_website` varchar(200) COLLATE utf8_bin NOT NULL DEFAULT '', `user_occ` text COLLATE utf8_bin NOT NULL, `user_interests` text COLLATE utf8_bin NOT NULL, `user_actkey` varchar(32) COLLATE utf8_bin NOT NULL DEFAULT '', `user_newpasswd` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '', `user_form_salt` varchar(32) COLLATE utf8_bin NOT NULL DEFAULT '', `user_new` tinyint(1) unsigned NOT NULL DEFAULT '1', `user_reminded` tinyint(4) NOT NULL DEFAULT '0', `user_reminded_time` int(11) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`user_id`), UNIQUE KEY `username_clean` (`username_clean`), KEY `user_birthday` (`user_birthday`), KEY `user_email_hash` (`user_email_hash`), KEY `user_type` (`user_type`) ) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
mysql配置:
并发默认151,超过时会出现“1040 Too many connections”,修改为99999:set global max_connections=99999;
超过1024时,碰到了Linux “open files”限制,出现错误“2001 Can't create UNIX socket (24)”。修改为99999:ulimit -n 99999
测试程序:
write 和 update 直接写入硬盘,这时要注意iowait。
key 和 read 每次先写入99条数据(这时影响观测iowait),然后并发查询,然后删除数据,重新生成。比如iterations=10,就会生成10次。如果并发1000,那很快就会把所有数据query cache起来,速度变快。这种情况和生产环境类似,保证内存大于数据库,数据只读一次硬盘,以后一直在内存中,所以不观测iowait。
./mysqlslap --auto-generate-sql --number-char-cols=28 --number-int-cols=43 --auto-generate-sql-load-type=write --auto-generate-sql-add-autoincrement --socket=/tmp/mysql.sock --concurrency=800 --number-of-queries=800 --user=root --password=1 --iterations=10
如果不考虑读一次硬盘,内存中cache了所有数据,是不是read并发就会很大?经测试,稍微大了一点,这时iowait显然为0,但CPU 100%,如果CPU更强,并发预计会进一步提高。
先写入1w条,只查某一条,确保query cache,mysqlslap key时并发为1300,全内存时为1700。
./mysqlslap --query='select * from t1 limit 1;' --socket=/tmp/mysql.sock --concurrency=800 --number-of-queries=800 --user=root --password=1 --iterations=10
mysql性能判断规则:
Average number of seconds to run all queries < 1秒,比如800个并发,执行800个sql,如果在1秒内不能完成,就会影响下一秒的800并发。
iops测试环境:
分区测试 还是 设备测试:分区测试。
4K QD32随机读/写(iops) | 硬盘到内存mysql key(按主键读) | 硬盘到内存mysql read |
硬盘mysql write |
硬盘mysql update | 硬盘mysql mixed | |
vr.org VR512 | 100-300/700-2900波动很大 | 并发1300,0.98s | 并发180,0.91s | 并发800,0.98s,iowait < 21 | 并发800,0.92s,iowait < 15 | 并发900,0.97s,iowait < 16 |
阿里云主机 | ||||||
阿里云数据库 | ||||||
盛大云主机 512 Windows | 300-420/350-400波动不大 |
|
||||
盛大云主机 512 Linux | 70-90/550波动不大 | 并发1300,0.97s |
并发300,0.99s | 并发1300,0.97s,iowait < 21 | 并发1100,0.96s,iowait < 18 | 并发1200,0.91s,iowait < 25 |
盛大云数据库 |
mysql测试图:
传统web服务的mysql压力:
读取量大,写入量小,所以加大内存即可。
案例:
1、小米论坛(bbs.xiaomi.cn),在2012年9月10日发帖和回帖合计89w,PV约2000w。
每次发帖回帖需要发表(同步)、增加积分(异步)等操作,按每次发帖需要3次同步写数据库算,89w * 3次 / (15h * 3600s)* 高峰时2倍 = 99次写/s。
页面数据在业务层、页面层有缓存,实际读取数据库按每次浏览需要1次读数据库算,2000w / (15h * 3600s)* 高峰时2倍 = 740次读/s。
按一台微型vps db并发900计算,两台vps db即可承受,一台自购服务器Raid即可承受。
2、远景论坛(bbs.pcbeta.com),在2012年8月Windows 8发布后,每天发帖回帖1.9w。
数据库写入并发:1.9w * 3次 / (15h * 3600s)* 高峰时2倍 = 2次写/s。
3、instagram使用Amazon云服务,达到820w UV,那也是普通web服务,并发很低,假设是5000w PV,并发量才 5000w / (15小时 * 3600) = 900。
“秒杀”服务的mysql压力:
像小米手机抢购、淘宝光棍节促销,这种“秒杀”服务流量集中在瞬间,而不是全天,并发写入量大、并发读取量更大。
案例:
2012年8月23日10点,小米1S开放购买20w台,事先有个预定量统计,约160w人,到了秒杀时间并发量读取为每秒十万级(登录、刷新),登录页面正常显示,但提交后返回500错误,数据库读取压力达到极限,登录很困难……
假设有50w人10点准时抢购,有30w人卡在登录步骤,一直刷新提交,按读取并发10w计算,微型vps的每台mysql为1300,需要76台微型vps……显然不合理,成本太高。购买云数据库也无法保证10w并发(云数据库待测试)。
按机械硬盘Raid的自购服务器mysql key为3000计算(参考值,待有条件时测试),需要33台服务器……
假如使用Intel 710 SSD,读取iops为3.8w,预计mysql key为5w,只需要2块SSD即可。
29分36秒抢购结束,数据库写入为 20w / 1776秒 = 112次/s,由于卡在了登录,所以写入量不大。
2012年9月6日10点,小米1S开放购买5w台电信版+15w台标准版,预定量未公布。参考上次160w的预定量,假设这次一样人数。
由于数据库读写能力有限,采用了分批选取一部分用户可以看见“验证预约信息”的链接,别的用户都看见“您没有购买小米手机1s的特权”,这一批处理完了,再选取下一批。
这句话语义错误,因为这些用户都是已经预约的,导致用户投诉。
9分40秒抢购结束。
20w订单,数据库写入为 20w / 580秒 = 344次/s
第一次抢购卡在登录,第二次抢购分批影响用户体验。
如果不分批,进行正常抢购,160w中有50w人在10点等待着,ajax检查登录返回抢购链接(如果使用memcache session或者mongo session,要考虑nosql并发。如果使用无需存储的加密仿session,需要考虑cpu能承受多少并发计算加密对比),
花费5秒输入预约信息(复制粘贴4秒,如果手打8秒),
然后提交数据库进行验证,数据库要承受10w-20w/s的读取压力,
都是预约过的,50w都人验证通过,点击购买下订单,数据库要承受10w-20w/s的写入压力。
所以,如果创业公司业务要做秒杀,需要自购服务器(用SSD Raid,加大内存),而不是采用云服务。
如果购买品牌服务器,可以自行购买SSD和内存,因为服务器厂商提供的配件要贵2倍以上。
比如dell 8核 1U、2U服务器价格约¥1.4w,但选配的硬盘很贵,参考文章末尾的截图。
所以无论是PC还是server,DIY一直是高性能低价格的终极解决方案。
如果ODM自行设计服务器,参考Facebook开放服务器计划:http://news.cnblogs.com/n/133536/
Dell服务器硬件价格图:
硬盘iops测试图:
参考资料:
http://tech.sina.com.cn/h/2012-08-02/09337458765_2.shtml
http://article.pchome.net/content-1515362-7.html
http://bbs.xiaomi.cn/forum.php?mod=viewthread&tid=4615631
http://news.cnblogs.com/n/133536/
http://www.liusuping.com/it-tech/server-rdimm-udimm.html
http://ssd.zol.com.cn/252/2529054.html
http://ssd.zol.com.cn/303/3031977.html
http://www.ssdfreaks.com/content/599/how-to-convert-mbps-to-iops-or-calculate-iops-from-mbs
http://www.xfastest.com/thread-65671-1-1.html
http://bbs.pceva.com.cn/thread-36832-1-1.html
http://tech.watchstor.com/storage-module-136610.htm
http://isjin.blog.51cto.com/612537/528622
http://www.datacentersky.com/undeer-linux-use-iometer.html
http://www.datacentersky.com/taught-you-how-to-use-iometer-test-tool-to-test-storage.html