数据库分片前的准备

为了解决前面的问题,我们要对库中的表进行水平拆分,也就是要对数据库进行分片处理,我们通常所说的分库分表呢,大多数情况下指的就是这种方式,这和MSYQL的分区表呢,也有些类似,不同的是MYSQL的分区表是在同一个节点上的,同一个数据库中建立的,而数据库分片后呢,通常是存在不同的物理节点上,数据库的分片呢,不像我们前面讲的分库分表一样容易实现,原来独立的数据库进行分片,必须要考虑很多问题,通常来说,如果不是万不得已,真的不建议大家,对数据库进行分片,我们见过很多的项目,业务刚刚上线的时候呢,数据库并发和负载,远远没有达到限制时,就对数据库进行分片处理,其实我觉得这样做完全是没有必要的,我们应该首先考虑,是否能够通过性能调优,或者更好的应用和数据库设计,来推迟分片,还是那句话,对数据库进行分片呢,并不是那么容易,并且分片后,数据库还会变的难以维护,如果我们到了真的不得不分片的时候呢,那也只能对数据库进行分片了,下面这个图呢,就是对订单表进行分片的一个结果,在单独的节点中,我们只有一个订单表相关的数据库,结果分片后呢,我们会形成多个相同结构的订单数据库,当然呢,多个数据库呢,可能会分布在不同的节点中,那么下面我们就来看看,能不能对一个独立的数据库进行分片,应该提前考虑些什么样的问题

在对数据库进行分片前,非常重要的一项工作,就是选择分区键,分区键决定了我们如何对数据库进行分片,以及分片后如何查询数据,分区键选择的是否合适呢,直接决定了分区后的数据库性能,对于分区键的选择,我们应该做到以下几点,首先选择分区键时,要尽可能的避免跨分区的查询数据的这种可能,如果完全的不做跨分区,查询数据呢,几乎也是不可能的,因为我们有很多统计分析类的查询,都需要跨分区来处理,和汇总数据,但是对于OLTP的应用,我们要尽可能减少跨分区的产生,因为跨分片查询时,应用不得不对多个分片进行查询,然后再把查询的结果,合并在一起,这通常来说,效率会很低,甚至比未分片的性能还要差,比如对于一个博客类的应用来说,如果我们按博客文章的ID,哈希值来分片,就可能不太合适,因为我们在显示每个人的博客时,都需要跨所有的分区,来进行查找,才能得到一个人所有的文章,而如果我们选择userId来做分片呢,则能保证同一个人的博客,全在一个分片中,如果想要显示一个人的所有博客,则只要在这一个分片中进行查询就可以了,所以在这种情况下,使用userIde和使用文章的id呢,来分片就显得更为合适一些,其次分区键要尽可能的可以保证各个分片中的数据量,是平均的,我们对数据库进行分片的目的呢,是为了减少主库的写负载,如果分片后,大部分的写呢,还是集中在某一个分片中,这样我们就失去了分片的意义,例如对于一个订单数据库,如果我们使用下单用户的ID来做分区键,并且在范围来进行分片的话,那么就要看看选择的ID范围是否合理,是不是大部分活用的用户分到一个片中,当然了,如果我们可以保证在分片后,每个查询都包括这个分区键的话,使用哈希函数来进行分区,可能是一种最好的使用分区的方式了

