最近学到对于高并发的优化技术,所以写了一篇笔记记录一下。
数据库优化可以从下边几个方面入手:
-
池化
-
主从分离
-
分库分表
-
发号器
-
NoSQL
-
池化
数据库连接池是最常见的池化手段。由于连接数据库会耗费比较多的时间,所以可以通过复用连接的方式来减少这些时间开销。使用连接池的时候需要控制好两个变量:最小连接数喝最大连接数。具体的连接规则类似下边的步骤:
-
使用连接的时候先查询数据库连接的数量。如果连接数小于最小连接数,就创建新连接;
-
如果大于最小连接数,则查询是否有空闲连接,如果有,就直接使用空闲连接;
-
如果没有空闲连接,则创建新的连接;
-
如果没有空闲连接,而连接数已经大于等于最大连接数,就根据配置好的等待时间进行等待;
-
如果等待的时间已经超过配置时间,就抛出异常。
同样的思想还可以类比jdk中的线程池。jdk在1.5版本中增加了线程管理的技术来优化效率,但是原生的线程池并不太适合web应用,主要的原因是这样的线程池主要适合的是CPU密集型的运算,而大部分的web应用都是IO密集型的项目,在每一次使用的时候,耗时比较大的一部分都是查询数据库的逻辑。实际处理业务逻辑的代码并不会占用太多的时间。
2.数据库的主从分离
做主从分离的主要的原因是数据库读写的次数不一致,这里边的差别可能是好几个量级。如果分开用两个服务器,一个处理写入,一个处理读取的请求,这样会更加有效率地利用资源。
主从分离最简单的实现是主从复制。
用MySQL作为例子来说吧,MySQL在更新数据库的时候会写binlog,再在合适的时候真正地去更改数据。从服务器只需要同步这个binlog就可以保持数据一致。
这样做虽然是简单,但是也有一些缺点:
首先是要保持读写数据的一致,如果主从不能保证一致,那么就没有分离数据库的意义。可以利用的方法有:在一些数据中增加冗余数据,例如:在新建任务的时候,已经提前把消息放到消息队列里边,在消息队列真正操作的时候,还需要从从库里边查询对应的任务信息,就有可能导致数据不一致(因为不能保证从库的100% 准确,虽然这种机率很低)。可以直接把数据缓存到消息队列里边,这样可以保证数据的准确,又减少查询的次数。
还有一个缺点是主从同步的延迟。对于一些时效性要求比较高的场景,可能不太适合这样的主从分离的方式,同步两个数据库总是需要时间。
具体实现主从分离的时候,可以利用一些框架,例如:MyCat、DBProxy等工具管理IP,不过查询数据库的时候都会经过代理,一个是耗时更长,一个是会增加运维的复杂性。
3.分库分表
主从分离已经能解决一定的数据增长问题了,但是随着数据的进一步增长,那么还可以利用分库分表的形式来进一步扩展数据库的容量。
做分库分表的一个原则时尽可能地把数据平均分到不同的节点当中,切分的数据主要有两种方式:垂直切分、水平切分。
垂直切分是指专库专用,也就是一个订单的系统,里边只会有订单相关的库。其他业务上不相关的库不会出现在订单系统里边。
水平分切是指按照一定的规则将数据库拆分,一般使用的规则有:按照hash指或者是按照时间 。
分库分表会增加数据查询时候䣌复杂度——查询的时候需要先确定数据所在的表是那个库中,并且如果是一些复杂的操作,例如join、count等操作需要自己手动实现(不是一个库,不能用sql来进行连接。)
由于有这些不太便利的问题,所以能不分库就尽量不分库,如果要做,那么尽量一步到位。
4.发号器
当我们把数据库通过主从分离、分库分表之后形成 了一个复杂的系统,那么在写入数据需要面临一个问题:怎么保证数据插入之后的ID是唯一的?
那么要怎样生成全局的唯一ID呢?UUID是我第一个想到的方法,但是这个却不是一种好的选择。
首先UUID并没有实际的意义,同时也不能用作排序的索引。以InnoDB为例,由于InnoDB使用的是B+树作为索引,索引是有序的。在索引中间再插入值,无疑会导致一部分的索引需要重新排列。也就会导致额外的性能开销。所以久有人对其进行了改进,这就是SnowFlake的来由。
SnowFalke的核心思想是把64bit的二进制数字分成若干个部分,并且每一个部分都有其自己的意义:
第一部分是第一位,恒定是0;
第二部分是41位的时间戳,相当与69年;
第三部分是10位的机器码,如果由多个机房,这个地方是可以继续进行划分的,最多支持每台机器每一毫秒生成4096个ID ;
第四部分是剩下的部分,是序列号。
上边的逻辑可以直接写道代码里边,也可以独立部署为一个应用,每次生成的时候通过网络调用。
这里有一些需要注意的地方:
-
时间戳建议记录的是秒,这样可以区分出时间区间里发出多少个信号。其实就是实际上请求分布的不均匀,如果利用的是比秒小的单位,很可能导致数据在某些地方聚集;
-
生成序列号的时候可以做一下随机数,目的也是为了平衡数据的分布。
5.NoSQL
意思是:No only SQL,不是No SQL。本来是用作传统关系型数据库的取代品,但是现在作为数据库的互补也是一个很好的选择。
NoSQL具有更好的性能,而且变更容易,更加适合大数据量的场景。当上边的数据库优化技术还是不能满足需求的时候,尝试用NoSQL来继续优化。大部分的NoSQL组件都是利用LSM树,简单说一下其原理:
首先数会被优先写入MemTable中(一般会用“Write Ahead log”的方式备份到磁盘上),当MemTable积累到一定程度之后,会被刷新到一个新的文件中,这个文件称为:SSTable。SSTable积累到一定层度,会进行自动合并。
查询的时候会先找MemTable,然后再找SSTable,由于涉及到问价,所以这样查找肯定效率比较慢。
倒排索引是另外一个NoSQL组件的很好用的功能。倒排索引指的是,利用关键字找到索引的功能,利用这个特点ElasticSearch可以提供全文的分布式搜索服务。
同样强大的还有MongoDB的扩展性,利用Replica副本集和Shard等分片技术,可以自动扩展数据,并且做到负载均衡。