⼤数据时代的海量数据存储、和⾼并发解决⽅案总结
⼀、结构化数据的存储
随着互联⽹应⽤的⼴泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于⼀个⼤型的互联⽹应⽤,每天⼏⼗亿的PV⽆疑对数据库造成了相当⾼的负载。对于系统的稳定性和扩展性造成了极⼤的问题。
⽔平切分数据库,可以降低单台机器的负载,同时最⼤限度的降低了了宕机造成的损失。
通过负载均衡策略,有效的降低了单台机器的访问负载,降低了宕机的可能性;
通过集⽅案,解决了数据库宕机带来的单点数据库不能访问的问题;
通过读写分离策略更是最⼤限度了提⾼了应⽤中读取(Read)数据的速度和并发量。
1、什么是数据切分
通过⼀系列的切分规则将数据⽔平分布到不同的DB或table中,在通过相应的DB路由或者 table路由规则到需要查询的具体的DB或者table,以进⾏Query操作。这⾥所说的“sharding”通常是指“⽔平切分”。具
体将有什么样的切分⽅式呢和路由⽅式呢?接下来举个简单的例⼦:我们针对⼀个Blog应⽤中的⽇志来说明,⽐如⽇志⽂章(article)表有如下字段:
article_id(int),  title(varchar(128)),  content(varchar(1024)),  user_id(int)
我们可以这样做,将user_id为 1~10000的所有的⽂章信息放⼊DB1中的article表中,将user_id为10001~20000的所有⽂章信息放⼊DB2中的 article表中,以此类推,⼀直到DBn。以此类推,利⽤分库的规则,反向的路由到具体的DB,这个过程我们称之为“DB路由”。
考虑到数据切分的DB设计,将违背这个通常的规矩和约束,为了切分,我们不得不在数据库的表中出现冗余字段,⽤作区分字段或者叫做分库的标记字段,⽐如上⾯的article的例⼦中的user_id这样的字段(当然,刚才的例⼦并没有很好的体现出user_id的冗余性,因为
user_id这个字段即使就是不分库,也是要出现的,算是我们捡了便宜吧)。当然冗余字段的出现并不只是在分库的场景下才出现的,在很多⼤型应⽤中,冗余也是必须的,这个涉及到⾼效DB的设计。
2、为什么要数据切分
举个例⼦说明,⽐如article表中现在有5000w条数据,此时我们需要在这个表中增加(insert)⼀条新的数据,insert完毕后,数据库会针对这张表重新建⽴索引,5000w⾏数据建⽴索引的系统开销还是不
容忽视的。但是反过来,假如我们将这个表分成100 个table呢,从article_001⼀直到article_100,5000w⾏数据平均下来,每个⼦表⾥边就只有50万⾏数据,这时候我们向⼀张只有50w⾏数据的table中insert数据后建⽴索引的时间就会呈数量级的下降,极⼤了提⾼了DB的运⾏时效率,提⾼了DB的并发量。当然分表的好处还不知这些,还有诸如写操作的锁操作等,都会带来很多显然的好处。
Oracle的DB确实很成熟很稳定,但是⾼昂的使⽤费⽤和⾼端的硬件⽀撑不是每⼀个公司能⽀付的起的。我们⽤免费的MySQL和廉价的Server甚⾄是PC做集,达到⼩型机+⼤型商业DB的效果,减少⼤量的资⾦投⼊,降低运营成本,何乐⽽不为呢?所以,我们选择Sharding
3、怎么数据切分
1. 数据切分可以是物理上的,对数据通过⼀系列的切分规则将数据分布到不同的DB服务器上,通过路由规则路由访问特定的数据库,这
样⼀来每次访问⾯对的就不是单台服务器了,⽽是N台服务器,这样就可以降低单台机器的负载压⼒。
2. 数据切分也可以是数据库内的 ,对数据通过⼀系列的切分规则,将数据分布到⼀个数据库的不同表中,⽐如将article分为
article_001,article_002等⼦表,若⼲个⼦表⽔平拼合有组成了逻辑上⼀个完整的article表,这样做的⽬的其实也是很简单的。
综上,分库降低了单点机器的负载;分表,提⾼了数据操作的效率,尤其是Write操作的效率。 ⾏⽂⾄此我们依然没有涉及到如何切分的问题。接下来,我们将对切分规则进⾏详尽的阐述和说明。
要想做到数据的⽔平切分,在每⼀个表中都要有相冗余字符 作为切分依据和标记字段,通常的应⽤中我们选⽤user_id作为区分字段,基于此就有如下三种分库的⽅式和规则:
按号段分:
(1) user_id为区分,1~1000的对应DB1,1001~2000的对应DB2,以此类推;
优点:可部分迁移
缺点:数据分布不均
(2)hash取模分:
对user_id进⾏hash(或者如果user_id是数值型的话直接使⽤user_id 的值也可),然后⽤⼀个特定的
数字,⽐如应⽤中需要将⼀个数据库切分成4个数据库的话,我们就⽤4这个数字对user_id的hash值进⾏取模运算,也就是user_id%4,这样的话每次运算就有四种可能:结果为1的时候对应DB1;结果为2的时候对应DB2;结果为3的时候对应DB3;结果为0的时候对应DB4,这样⼀来就⾮常均匀的将数据分配到4个DB中。
优点:数据分布均匀
缺点:数据迁移的时候⿇烦,不能按照机器性能分摊数据
(3)在认证库中保存数据库配置
就是建⽴⼀个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询⼀次这个数据库,以得到具体的DB信息,然后才能进⾏我们需要的查询操作。
优点:灵活性强,⼀对⼀关系
缺点:每次查询之前都要多⼀次查询,性能⼤打折扣
以上就是通常的开发中我们选择的三种⽅式,有些复杂的项⽬中可能会混合使⽤这三种⽅式。 通过上⾯的描述,我们对分库的规则也有了简单的认识和了解。当然还会有更好更完善的分库⽅式,还需要我们不断的探索和发现。
分布式数据⽅案提供功能如下:
(1)提供分库规则和路由规则(RouteRule简称RR),将上⾯的说明中提到的三中切分规则直接内嵌⼊本系统,具体的嵌⼊⽅式在接下来的内容中进⾏详细的说明和论述;
(2)引⼊集(Group)的概念,保证数据的⾼可⽤性;
(3)引⼊负载均衡策略(LoadBalancePolicy简称LB);
(4)引⼊集节点可⽤性探测机制,对单点机器的可⽤性进⾏定时的侦测,以保证LB策略的正确实施,以确保系统的⾼度稳定性;
(5)引⼊读/写分离,提⾼数据的查询速度;
⼆、针对海量数据和⾼并发的主要解决⽅案
2.1、海量数据的解决⽅案:
1. 使⽤缓存;
2. 页⾯静态化技术;
3. 数据库优化;
4. 分离数据库中活跃的数据;
5. 批量读取和延迟修改;
6. 读写分离;
7. 使⽤NoSQL和Hadoop等技术;
8. 分布式部署数据库;
9. 应⽤服务和数据服务分离;
10. 使⽤搜索引擎搜索数据库中的数据;
11. 进⾏业务的拆分;
2.2、⾼并发情况下的解决⽅案:
1. 应⽤程序和静态资源⽂件进⾏分离;
2. 页⾯缓存;
3. 集与分布式;
4. 反向代理;
5. CDN;
三、海量数据的解决⽅案
3.1、使⽤缓存
⽹站访问数据的特点⼤多数呈现为“⼆⼋定律”:80%的业务访问集中在20%的数据上。
例如:在某⼀段时间内百度的搜索热词可能集中在少部分的热门词汇上;新浪微博某⼀时期也可能⼤家⼴泛关注的主题也是少部分事件。
总的来说就是⽤户只⽤到了总数据条⽬的⼀⼩部分,当⽹站发展到⼀定规模,数据库IO操作成为性能瓶颈的时候,使⽤缓存将这⼀⼩部分的热门数据缓存在内存中是⼀个很不错的选择,不但可以减轻数据库的压⼒,还可以提⾼整体⽹站的数据访问速度。
使⽤缓存的⽅式可以通过程序代码将数据直接保存到内存中,例如通过使⽤Map或者ConcurrentHashMap;另⼀种,就是使⽤缓存框架:Redis、Ehcache、Memcache等。
使⽤缓存框架的时候,我们需要关⼼的就是什么时候创建缓存和缓存失效策略。
缓存的创建可以通过很多的⽅式进⾏创建,具体也需要根据⾃⼰的业务进⾏选择。例如,新闻⾸页的新闻应该在第⼀次读取数据的时候就进⾏缓存;对于点击率⽐较⾼的⽂章,可以将其⽂章内容进⾏缓存等。
内存资源有限,选择如何创建缓存是⼀个值得思考的问题。另外,对于缓存的失效机制也是需要好好研究的,可以通过设置失效时间的⽅式进⾏设置;也可以通过对热门数据设置优先级,根据不同的优先级设置不同的失效时间等;
需要注意的是,当我们删除⼀条数据的时候,我们要考虑到删除该条缓存,还要考虑在删除该条缓存之前该条数据是否已经到达缓存失效时间等各种情况!
使⽤缓存的时候还要考虑到缓存服务器发⽣故障时候如何进⾏容错处理,是使⽤N多台服务器缓存相
同的数据,通过分布式部署的⽅式对缓存数据进⾏控制,当⼀台发⽣故障的时候⾃动切换到其他的机器上去;还是通过Hash⼀致性的⽅式,等待缓存服务器恢复正常使⽤的时候重新指定到该缓存服务器。Hash⼀致性的另⼀个作⽤就是在分布式缓存服务器下对数据进⾏定位,将数据分布在不⽤缓存服务器上。
3.2、页⾯静态化技术
使⽤传统的JSP界⾯,前端界⾯的显⽰是通过后台服务器进⾏渲染后返回给前端游览器进⾏解析执⾏,如下图:
当然,现在提倡前后端分离,前端界⾯基本都是HTML⽹页代码,通过Angular JS或者NodeJS提供的路由向后端服务器发出请求获取数据,然后在游览器对数据进⾏渲染,这样在很⼤程度上降低了后端服务器的压⼒。
还可以将这些静态的HTML、CSS、JS、图⽚资源等放置在缓存服务器上或者CDN服务器上,⼀般使⽤最多的应该是CDN服务器或者Nginx服务器提供的静态资源功能。
规则1:尽量减少HTTP请求。
规则2:使⽤CDN.
规则3:添加Expires头。
规则4:采⽤Gzip压缩组件。
规则5:将样式表放在顶部。
规则6:将脚本放在底部。
规则7:避免CSS表达式。
规则8:使⽤外部JavaScript和CSS.
规则9:减少DNS查询
规则10:精简JavaScript.
规则11:避免重定向。
规则12:删除重复的脚本。
规则13:配置ETag
规则14:使Ajax可缓存。
3.3、数据库优化
数据库优化是整个⽹站性能优化的最基础的⼀个环节,因为,⼤多数⽹站性能的瓶颈都是开在数据库IO操作上,虽然提供了缓存技术,但是对数据库的优化还是⼀个需要认真的对待。⼀般公司都有⾃⼰的DBA团队,负责数据库的创建,数据模型的确⽴等问题,不像我们现在⼏个不懂数据库优化的⼈只能在⽹上⼀篇篇数据库优化的⽂章,⾃⼰去摸索,并没有形成⼀个系统的数据库优化思路。
对于数据库的优化来说,是⼀种⽤技术换⾦钱的⽅式。数据库优化的⽅式很多,常见的可以分为:数据库表结构优化、SQL语句优化、分区、分表、索引优化、使⽤存储过程代替直接操作等 。
3.3.1、表结构优化
对于数据库的 开发规范与使⽤技巧以及设计和优化,前边的时候总结了⼀些⽂章,这⾥偷个懒直接放地址,有需要的可以移步看⼀下:
a) MySQL开发规范与使⽤技巧总结:blog.csdn/xlgen157387/article/details/48086607
b) 在⼀个千万级的数据库查寻中,如何提⾼查询效率?:blog.csdn/xlgen157387/article/details/44156679
另外,再设计数据库表的时候需不需要创建外键,使⽤外键的好处之⼀可以⽅便的进⾏级联删除操作,但是现在在进⾏数据业务操作的时候,我们都通过事物的⽅式来保证数据读取操作的⼀致性,我感觉相⽐于使⽤外键关联MySQL⾃动帮我们完成级联删除的操作来说,还是⾃⼰使⽤事物进⾏删除操作来的更放⼼⼀些。
3.3.2、SQL优化
对于SQL的优化,主要是针对SQL语句处理逻辑的优化,⽽且还要根据索引进⾏配合使⽤。另外,对于SQL语句的优化我们可以针对具体的业务⽅法进⾏优化,我们可以将执⾏业务逻辑操作的数据库执⾏时间记录下来,来进⾏有针对性的优化,这样的话效果还是很不错的!例如下图,展⽰了⼀条数据库操作执⾏调⽤的时间:
关于SQL优化的⼀些建议,以前整理了⼀些,还请移步查看:
a) 19个MySQL性能优化要点解析:blog.csdn/xlgen157387/article/details/50735269
b) MySQL批量SQL插⼊各种性能优化:blog.csdn/xlgen157387/article/details/50949930
3.3.3、分表
分表是将⼀个⼤表按照⼀定的规则分解成多张具有独⽴存储空间的实体表,我们可以称为⼦表,每个表都对应三个⽂件,MYD数据⽂件,.MYI索引⽂件,.frm表结构⽂件。这些⼦表可以分布在同⼀块磁盘上,也可以在不同的机器上。数据库读写操作的时候根据事先定义好的规则得到对应的⼦表名,然后去操作它。
例如:⽤户表
⽤户的⾓⾊有很多种,可以通过枚举类型的⽅式将⽤户分为不同类别category:学⽣、教师、企业等 ,这样的话,我们就可以根据类别category来对数据库进⾏分表,这样的话每次查询的时候现根据⽤户的类型锁定⼀个较⼩的范围。
不过分表之后,如果需要查询完整的顺序就需要使⽤多表操作了。
3.3.4、分区
数据库分区是⼀种物理数据库设计技术,DBA和数据库建模⼈员对其相当熟悉。虽然分区技术可以实现很多效果,但其主要⽬的是为了在特定的SQL操作中减少数据读写的总量以缩减响应时间。
分区和分表相似,都是按照规则分解表。不同在于分表将⼤表分解为若⼲个独⽴的实体表,⽽分区是将数据分段划分在多个位置存放,可以是同⼀块磁盘,也可以在不同的机器。分区后,表⾯上还是⼀张表,但数据散列到多个位置了。数据库读写操作的时候操作的还是⼤表名字,DMS⾃动去组织分区的数据。
当⼀张表中的数据变得很⼤的时候,读取数据,查询数据的效率⾮常低下,很容易的就是讲数据分到不同的数据表中进⾏保存,但是这样分表之后会使得操作起来⽐较⿇烦,因为,将同类的数据分别放在不同的表中的话,在搜索数据的时候需要便利查询这些表中的数据。想进⾏CRUD操作还需要先到对应的所有表,如果涉及到不同的表的话还要进⾏跨表操作,这样操作起来还是很⿇烦的。
sql语句优化方式使⽤分区的⽅式可以解决这个问题,分区是将⼀张表中的数据按照⼀定的规则分到不同的区中进⾏保存,这样进⾏数据查询的时候如果数据的范围在同⼀个区域内那么就可以⽀队⼀个区中的数据进⾏操作,这样的话操作起来数据量更少,操作速度更快,⽽且该⽅法是对程序透明的,程序不需要进⾏任何的修改。
3.3.5、索引优化
索引的⼤致原理是在数据发⽣变化的时候就预先按指定字段的顺序排列后保存到⼀个类似表的结构中,这样在查索引字段为条件记录时就可以很快地从索引中到对应记录的指针并从表中获取到相应的数据,这样速度是很快地。
不过,虽然查询的效率⼤⼤提⾼了,但是在进⾏增删改的时候,因为数据的变化都需要更新相应的索引,也是⼀种资源的浪费。
关于使⽤索引的问题,对待不同的问题,还是需要进⾏不同的讨论,根据具体的业务需求选择合适的索引对性能的提⾼效果是很明显的⼀个举措!
推荐⽂章阅读:
a) 数据库索引的作⽤和优点缺点以及索引的11中⽤法:blog.csdn/xlgen157387/article/detail
s/45030829
b) 数据库索引原理:blog.csdn/kennyrose/article/details/7532032
3.3.6、使⽤存储过程代替直接操作
存储过程(Stored Procedure)是在⼤型数据库系统中,⼀组为了完成特定功能的SQL 语句集,存储在数据库中,经过第⼀次编译后再次调⽤不需要再次编译,⽤户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执⾏它。存储过程是数据库中的⼀个重要对象,任何⼀个设计良好的数据库应⽤程序都应该⽤到存储过程。
在操作过程⽐较复杂并且调⽤频率⽐较⾼的业务中,可以将编写好的sql语句⽤存储过程的⽅式来代替,使⽤存储过程只需要进⾏⼀次变异,⽽且可以在⼀个存储过程⾥做⼀些复杂的操作
3.4、分离数据库中活跃的数据
正如前边提到的“⼆⼋定律”⼀样,⽹站的数据虽然很多,但是经常被访问的数据还是有限的,因此可以讲这些相对活跃的数据进⾏分离出来单独进⾏保存来提⾼处理效率。
其实前边使⽤缓存的思想就是⼀个很明显的分离数据库中活跃的数据的使⽤案例,将热门数据缓存在内存中。
还有⼀种场景就是,例如⼀个⽹站的所⽤注册⽤户量很⼤千万级别,但是经常登录的⽤户只有百万级别,剩下的基本都是很长时间都没有进⾏登录操作,如果不把这些“僵⼫⽤户”单独分离出去,那么我们每次查询其他登录⽤户的时候,就⽩⽩浪费了这些僵⼫⽤户的查询操作。
3.5、批量读取和延迟修改
批量读取和延迟修改的原理是通过减少操作数据库的操作来提⾼效率
批量读取是将多次查询合并到⼀次中进⾏读取,因为每⼀个数据库的请求操作都需要链接的建⽴和链接的释放,还是占⽤⼀部分资源的,批量读取可以通过异步的⽅式进⾏读取。
延迟修改是对于⼀些⾼并发的并且修改频繁修改的数据,在每次修改的时候⾸先将数据保存到缓存中,然后定时将缓存中的数据保存到数据库中,程序可以在读取数据时可以同时读取数据库中和缓存中的数据。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。