阅读目录
1.数据库选型
2.XCode组件分库技术
3.现有数据库的数据统计情况
本博客所有文章分类的总目录:【总目录】本博客博文总目录-实时更新
开源C#彩票数据资料库系列文章总目录:【目录】C#搭建足球赛事资料库与预测平台与彩票数据分析目录
本篇文章开始将逐步介绍使用C#搭建足球赛事资料库与预测平台的相关细节。还是先从数据库开始,从本文开始将逐步对每个核心实体类和数据库设计相关的内容进行讲解,并公布源代码,至于能不能跑起来,看的看个人努力。数据库很庞大,且采用了XCode非常牛逼的分库技术,秒杀千万级乃至上亿的数据需求。而只需要最基本的C#技术,对我这种数据库文盲来说,真的帮助非常大。
考虑到足球赛事资料库的复杂性,以及考虑到项目的前瞻性(要考虑到很多还没有发生的事情,便于以后扩展),以及大量数据,查询和计算的速度,本项目经历了3次重构,到目前为止其实也不是很成型,但基本趋于稳定。现在总结起来肯定是很流畅,但这中间的过程非常痛苦,也希望把这些经验写出来,有自己做的朋友可以一起探讨,避免踩坑。
本文原文地址:【原创】C#搭建足球赛事资料库与预测平台(2) 数据库与XCode组件
回到目录
1.数据库选型
1.1 概述
上学的时候,自己折腾数据库,现在看来其实是走了很多弯路,动不动就搞一个庞大的Ms Sql ,哪怕一个再简单的东东,也搞一个mssql,安装不仅占用系统资源,还慢,部署也挺麻烦。。虽然用的是盗版,但其实现在看来,由于自己的无知和大学书本知识的匮乏,盲目的指导,我们失去了很多美好的东西。在近几年的工作中,我已经逐步抛弃了MSSQL,MySQL这些庞然大物,并不是说他们不能用,或者不好用。只是问题太小,杀鸡焉用牛刀。。。小题大作不仅自己累,还影响总体的效果,还会浪费很多宝贵的时间。在一些小的场景下,我会使用一些C#文件数据库,如NDataBase,XML Database等等,在一些实际数据量在亿及级别的情况下,会选择Sqlite数据库等等。只要能满足我的使用要求即可,没必要搞的那么花哨。
1.2 Sqlite优点
所以说说本项目使用的数据库:Sqlite,使用它的原因有:
1.轻量级、跨平台。它是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。而且那个动态库的尺寸也非常小,几百K而已。相比几个G的 MSSQL,情何以堪;
2.绿色、单一文件。它的核心引擎本身不依赖第三方的软件,使用不需要“安装”,可以省去部署时不少麻烦。是不是有人安装 MSSQL 经常报错。。没安装之前的校验就通不过。。是不是很坑爹;这个也是处于个人开发成本的考虑,想一想买一个几个G的数据库空间啥价格,而买几个G的硬盘空间简直就是白菜。。在不影响功能的情况下,何乐而不为;
3.XCode组件的特殊支持。本人12年起开始使用XCode组件,非常感谢它减轻了我很多关于数据库的东西,让我只需要关注业务。XCode对Sqlte的支持可以说更加人性化,对很多Sqlite的缺点进行了优化,而且有大量的使用案例。同时XCode在计划的V9版本开始支持网络和分布式数据库,这样Sqlite的功能将更加强大,没有理由不使用。而且XCode灵活的分库技术也是解决我查询问题的重要手段,虽然我不懂MSSQL上亿数据的优化,但我可以用分库,更加低的技术含量和有限的资源来解决问题;
1.3 Sqlite缺点与解决方案
说完Sqlite的优点,有人可能说,这些谁不知道,但怎么面对它的缺点呢?其实这都不是事。。。
1.不理想的在并发性能。这是Sqlite被认为的最大问题之一,由于是单一文件数据库,可能会被写操作独占,从而导致其它读写操作阻塞或出错。这个问题不能否认,我认为应该单独放在具体情况中来看。本文的项目数据库要求在常规时期写操作和读操作基本不会同步,因为写操作是数据采集更新的时候,这个时候是基本不会进行查询分析的;其次即使查询分析,由于大量的使用了分库技术,独占的情况也会少得多。
更重要的一点:XCode组件对Sqlite的并发做了特殊处理,如果失败就重试,间隔300毫秒,一共重试5次。所以这样可以极大的减少数据库锁定的错误。加上合理的分库策略,这种错误其实的机率非常低,实在不行,还可以用其他策略。所以在这个项目中,这个问题其实不是问题。至于Sqlite的并发性研究,可以参考XCode的主要开发者@大石头的文章:SQLite高并发研究报告
2.网络访问。有时候需要访问其它机器上的SQLite数据库文件,就会把数据库文件放置到网络共享目录上。据说这样会在并发读写的时候会出现数据损坏的问题。这个问题其实以前也困扰我,因为如果以后做大了,考虑做分布式的时候,这样是非常不便利的,利用网络共享目录肯定是诸多不方便和不安全,但考虑到XCode V9会进行重大更新,会支持网络和分布式数据库,到时候Sqlite也可以进行分布式操作,这些也都不是问题。至于亿级数据,也暂时不需要网络分布式,所以这个暂时不用担心。
回到目录
2.XCode组件分库技术
这是个人认为本项目非算法的最大的亮点之一。这和选用Sqlite作为数据库也有很大关系,一方面本人对数据库了解较少,无法用MSSQL轻松解决几千万数据的问题,数据迁移,服务器管理,大量数据查询的效率问题,这些对个人来说真的很难办到,不是办不到,是在有限的时间和精力下,不能做的更好,这也是选用XCode组件和Sqlite数据库的主要原因。说一说XCode分库技术的在本项目中的用法和作用,让大家也提前开开荤。
至于X组件的技术文章,可以参考本博客的菜单栏“X组件”。
2.1 分库的目的与场景
本项目中大量进行分库的目的很明显,减少查询的复杂程度,同时也便于数据库迁移和分离,特别考虑开源情况下,可以直接获取各个联赛数据库以及各种附加数据,而不需要单独进行导出。本项目的由于累积的历史数据会越来越多,而且经过前2个版本的测试,大量的附加数据查询非常频繁,因此必须通过这种方法来减少的查询复杂度。例如从单表500万的数据表中,多次查询数据 和 从单表3万的数据库中查,差别会有多少?当然我没测试过,简单情况下,时间总归是有差别的,应该也不小,而且500万数据是要增长的,而单表3万是基本不会增长(因为一场比赛结束了,相关数据就终止了,不会再增加)。
本项目的分库场景非常多,这里举几个例子说明,其他的在具体的数据库表设计和业务分析的时候再进行说明。
1.比赛场次表。比赛场次表按照联赛进行分库,每一个联赛或者杯赛,的数据都保存在一起。按照最多球队的西甲20支队伍来说,一年也就380场比赛,10年也就3800条记录,所以怎么查,技术再差,也不会慢到哪里去,何苦还有XCode的缓存支持;在前2个版本的设计中,还考虑过设计一个总库,但由于实际的使用情况场景还有待考察,所以新版本的数据库还暂时没有添加总库。总库和分库的作用是协调互补的,根据实际情况进行。
2.欧赔指数表。胜平负的玩法是足球比赛中最常见的,所以欧赔的数据也是最多的,欧赔指数的复杂性不在于每一场比赛会动态调整,而在于每一场比赛,每一个家赔率公司开出的赔率都不一样,赔率公司多大几百家,考虑到实际情况,本系统只采集了权威的20家左右,分库方式也很特别,为了获得查询的效率,按照 赔率公司 + 联赛 的方式进行分库。
3.其他指数表。大小盘指数表和其他指数表与欧赔类型,都采用了 最符合时间的 分库方式。
2.2 分库的方法与使用情况
在XCode中,分库是非常简单的事情,只需要修改对应的连接字符串,就可以了,查询之前也设置对应的链接字符串就可以了,下面就是核心的设置连接字符串的方法,注意方法的核心是修改Meat.ConnName,至于值取决你分库的标准,如下面的比赛场次表的分库:
下面这个是赔率数据的一个分库情况:
只需要在插入数据或者查询数据前,执行上述方法就可以了。其他操作和你的业务是一模一样的。
回到目录
3.现有数据库的数据统计情况
由于赔率数据的时效性,本系统也只采集了2013年以后的赔率数据,至于场次数据,可以追溯到10年前。所以暂时来说数据量不是很大,随着累计的时间越来越长,数据的增长会越来越快。写了一个简单的程序对现在项目的数据库的数量和数据记录做了一下统计:
看看核心代码:
//按照key-vale存储数据,key为表名称,value为记录对象Dictionary<String, DbCount> count = new Dictionary<string, DbCount>();//获取文件夹下所有的数据库var allDbFiles = GetAllFilesByFolder(folderName, true);//遍历所有数据库文件 int index = 1;foreach (var item in allDbFiles){ var path = item.ToString(); if (path.Contains("shm") || path.Contains("wal")) continue; //添加数据库连接字符串 DAL.AddConnStr("DbTest", "Data Source=" + item.ToString(), null, "Sqlite"); var dal = DAL.Create("DbTest"); List<IDataTable> tableList = dal.Tables;//获取所有的表 //循环每个表 Console.WriteLine("{0}-{1}",index++,allDbFiles.Count); foreach (var tab in tableList) { IEntityOperate Factory = dal.CreateOperate(tab.Name); int allCount = Factory.FindCount(); if (!count.ContainsKey(tab.Name)) count.Add(tab.Name, new DbCount(allCount)); else count[tab.Name].RecordCount += allCount; } if (DAL.ConnStrs.ContainsKey("DbTest")) DAL.ConnStrs.Remove("DbTest");}
看看统计的结果:
截至2015年5月8日,总的数据量是:743万。主要原因是一方面历史数据过了太长时间采集不到(国外可以,但是麻烦)。所以考虑到实际情况,只采集了2013年以后的主要赔率数据。以前的数据只采集基本的比赛结果信息。