缘起---闲逛博客园
前几天的时候,在某一QQ群看到一条消息“XXX酒店开房XXXBTXX迅雷BT下载”,当时是一目十行的心态浏览,目光掠过时,
第一反应我想多了~以为是XX种子(你懂的~),但并不感兴趣。
直到又回到博客园逛时,看到一篇最多评论的文章:
看看多线程的效率有多差劲! - 张浩华 - 博客园
http://www.cnblogs.com/zhhh/p/3385751.html
于是点击进去了。这时,我才回想起来,当时是自己邪想多了......
原来是2000w条开房数据记录的数据库,浏览了文章后,我表示对这2000w感兴趣了!于是,就催生了此文。
结缘---搜索引擎来牵线
看了上面提到的那文章,我决定也要玩一玩,不过那园友有点儿“懒”了,没有提供数据库以及Demo下载。好吧,这时候我们要发挥搜索本领了,几经搜索,最后是Google帮上的忙(百度搜索到的链接很多都已经被和谐了~)。 (温馨提示:近来上级严查水表,各位传播东西需小心~)。
一个小时过去了,1.7GB的压缩文件终于下载完成:
C:\[fuliba.net]某酒店2000W数据(解压密码:sjisauisa是就数据8很舒适好sjjss).rar 文件大小:1834815332字节 创建日期:2013年10月24日 15:38:55 哈希值(MD5):091AAC2B45D76CE1CD4248E7FFF1C00E
解压后,约8GB多。
小巫见大巫
面对这千万条数据级别的数据库文件,先来看一看我的本机配置信息:
操作系统名称 Microsoft Windows Ultimate 操作系统核心(Kernel)类型 Multiprocessor Free (-bit) Microsoft SQL Server 2008R2 Microsoft SQL Server Management Studio 10.50.1600.1 Microsoft Analysis Services Client Tools 10.50.1600.1 Microsoft Data Access Components (MDAC) 6.1.7601.17514 Microsoft MSXML 3.0 5.0 6.0 Microsoft Internet Explorer 9.10.9200.16686 Microsoft .NET Framework 2.0.50727.5472 Operating System 硬件概要 CPU: 英特尔 Core i3-380M (双核) 主板: 方正 R410CP (英特尔 HM55 (IbexPeak-M DH)) 内存: GBytes 显卡: ATI (PARK PRO/XT GL) 硬盘: 东芝 MK3265GSX 处理器信息 处理器: Intel(R) Core(TM) i3 CPU M @ .53GHz 运行速度: 2533.3 MHz 核心/线程: 双核, 四线程 核心代号: Arrandale SV 功耗: 25.0 W 插座: Socket G1 (rPGA988A) 一级缓存: 指令: x KB, 数据: x KBytes 二级缓存: 集成: x KB 三级缓存: MB 特性: MMX SSE SSE SSE SSSE SSE4. SSE4. EMT64 VT EIST TM1 TM2 主板信息 主板厂商: 方正 主板型号: 方正 R410CP 芯片组: 英特尔 HM55 (IbexPeak-M DH) 主板插槽: 2xPCI Express x1, 6xPCI Express x2, 1xPCI Express x16 USB支持: v2. PCI BIOS版本: V1. BIOS日期: 内存信息(总计: GBytes) 内存条1: 内存大小: MB 内存类型: DDR3 SDRAM 制造商: 三星 制造日期: 2010年第39周 内存条2: 内存大小: MB 内存类型: DDR3 SDRAM 制造商: 三星 制造日期: 2012年第1周 显卡信息 显卡芯片: ATI (PARK PRO/XT GL) 显存大小: MB of DDR3 SDRAM 显卡型号: ATI (PARK PRO/XT GL) [宏碁] 显卡BIOS版本: 频率: 157.0 MHz 存储信息 存储器1: 控制器: Serial ATA 3Gb/s 型号: 东芝 MK3265GSX 容量: , MB ( GB) 转速: RPM 缓存: KB NCQ功能: 支持, S.M.A.R.T.: 存在, 开启 48bit LBA: 支持, 开启 工作时间: 7088小时
测试机配置信息
应该还算可以吧?有点别扭的是在32位系统上硬是要它使用(破解--映射方式)6GBytes的内存,呵呵,会不会有什么后患呢?
之前学习使用的数据库文件多为MDF格式的,直接附加就能使用,但现在这个解压后是.bak格式的,是备份出来的,所以不能以附加的方式进行附加,
而是用【还原】的方式,如图:
也可以尝试用命令的方式还原bak文件并建立到一个新的数据库,(仅供参考,没实践过)
/* 备份数据DB 到.bak文件。然后利用此bak文件恢复一个新的数据库DBTest。 */ USE master BACKUP DATABASE DB TO DISK = 'g:\DBBack0930.bak' RESTORE FILELISTONLY FROM DISK = 'g:\DBBack0930.bak' RESTORE DATABASE DBTest FROM DISK = 'g:\DBBack0930.bak' WITH MOVE 'DBTest' TO 'E:\Program Files\Microsoft SQL Server2005\Data\DBTest.mdf', MOVE 'DBTest_log' TO 'E:\Program Files\Microsoft SQL Server2005\Data\DBTest_log.ldf' GO
OK!我们就开始还原吧,点击确定之后,我呆住了,机器也似乎呆住了,那还原的状态进度一直(2分钟内)是0,
鼠标光标变成了圆圈忙碌状态,系统瞬间变得很卡,(汗~~在校生,没见过什么大场面,被这还原进度惊呆了,各位不要见笑哦!),
又过了一分钟,终于有反应了,状态的百分比显示20%+,以步进6%左右递增,不会处于“卡死”状态了,
又过了几分钟,终于完成了。以前,课堂练习的附加,还原文件几乎都在秒级完成,几乎没有什么时间等待的概念,
这次可谓见识了什么叫海量(不过,但各位老鸟中,2000w,可能还只是沧海一粟(sù))数据。
还原成功!于是心急着马上查一查心中的她,是否榜上有名了。不加思索,就来了一条SQL语句:
select * from dbo.cdsgus where Name='女神'
噢,又一次惊呆了!在此先省略......字,
慢,别想多了,不是找到了女神的名单,而是电脑再次进入了呆住了的状态,只是查询还在在执行中,一开始,我还以为是内存太小,
CPU太差了,Ctrl+Alt+Del呼唤了任务管理器出来:
上图告诉我,不是CPU,内存的错,也就在这时,一个闪闪的红星引起了我的注意~~
对,你猜对了!就是硬盘的读写状态提示灯,它在飞快的闪烁,平时,它只会偶尔闪闪。于是,我再次从任务管理中调出【资源监视器】来观察情况,
“卡住”的原因找到了,跃然上图,不多说了。以前,很少会关注硬盘IO问题,毕竟经常性地Copy大文件的机会不多。而这次,我才真正关注起硬盘IO问题,在这给自己和各位对IO陌生的朋友补充一下:
读写IO(Read/Write IO)操作
磁盘是用来给我们存取数据用的,因此当说到IO操作的时候,就会存在两种相对应的操作,存数据时候对应的是写IO操作,取数据的时候对应的是是读IO操作。
单个IO操作 当控制磁盘的控制器接到操作系统的读IO操作指令的时候,控制器就会给磁盘发出一个读数据的指令,并同时将要读取的数据块的地址传递给磁盘,
然后磁盘会将读取到的数据传给控制器,并由控制器返回给操作系统,完成一个写IO的操作;
同样的,一个写IO的操作也类似, 控制器接到写的IO操作的指令和要写入的数据,并将其传递给磁盘,磁盘在数据写入完成之后将操作结果传递回控制器,
再由控制器返回给操作系统,完成一个写IO的操作。单个IO操作指的就是完成一个写IO或者是读IO的操作。
对于经常做网络,服务器的来说,I/O不好反映到网站上就是网站页面加载慢、卡、读取数据库慢,
甚至导致网页打开超时显现。
关于IO瓶颈处理的推荐文章:
Understanding Disk I/O - when should you be worried?
http://blog.scoutapp.com/articles/2011/02/10/understanding-disk-i-o-when-should-you-be-worried
贝塔中的DBA » IO系统性能之一:衡量性能的几个指标
http://www.dbabeta.com/2009/io-performence-01_several-concepts.html
等待了200多s,终于有结果了:
呵呵,终究没有出现女神的名字。
面对这2000w,一条select * from XXX就把我汗颜了,我在想着下一个要查谁的时候,内心一个念头把我拉住了。
不能这样折腾硬盘,伤不起.....
给在校生的练耙场---可以实践检验平时学习的理论知识
【背景】
平时课堂练习的项目,数量都是几十条为主,也不想无聊去自己建立上万条非真实数据去测试。
【实战】
查询一条数据就要等待200多s,你有这样的时间浪费吗?也就在这时,平时学习的理论知识要派上用场了-----【索引】
【四两拨千斤---索引来支招】
上面提到提到了,执行语句: select * from dbo.cdsgus where Name='女神' 花了200多秒,
假如结果只有一条,各位猜一猜执行:
select top 1 * from dbo.cdsgus where Name='女神' 要花多长时间呢? --留给各位实践一下。
好吧,为了快速地找到那个她,我们为字段名Name建立索引:
Create Index Index_ByName on dbo.cdsgus(Name) --由于记录多,建立索引的时间很慢,耗时:03:49
上面注释提到建立索引就花了209秒,那它在背后究竟做了什么呢?建立索引后再次查询,为什么会让我再次惊呆呢?
这时,再去查询执行同样的查询,大家猜一下这一次所花的时间,与刚才的耗时对比,又一次让我惊呆了。
平时课堂练习的项目几乎体会不到索引对于检索速度的影响,这次可谓实践,领会了!
天下没有免费的午餐,索引能为我们的检索速度起到了四两拨千斤作用,那么,肯定也是要付出代价的。你知道有何代价么?
在课堂中,关于索引的缺点只有理论的提到几句:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。 第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
很少试实践证明,即使实践了,由于是几十,百条数据的项目,感受不到。
这次可谓实践了。
针对以上缺点,
第一条,209s 耗费时间这个深有体会了。
第二条,需要占用物理空间,请看以下数据对比:
未为字段Birthday建立索引前的文件大小: : ,,, shifenzheng.mdf : ,, shifenzheng_1.LDF 建立索引后的文件大小: : ,,, shifenzheng.mdf : ,, shifenzheng_1.LDF
对于第三条,这里就不实践了,毕竟这个数据库是用来查数据的用的,从第一,二条缺点也可以推断出第三条了。
对于经常用来查询的字段,建立索引的利大于弊,复习一下:
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。 第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。 第四,在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
有了索引后,我们查询就方便多了,大家想不想知道我们的DuDu是否入围呢? 大伙来围观:
select * from dbo.cdsgus where Name='杜勇' --耗时 4s --(393 row(s) affected)
篇幅有限,也防和谐,就不在这公布具体数据,嘻嘻!大家想一想,同名的人在国内还是挺多的嘛!
由于我们在这里都是直接对数据库查询,也就不提供应用层的界面了,如Winform,WebFromt等,如果做那些,
要考虑到储存过程,高效分页查询等,所以下文继续只是用SQL语句演示。
疱丁解牛---分析表结构设计
上面从实用主义出发,走的是:下载---》还原---》查询结果,这样的路线,根本没有从底层,结构上的东西去想一想。
嗯,那现们静下心来,看一看这个表设计如何,
Column_name Type Computed Length Prec Scale Nullable TrimTrailingBlanks FixedLenNullInSource Collation Name yes (n/a) (n/a) Chinese_PRC_CI_AS CardNo yes (n/a) (n/a) Chinese_PRC_CI_AS Descriot yes (n/a) (n/a) Chinese_PRC_CI_AS CtfTp yes (n/a) (n/a) Chinese_PRC_CI_AS CtfId yes (n/a) (n/a) Chinese_PRC_CI_AS Gender yes (n/a) (n/a) Chinese_PRC_CI_AS Birthday yes (n/a) (n/a) Chinese_PRC_CI_AS Address yes (n/a) (n/a) Chinese_PRC_CI_AS Zip yes (n/a) (n/a) Chinese_PRC_CI_AS Dirty yes (n/a) (n/a) Chinese_PRC_CI_AS District1 yes (n/a) (n/a) Chinese_PRC_CI_AS District2 yes (n/a) (n/a) Chinese_PRC_CI_AS District3 yes (n/a) (n/a) Chinese_PRC_CI_AS District4 yes (n/a) (n/a) Chinese_PRC_CI_AS District5 yes (n/a) (n/a) Chinese_PRC_CI_AS District6 yes (n/a) (n/a) Chinese_PRC_CI_AS FirstNm yes (n/a) (n/a) Chinese_PRC_CI_AS LastNm yes (n/a) (n/a) Chinese_PRC_CI_AS Duty yes (n/a) (n/a) Chinese_PRC_CI_AS Mobile yes (n/a) (n/a) Chinese_PRC_CI_AS Tel yes (n/a) (n/a) Chinese_PRC_CI_AS Fax yes (n/a) (n/a) Chinese_PRC_CI_AS EMail yes (n/a) (n/a) Chinese_PRC_CI_AS Nation yes (n/a) (n/a) Chinese_PRC_CI_AS Taste yes (n/a) (n/a) Chinese_PRC_CI_AS Education yes (n/a) (n/a) Chinese_PRC_CI_AS Company yes (n/a) (n/a) Chinese_PRC_CI_AS CTel yes (n/a) (n/a) Chinese_PRC_CI_AS CAddress yes (n/a) (n/a) Chinese_PRC_CI_AS CZip yes (n/a) (n/a) Chinese_PRC_CI_AS Family yes (n/a) (n/a) Chinese_PRC_CI_AS Version yes (n/a) (n/a) Chinese_PRC_CI_AS id no (n/a) (n/a) NULL
可以看得出作者很懒,除了主键id,其它字段一律用 nvarchar,长度4000应付,其实看到这里的时候,我有点怀疑这些数据的真实性(是否随意生成)的。
与 , 之间。字节的存储大小是所输入字符个数的两倍。所输入的数据字符长度可以为零
验证数据真实性(希望更多朋友提供准确的方式)
带着疑问出发,我用了这个语句来查询:
select * from dbo.cdsgus where Address like 'XX省XXx市XX镇XX村%' --X代表地点名称
呵呵,还真找到了几个熟悉的小伙伴,尚且当它是真的吧。又或者原数据库被人加工、整理过之后,随便建立了一个表保存起来的之后再备份出来放在网上的。
(我就纳闷了,整个备份文件还原之后仅只有一个表~)
说回正题,我们再看看:
Name Owner Type Created_datetime cdsgus dbo user table
作者是夜猫子,看一看创建日期: ::21.120。
ID自动增涨列:
Identity Seed Increment Not For Replication id
index_name index_description index_keys Index_Birthday nonclustered located on PRIMARY Birthday Index_ByAddress nonclustered located on PRIMARY Address Index_ByName nonclustered located on PRIMARY Name PK_cdsgus clustered, unique, primary key located on PRIMARY id
原本只有id默认主键索引,Name,Address,Birthday是我后来加上的。
constraint_type constraint_name delete_action update_action status_enabled PRIMARY KEY (clustered) PK_cdsgus (n/a) (n/a) (n/a) (n/a) id
ok!表字段结构我们看完了,尽管这这个数据库只有一个表,表中只设置了一个id主键。但从结果显示中,
我们可以看出,其实有几个字段是作外键使用的,
如下图中的:District1--6,family,id等。
既然了了外键,那么应该还有其它的数据表,希望哪位可以提供上来,呵呵。
天马行空---数据挖掘、分析、利用
数据有了,除了用来练习几个select 语句还能有什么用呢?
这个时候,该发挥我们的天马行空思想了,看一看,想一想里面都有什么了?
由于里面的数据有其真实性,且有了身份证号码,邮箱地址,手机号码......嘿嘿,想到了吧?不要用来做坏事哦!
- 骗子们得到数据后,可能会用来群发敲诈短信;
- 广告诉们可能会用来传广告;
- 数据挖掘专家可能会用来了解各地酒店人口入住人数,年龄阶段,分布时间范围等。
- ......
我们也来学着分析一下,(在园子里的应该都接触过sql语句,下面直接取样分析)
地区分布:(以北,上,广为例)
select count(*) as '北京' from dbo.cdsgus where Address like '北京%' select count(*) as '上海' from dbo.cdsgus where Address like '上海%' select count(*) as '广东' from dbo.cdsgus where Address like '广东%'
结论:留给大家思考......
年龄分布:
select count(*) as '00后' from dbo.cdsgus where Birthday like '20%' select count(*) as '1990后' from dbo.cdsgus where Birthday like '199%' select count(*) as '1980后' from dbo.cdsgus where Birthday like '198%' select count(*) as '1970后' from dbo.cdsgus where Birthday like '197%' select count(*) as '1960后' from dbo.cdsgus where Birthday like '196%' select count(*) as '1950后' from dbo.cdsgus where Birthday like '195%' select count(*) as '1940后' from dbo.cdsgus where Birthday like '194%' select count(*) as '1930后' from dbo.cdsgus where Birthday like '193%' select count(*) as '1920后' from dbo.cdsgus where Birthday like '192%' select count(*) as '1910后' from dbo.cdsgus where Birthday like '191%' select count(*) as '1900后' from dbo.cdsgus where Birthday like '190%' select count(*) as '1800后' from dbo.cdsgus where Birthday like '180%'
结果:80后作为主力军,你是否在其中呢?
类似的模糊查询,大家可以灵活变通,如查询客户是移动的用户还是联通的多(根据手机号码前N位),邮箱域名等。
更形象的建模数据挖掘,分析,推荐大家看:
对网上盛传的两千万泄漏数据的简单分析 - 深蓝 - 博客园
http://www.cnblogs.com/studyzy/p/3388887.html
在这,借用一些数据:
年龄阶段分布
省份分布
省份 | 酒店排行 | 人口排行 | 上升名次 |
江苏 | 1 | 5 | 4 |
上海 | 2 | 24 | 22 |
北京 | 3 | 26 | 23 |
山东 | 4 | 2 | -2 |
广东 | 5 | 1 | -4 |
浙江 | 6 | 10 | 4 |
河南 | 7 | 3 | -4 |
湖北 | 8 | 9 | 1 |
辽宁 | 9 | 14 | 5 |
陕西 | 10 | 16 | 6 |
河北 | 11 | 6 | -5 |
福建 | 12 | 17 | 5 |
山西 | 13 | 18 | 5 |
安徽 | 14 | 8 | -6 |
黑龙 | 15 | 15 | 0 |
天津 | 16 | 27 | 11 |
四川 | 17 | 4 | -13 |
江西 | 18 | 13 | -5 |
湖南 | 19 | 7 | -12 |
吉林 | 20 | 21 | 1 |
内蒙 | 21 | 23 | 2 |
重庆 | 22 | 20 | -2 |
广西 | 23 | 11 | -12 |
甘肃 | 24 | 22 | -2 |
贵州 | 25 | 19 | -6 |
* | 26 | 25 | -1 |
云南 | 27 | 12 | -15 |
海南 | 28 | 28 | 0 |
宁夏 | 29 | 29 | 0 |
青海 | 30 | 30 | 0 |
* | 31 | 31 | 0 |
手机号段分布
139 | 1399857 |
138 | 1230530 |
135 | 782764 |
136 | 778188 |
137 | 683742 |
186 | 581451 |
159 | 456526 |
158 | 434760 |
133 | 356135 |
150 | 324798 |
前10大邮箱域名排名:
@qq.com | 611842 |
@163.com | 594392 |
@126.com | 274512 |
@hotmail.com | 203237 |
@sina.com | 151798 |
@yahoo.com.cn | 101692 |
@gmail.com | 96346 |
@139.com | 67565 |
@sohu.com | 50179 |
@yahoo.cn | 31274 |
建议
查询示例到此告一段落,现在网上也有不少人把这数据库作为数据源,可以通过Web页面进行查询了,
但我用了几个都很不理想,速度慢得可怜,有时候真怀疑他有没有建立相应的索引了,最大的局限性是:仅能通过身份证,姓名去查。
对于作为程序员的你,你接受这样的低权限吗?
所以,建议大家有空还是应该找到这文件,在本地测试,推荐给所有在校生,可以用来练习一下多数据查询,优化问题。
当然,如果哪位朋友有资源的话,建立一个Web页面,可以直接输入sql命令来查询的那就更好了。
最后来一张关于数据价值转化的图:
我们现在得到数据库了,直接到达了加工车间这一层,至于怎样去发掘,那就要看你们的创意了。
总结
自从在学校交了210元的费用考了个所谓的的数据库工程师资格证(学校要求必须要考,但没什么技术含量),
之后比较少接触SqlServer数据库了,转眼,10月底了,再过一段时间就要准备实习了,目前还是在校生。
这次藉由这个2000w来温习了一下相关的基础知识,
磁盘IO性能问题,数据备份,恢复,索引,模糊查询,函数调用,大数据分析,挖掘,利用等。
抛砖引玉---期待亮点评论
此文,仅仅代表一个在校生初试2000w条记录的数据库的浅显实践,泛泛而谈,个中的结论,推断难免轻浮、果断。
不足之处,还望各位资深读者多多指导,斧正本文的错误论点。