存储系统设计
什么是池化技术?
在一些项目中,会提前分配一块大的内存空间作为数据缓冲区,用来优化内存分配,这就是一种“内存池”化技术。还可以用数据库连接池来优化数据库的连接,这就是
连接池
技术。池化技术是预先分配一些资源并循环利用,以达到池化的目的。
不用连接池会出现哪些问题?
单连接无法支撑高并发
每次请求都需要建立和关闭连接,会增加请求延迟
如果在高并发场景下频繁地建立和关闭连接,则会导致操作系统耗费 过多 CPU资源。
连接池原理
连接池是指,预先分配一批连接,并将他们放入一个缓冲区中循环使用,形成池化效应。数据库连接池有3个重要地配置:最小连接数、空闲连接数和最大连接数
如果当前连接数小于最小连接数,则创建新地连接来处理数据库地请求
如果连接池中有空闲连接,则复用空闲连接。如果没有空闲连接,且当前连接数小于最大连接数,则创建新地连接
如果当前数连接数大于最大连接数,则根据配置中设定地时间等待旧地 连接可用
如果等待时间超过了设定地时间,则向用户抛出错误。
如何维护数据库连接池中的连接?导致连接异常的原因有哪些?
数据库域名或者IP地址发生了变更。
MYSQL数据库会主动断开一定时间内没活跃的连接,这个过程无感知。
解决方案:
- 启用一个线程池,定期检测数据库连接池中的连接是否可用。如C3P0连接池就可以使用这种方法放出 select 1进行检测
- 在获取连接后,先验证是否可用,可用则执行SQL语句。
线程池是如何工作的?
线程池有6个参数
- corePoolSize 核心线程数 即常驻线程的数量
- maxPoolSize 最大线程数
- keepAliveTime 空闲线程的存活时间
- ThreadFactory 线程工厂用于创建新线程
- workQueue 用于存放任务的队列
- Handler 处理被拒绝的任务
- 在提交任务后,如果线程池数小于核心线程数,则创建新的线程执行任务
- 随着任务的不断增加,如果线程池中的线程数大于核心线程数,则新提交的任务会被放入任务队列中,等待核心线程空闲后再执行队列中的任务
- 当任务队列WorkQueue中的任务堆满时,线程池会参照最大线程数继续创建线程来执行任务。
- 当线程数达到最大线程数,如果还有新的任务提交,则拒绝这些任务。
使用线程池的好处
- 线程池不仅可以解决线程生命周期的开销问题,还可以加快响应速度
- 线程池 可以统筹内存 和CPU使用,避免资源使用不当
- 线程池可以统一管理资源。
协程池有什么作用
在高并发场景下,线程的创建和销毁都会给CPU和内存带来的一些性能开销,所以协程出现了。协程在Go语言当中,一个协程的初始内存时2KB。
- 协程的创建和销毁完全是在用户态下执行的,不涉及用户态和内核态的切换。
- 协程完全由应用程序在用户态下调用,不涉及内核态的上下文切换。
- 在切换协程时不需要处理线程状态,所以需要保存的上下文也很少,速度也很快。
协程是如何实现的?
- 抢占式 在这种实现方式中,所有任务被存放在一个共享的channel中,多个协程同时消费channel中的任务,谁先拿到谁先执行。
- 调度式 在调度式协程池中,每个协程都有自己的channel,每个协程只消费自己的channel。
数据库采用主从架构——数据再也不会丢了
- 什么是数据库的主从架构?
- 主从复制 保证数据安全,最简单且最有效的方式是对数据进行定期备份。
- Master将数据的变更记录到binlog文件中
- 主库Master中的Binlog dump thread在收到写请求后 读取Binlog文件,然后将读取的数据推送给从库Slave中的I/O thread。
- 从库Slave中的I/O thread将读取的Binlog 写入本地的Relaylog日志文件中
- 从库Slave中的SQL thread 检测到Relaylog的变更请求,读取Relaylog文件在从库Slave中的回放,以实现主从数据库的一致性。
- 主从延迟 对于 主从复制,基于性能的考虑,主库的写入流程并不是等待从库完成同步后才给客户端返回结果,而是采用异步的方式直接返回结果。
- 主从架构中的数据是如何实现同步的?、
- 主库提交事务、更新存储引擎中的数据
- 主库将binlog文件写入磁盘
- 主库给客户端返回响应结果
- Binlog文件被复制到所有的从库中
- 每个从库将复制过来的Binlog文件写入中继日志
- 从库回放Binlog文件、更新存储引擎中的数据,然后给主库返回复制成功的结果。
- 数据库读写分离能解决什么问题?
- 提升数据库并发能力,将读流量分摊到各个从库中,且从库可以横向扩展
- 保证数据库的可用性
- 数据库读写分离造成数据不一致,该怎么办?
- 根据业务的特点来设计业务场景、尽量规避 无完美解决方案。
数据库分库分表——处理海量数据的终极 大招
- 在什么情况下需要分库分表?
在Mysql数据库无法承受海量数据压力时,可以采用分布式思想对数据库进行拆分
分库分表的思考?
- 如果单张表数据达到了千万条或者亿条,那此时对数据库本身进行优化基本不会提升多少性能
- 数据量的飞速增长,使得磁盘空间特别紧张,此时数据的备份和恢复耗时增加不少
- 对于部分核心模块的数据,可以将其单独存放在一个核心库中,一旦该库出现异常,那该如何隔离其他核心模块以使其不受影响?
- 必须思考如何突破单台数据库的写请求瓶颈?
所以最有效的解决办法是分片——对数据进行分片存储。
如何分库分表?
- 分库分表的核心思想是 尽可能将数据平均分散到多个 数据库节点或者多张表中。分库分表解决3个关键问题
- 提升数据的查询性能,因为所有节点都只存储了一部分数据
- 突破单机的存储瓶颈
- 提升并发的写性能,因为数据被分摊到多个数据节点上了,所以数据的写请求 从 单一库的写入 变为多个分片的写入
(1)垂直拆分 将一个数据库中的表拆分到多个数据中,或者一张表拆分成多张表,通常按照业务模型来进行垂直拆分。
(2)水平拆分 是指将单张数据表的数据按照某种算法规则拆分到多个数据库和数据库表中。