集团控股

首页 - > 10bet十博网址APP > 集团控股 >

支撑百万并发的数据库架构如何设计?

作者:10bet十博网址APP     来源:10bet十博网址APP    

浏览量:56451     发布时间:2019-04-05

近期,信息集团知识产权投资公司党支部组织全体党员在公司会议室,就“学习强国”平台知识展开热烈的交流讨论与分享。

首先我们先考虑第一个问题,数据库每秒上万的并发请求应该如何来支撑呢要搞清楚这个问题,先得明白一般数据库部署在什么配置的服务器上。 通常来说,假如你用普通配置的服务器来部署数据库,那也起码是16核32G的机器配置。 这种非常普通的机器配置部署的数据库,一般线上的经验是:不要让其每秒请求支撑超过2000,一般控制在2000左右。 控制在这个程度,一般数据库负载相对合理,不会带来太大的压力,没有太大的宕机风险。

所以首先第一步,就是在上万并发请求的场景下,部署个5台服务器,每台服务器上都部署一个数据库实例。

然后每个数据库实例里,都创建一个一样的库,比如说订单库。

此时在5台服务器上都有一个订单库,名字可以类似为:db_order_01,db_order_02,等等。 然后每个订单库里,都有一个相同的表,比如说订单库里有订单信息表,那么此时5个订单库里都有一个订单信息表。

比如db_order_01库里就有一个tb_order_01表,db_order_02库里就有一个tb_order_02表。

这就实现了一个基本的分库分表的思路,原来的一台数据库服务器变成了5台数据库服务器,原来的一个库变成了5个库,原来的一张表变成了5个表。 然后你在写入数据的时候,需要借助数据库中间件,比如sharding-jdbc,或者是mycat,都可以。

你可以根据比如订单id来hash后按5取模,比如每天订单表新增50万数据,此时其中10万条数据会落入db_order_01库的tb_order_01表,另外10万条数据会落入db_order_02库的tb_order_02表,以此类推。

这样就可以把数据均匀分散在5台服务器上了,查询的时候,也可以通过订单id来hash取模,去对应的服务器上的数据库里,从对应的表里查询那条数据出来即可。

依据这个思路画出的图如下所示,大家可以看看:做这一步有什么好处呢第一个好处,原来比如订单表就一张表,这个时候不就成了5张表了么,那么每个表的数据就变成1/5了。 假设订单表一年有1亿条数据,此时5张表里每张表一年就2000万数据了。

那么假设当前订单表里已经有2000万数据了,此时做了上述拆分,每个表里就只有400万数据了。

而且每天新增50万数据的话,那么每个表才新增10万数据,这样是不是初步缓解了单表数据量过大影响系统性能的问题另外就是每秒1万请求到5台数据库上,每台数据库就承载每秒2000的请求,是不是一下子把每台数据库服务器的并发请求降低到了安全范围内这样,降低了数据库的高峰期负载,同时还保证了高峰期的性能。 但是上述的数据库架构还有一个问题,那就是单表数据量还是过大,现在订单表才分为了5张表,那么如果订单一年有1亿条,每个表就有2000万条,这也还是太大了。

所以还应该继续分表,大量分表。

比如可以把订单表一共拆分为1024张表,这样1亿数据量的话,分散到每个表里也就才10万量级的数据量,然后这上千张表分散在5台数据库里就可以了。 在写入数据的时候,需要做两次路由,先对订单idhash后对数据库的数量取模,可以路由到一台数据库上,然后再对那台数据库上的表数量取模,就可以路由到数据库上的一个表里了。 通过这个步骤,就可以让每个表里的数据量非常小,每年1亿数据增长,但是到每个表里才10万条数据增长,这个系统运行10年,每个表里可能才百万级的数据量。

这样可以一次性为系统未来的运行做好充足的准备,看下面的图,一起来感受一下:在分库分表之后你必然要面对的一个问题,就是id咋生成因为要是一个表分成多个表之后,每个表的id都是从1开始累加自增长,那肯定不对啊。 举个例子,你的订单表拆分为了1024张订单表,每个表的id都从1开始累加,这个肯定有问题了!你的系统就没办法根据表主键来查询订单了,比如id=50这个订单,在每个表里都有!所以此时就需要分布式架构下的全局唯一id生成的方案了,在分库分表之后,对于插入数据库中的核心id,不能直接简单使用表自增id,要全局生成唯一id,然后插入各个表中,保证每个表内的某个id,全局唯一。 比如说订单表虽然拆分为了1024张表,但是id=50这个订单,只会存在于一个表里。 那么如何实现全局唯一id呢有以下几种方案:这个方案就是说你的系统每次要生成一个id,都是往一个独立库的一个独立表里插入一条没什么业务含义的数据,然后获取一个数据库自增的一个id。 拿到这个id之后再往对应的分库分表里去写入。 比如说你有一个auto_id库,里面就一个表,叫做auto_id表,有一个id是自增长的。 那么你每次要获取一个全局唯一id,直接往这个表里插入一条记录,获取一个全局唯一id即可,然后这个全局唯一id就可以插入订单的分库分表中。 这个方案的好处就是方便简单,谁都会用。 缺点就是单库生成自增id,要是高并发的话,就会有瓶颈的,因为auto_id库要是承载个每秒几万并发,肯定是不现实的了。

这个每个人都应该知道吧,就是用UUID生成一个全局唯一的id。

好处就是每个系统本地生成,不要基于数据库来了。

不好之处就是,UUID太长了,作为主键性能太差了,不适合用于主键。 如果你是要随机生成个什么文件名了,编号之类的,你可以用UUID,但是作为主键是不能用UUID的。 这个方案的意思就是获取当前时间作为全局唯一的id。

但是问题是,并发很高的时候,比如一秒并发几千,会有重复的情况,这个肯定是不合适的。

一般如果用这个方案,是将当前时间跟很多其他的业务字段拼接起来,作为一个id,如果业务上你觉得可以接受,那么也是可以的。

你可以将别的业务字段值跟当前时间拼接起来,组成一个全局唯一的编号,比如说订单编号:时间戳+用户id+业务含义编码。

SnowFlake算法,是Twitter开源的分布式id生成算法。

其核心思想就是:使用一个64bit的long型的数字作为全局唯一id。 这64个bit中,其中1个bit是不用的,然后用其中的41bit作为毫秒数,用10bit作为工作机器id,12bit作为序列号。 给大家举个例子吧,比如下面那个64bit的long型数字:。