对于数据库的使用,我们不仅仅要考虑 MySQL 本身的优化,还需要考虑应用层的优化。因为有些线上问题,就是由于应用层设置不合理导致的。
本节就来聊聊几种应用层的优化方法。
1 使用连接池
MySQL 如果频繁创建和断开连接,那 MySQL 的开销会比较大,可能会占用过多的服务器内存资源,甚至导致响应时间变慢。此时就可以考虑使用连接池来改进性能。
连接池可以理解为:创建一些持久连接的“池”,新的请求可以使用这些连接池,减少创建和断开连接的次数。
其大致原理是:
- 当进程启动时,创建相应的数据库连接池对象;
- 如果程序需要请求数据库,则直接从连接池获取到一个连接;
- 数据库请求完成后,释放数据库连接池。
那么连接池会不会导致服务器连接过多呢?
通常情况下,连接池不会导致服务器连接过多,因为它们会在进程间排队和共享连接。
在《高性能 MySQL》第 14 章:应用层优化中就提到:当遇到连接池完全占满时,应该将连接请求进行排队,而不是扩展连接池。这样可以避免将压力都转到 MySQL 上而导致 MySQL 连接数过多。
2 减少对 MySQL 的访问
避免对同一行数据做重复检索,比如查询某个用户信息。
首先查出这个用户的联系方式:
select phone from user_info where user_id=111;
然后再查出这个用户的姓名:
select name from user_info where user_id=111;
显然上面的方式并不是最优的,可以将两条 SQL 合并成一条:
select phone,name from user_info where user_id=111;
然后返回给客户端。这样跟数据库建立连接的次数从 2 次降低到 1 次,从而节省了部分建立连接所花费的内存和时间。
3 增加 Redis 缓存层
在很多业务场景,Redis 充当着不可或缺的角色。这里介绍几种通过 Redis 环境 MySQL 压力的场景:
3.1 计数器
在专栏第 9 节,就提到了使用 Redis 做计数器的场景。当统计数增加,则在 Redis 中执行下面的命令让计数器加 1:
INCR t1_count
通过这种方式缓解在 MySQL 中执行 update 的压力。
3.2 K-V 数据缓存
在 MySQL 中,如果某个字段会被频繁查询,而该字段内容变化的概率又不是很大,就可以考虑使用 Redis 缓存。比如电商业务,查看上个季度某类型商品的销量排行,如果这一个功能放在主页,点击量可能会非常高,因此可以考虑放在 Redis 中。
3.3 消息队列
Redis 中可以非常方便的使用消息队列。
生产者通过 lpush 将消息放在 list 中,消费者通过 rpop 取出该消息。
我曾经工作的一家公司就使用 Redis 实现短信消息队列。如果用户在 APP 上点击注册用户名密码,需要填下手机号验证,程序会将该用户的手机号放在 Redis 的 list 中,然后另外一个程序一直去消费 list 中的手机号,取出手机后,则调用第三方短信接口,发送手机短信息给到用户。
4 单表过大及时归档
比如单张表过大,可能有下面这些影响:
- 在修改表结构时导致长时间主从延迟;
- 备份时间过久;
- 查询速度可能也会变慢。
因此,可以考虑对历史数据归档(比如日志数据),控制单表的数据量。
5 代码层读写分离
在配置了 MySQL 主从环境的情况下,可以考虑使用读写分离,通过程序配置的这种方式,在专栏第 27 节有提到过。更新走主库,查询走从库。
当然,主从同步可能因为大事务或者网络等原因导致同步延迟,在使用读写分离是也需要考虑到延迟这一点。
6 表的索引提前规划
在专栏第 10 节中,我们讲到了为什么添加索引能提高查询速度?因此,条件字段有索引显得格外重要。
当开发或者 DBA 在创建新表时,就应该考虑在表的条件字段添加合适索引。这样可以避免业务上线后,数据量一上来就出现大量慢查询而导致 MySQL 服务器高负载。
7 总结
本节讲解了使用 MySQL 时,应用层的一些优化。
提到了以下几点优化方案:
1、使用连接池;
2、减少对 MySQL 的访问;
3、增加 Redis 缓存层;
4、单表过大及时归档;
5、代码层读写分离;
6、表的索引提前规划;
······
实际工作中也是,不单单 DBA 要去优化 MySQL,开发也应该考虑在应用层去做一些优化,以保证业务稳定高效。
8 问题
你觉得连接 MySQL 的应用层还有哪些情况是可以去优化改善的?欢迎在留言区讨论。
9 参考资料
《深入浅出 MySQL》第 23 章:应用优化
《高性能 MySQL》第 14 章:应用层优化