在一个应用中,我们需要分片的表总是少数的,换句话说,不可能一个应用中的所有表,都被频繁的写入数据,分片后,那些不要分片的表,如何存储,总的来说呢,也有两种方式,第一种就是在每个分片中呢,存储一份相同的数据,这种方法呢,通常用于表本身数据量不大,而且不会经常被更新的自检类表,同时这些表又经常需要和分区表,一起关联查询,在一个分区中存储一份冗余的数据呢,可以更好地提高数据库的查询效率,如果使用这种方式,对于维护每个分片中,相同表的一致性,就显得非常重要了,我们一般可以使用多写的方式,来维护这些表的数据,也就是说,利用在对这些表更新时,会同时更新所有分片中相同的表,但是由于这种更新呢,一般不会放在一个事务中,所以我们要定期的对这些表的数据呢,进行检查,以防止由于这些表,数据不一致而造成业务逻辑上的问题,而另一种方式呢,就是把所有不需要分片的数据,存储在一个公共的位置,这种方法的好处呢,就是在集群中不存在数据冗余的问题,应用也不用维护多份相同的数据,如果分片的表要和这些表进行关联查询的话,就只能由程序分别查询后,再进行操作了,所以这种方法在查询效率上,比上一种方法要差一些

这种方法在查询效率上,比上一种方法要差一些,接下来我们还要考虑如何在节点上部署分片的问题,并不是说一个节点上,能部署一个数据库的分片,对于分片如何部署到节点上,有很多种可能可以选择的方式,其中一些最常用的方法呢,就是下面这些,每个分片使用单一一个数据库,并且数据库名也相同,一般来说,每个分片的数据库结构呢都要和原来单一实例数据库的结构相同,所以在每个分片上部署一个分片数据库,可以保证数据库,表名,和原来单一数据库,是完全一致的,而另一种做法呢,是将多个分片,存储在一个数据库中,由于数据库中的表是不能重名的,所以这时候呢,就需要在每个分片表的后面,加上一个分片号来作为后缀,以区分不同的分片表,这种情况下呢,一个数据库中呢,可以包含多个分片表,当然了,除了对一个分片表存储在一个数据库中外,我们还可以在一个节点中部署多个数据库,由于一个数据库节点的数据库名呢,也是不能够重复的,所以我们要对数据库名来进行编号了,并且在每个数据库中,包含一套或多套分片表,当然了,如果要在一个数据库中包含多个分片表的话,我们同时还要对分片表再进行编号处理,那么除了以下几个方法外呢,我们还可以使用一个节点多实例的方法,来存储这个分片,无论使用哪种方法呢,都要考虑我们的应用如何修改才能适应我们的分片存储方式,一定还要避免把数据写入错误的一种分片,或者去错误分片中去查询数据的情况出现,这也是对数据库进行分片处理中,复杂的一个部分

在节点中如何存储分片后,我们还要决定如何在分片中,分配数据,数据在各个分片中的分布,对数据库的性能又很大的影响,首先我们可以尽量平均的在各个分片中来分片数据,这个平均呢不单单指数据量的平均,还包括访问量上的平均,因为我们发现,在一些情况下,热点数据呢,很有可能集中在某一个或者某几个分片中,这时这几个分片的负载呢,都要比其他分片的负载要高的多,当数据库查询这几个负载高的分片时呢,性能就会大幅下降,所以选择如何在分片中,分配数据,就一定要结合我们的业务情况来做分析,然后再做决定,一般来说,在分片中分配数据呢,可以使用一下几种方式,第一种是使用分区键的哈希值,然后和计划分区的数量来取模的方式,来在不同的分片中呢,分配数据,比如我们要把分区键为101的数据,分配到10个分片键中的一个,那么就可以通过101对分片的模,得到分片号为1的分片,之所以要对分区键进行哈希分片呢,然后再取模呢,因为分区键呢,并不总是数值型的数据,所以我们要利用合适的哈希函数,来吧其他类型的数据,转换为数值类型的数据,然后再取模,当然了,如果和上面的例子一样,本身就是利用自增ID的主键来当做分区键的,那样的话呢,就可以使用分区键取模的方式呢,来在各个分区中,分配数据了,使用这个方式的好处呢,就是可以相对平均的在各个分片中分配数据,而第二种方式呢,就是利用分区键的范围,来在各个分片中分配数据,还以上面的例子来说,如果我们有10个分片,第一个分片中包含1到100的数据,第二个分片中呢,包含了101到200的数据,以此类推,如果我们要保存101的数据呢,如果使用这种方式的话呢,这个数据就应该存储在第二个应用中,这种分配分片数据的方式呢,常用于分区键为日期类型的数据,或者数值类型的数据文件,其优点呢就是我们可以很清楚的知道,数据会分配到哪个分区片中,但是那种方式很容易产生数据分配不平均,以及数据访问量的情况,之前我就遇到过这样的情况,那个时候是对学生做题的题库数据库,进行分片,当时是选择学生课题ID的范围,来进行分片的,但是这个数据库运行了一段时间之后呢,我们就会发现了,选择某一课程的学生呢,远远多于其他课程,所以这门课程所在的数据库压力呢,也就原高其他分片,所以一旦对这个数据库进行读写操作的时候呢,应用的性能就会急剧下降,以至于我们后面不得不对这个数据进行分片处理,那么前面两种分片数据的分片方式呢,我们都无法灵活的堆哪些数据,那个分片中进行控制,而使用第三种方式,可以方便的对数据库的分片位置呢,来进行控制,这种方式就是要建立一个分区键和对应关系表,在这张表中呢,可以对什么数据,存储在哪些分片中,进行配置,在进行数据存储之前呢,先通过几张表,来获取具体要存储的分片中的信息,然后再把数据存储到分片中,大家可能会发现,使用这种方式时呢,对分区键和分区的对应关系表,可能会产生很大的读压力,所以我们可以使用缓存这种方式呢,来把这张表进行缓存,这一点大家一定要注意,否则这张表可能就会成为系统的瓶颈

当我们把一个单节点数据库,转换为多节点分片数据库,所面临的最大一个问题,可能就是分片后如何维护一个表中唯一ID的一个问题,在当节点环境中呢,我们可以使用mysql的auto_increment属性,来获取表中的唯一ID,但是分片后,如果我还使用这种方式,就会造成每个分片中的ID相同的情况,所以为了解决这个问题,我们就不得不更改或者唯一ID的方式了,比较常用的方法呢,有以下几个,第一种方式呢,还是使用auto_increment属性,来在表中生成唯一ID,不过我们要修改两个参数,一个是auto_increment_increment,另一个是auto_increment_offset,关于这两个参数呢,我们在介绍MYSQL高可用时呢,也为大家介绍过了,大家只要记住,auto_increment_increment的值呢,要和分片节点的数量相同,这个参数决定了每次自增ID的增长的步长,就是说,如果我们有6个节点,来分片集群,那么这个参数的值呢就要设置为9,而另一个参数呢,分别设置1到6不同的值,这样就能够保证每一个节点中,相同表生成自增ID的值呢,是不冲突的,但是这种方式只适用于,一个节点中只保存一套分区表的情况,如果一个节点保存了多套分区表,那么这个方式就不能够使用了,因为在同一节点中,相同的分区表中,所生成的ID还是会有冲突,所以我们就不能使用这种方式了,那么你来看看下面这两种方式,第二种方式呢,是配置一个全局节点ID,并在这个节点中呢,建立相应的表,来使用auto_increment的属性,来生成这个自增ID,应用程序先通过这张表,获取唯一ID后,再通过分区函数,把数据插入到不同的分片中,使用这种方式还是比较简单的,但是这个全局节点呢,又可能成为这个系统的性能瓶颈,所以我们就引出了下面这种方式第三种方式实际上和第二种方式非常像,只是用了Redis这样的缓存系统呢,来代替第二种方法中的全局节点,大家知道,Redis的这种读写效率呢,要远高于MYSQL,使用Redis生成唯一的ID呢,其效率也会非常的高,就避免了由于生成ID的效率的问题,而造成系统性能下降的发生,到此我们就知道进行数据库分片前,我们要做的准备工作都有什么,下面我们就以一个例子一起来看一看

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部