分批提交数据_数据库常遇错误及解决思路

5deec240c786a5720ffe7c297650cd99.png

1.访问数据库超时

​ 判断故障是不是和访问量有关系 ->排查重点就应该放在服务用户访问的功能上,比如首页,商品列表页,内容推荐等功能

​ 如果系统只是在访问量峰值的时候,请求超时,但是随着访问量减少,系统能够自动恢复,可以排除后台服务被大量请求打死的可能性。

​ MySQL总CPU利用率高的现象,绝大多数情况是由慢SQL导致的,可以分析慢SQL日志,是查找类似问题原因最有效的方法 ->分析慢SQL日志的时候,首先要找到一个特别慢的SQL

​ 对于许多慢SQL,比如排行榜,一定要做缓存。

​ 如果CPU利用率,呈周期性,有规律的波动就要考虑定时任务了

作为开发,我们应该

​ 一、在编写SQL的时候,一定小心谨慎仔细评估

​ 1.1 、SQL涉及到的表,数据规模是多少

​ 1.2、SQL可能会遍历的数据量是多少

​ 1.3、尽量避免写出慢SQL

​ 二、能不能利用缓存减少数据库查询次数?在使用缓存的时候,特别需要注意的就是缓存命中率,要尽量避免请求命中不了缓存,穿透到数据库。

​ 建议:

​ 上线一个定时监控和杀掉慢SQL的脚本。这个脚本每分钟执行一次,检测一分钟内,又没有执行时间超过一分钟的慢SQL,如果有,直接杀掉

​ 做一个简单的静态页面的首页作为降级方案。请求超时的时候返回这个静态页面,暂时降级。

  1. 避免写出慢SQL

​ 一般一台MySQL服务器,平均每秒钟执行的SQL数量在几百左右,就已经算非常繁忙得了。

​ 在编写一条查询语句的时候,可以根据要查询数据表的数据总量,估算一下这条查询大致需要遍历多少行数据。

​ a、使用索引避免全表扫描

​ 虽然索引能够有效减少执行查询时遍历数据的行数,提高查询性能,但是代价就是会降低数据插入,删除和更新的性能。所以,对于更新频繁并且对于性能要求高的表,尽量少建索引。对于查询多的表,多建索引。

​ b、对于复杂的查询,最好分析SQL执行计划

​ 使用EXPLAIN关键字执行查询语句

  1. SQL是如何在数据库中执行的

​ 数据库的服务端,可以划分为执行器和存储引擎两部分。执行器负责解析SQL执行查询,存储引擎负责保存数据。

4.使用缓存保护MySQL

​ Redis可以作为MYSQL的前置缓存,可以替MySQL抵挡大部分查询请求,很大程度上环节MySQL并发请求的压力

​ a.更新缓存的最佳方式

​ Redis是一个使用内存保护数据的高性能的KV数据库,高性能主要在于

​ 1.简单的数据结构

​ 2.使用内存存储数据

​ 内存是一种易失性存储,所以牺牲了大部分功能,牺牲了数据可靠性,换取了高性能。

​ 虽然Redis支持将数据持久化到磁盘,并且支持主从复制,但是仍然是一个不可靠的存储,设计上就天然不保证数据的可靠性。所以我们使用Redis的时候,就要兼容Redis丢数据的情况,做到即使Redis发生了丢数据,也不影响数据准确性。

​ 可以使用Cache Aside模式更新缓存,可以非常有效避免并发读写导致的脏数据问题。

5.读写分离

​ 读写分离是提升MySQL并发的首选方案

​ 读写分离中数据库应对的绝大部分请求都是只读查询请求,并且实施起来简单,把使用单机MySQL的系统升级为读写分离的多实例,只需要简单修改DAO代码,把对数据库的读写请求分开,请求不同的MYSQL实例就可以了

​ 怎么实施MYSQL的读写分离方案

​ 1.部署一主多从多个MYSQL实例,并让他们之间保持数据实时同步

​ 2.分离应用程序对数据库的读写请求,分别发送到从库和主库

分离应用程序的读写请求方法有下面这三种(查看官网分档):

​ 纯手工方式:修改应用程序的 DAO 层代码,定义读写两个数据源,指定每一个数据库请求的数据源。

​ 组件方式:也可以使用像 Sharding-JDBC 这种集成在应用中的第三方组件来实现,这些组件集成在你的应用程序内,代理应用程序的所有数据库请求,自动把请求路由到对应数据库实例上。

​ 代理方式:在应用程序和数据库实例之间部署一组数据库代理实例,比如说 Atlas 或者 MaxScale。对应用程序来说,数据库代理把自己伪装成一个单节点的 MySQL 实例,应用程序的所有数据库请求被发送给代理,代理分离读写请求,然后转发给对应的数据库实例。

​ 读写分离的副作用:

​ 可能存在数据不一致的情况

​ 主从同步延迟会导致主库和从库之间出现数据不一致的情况,我们的应用程序应该能兼容主从延迟,避免因为主从延迟而导致的数据错误。规避这个问题最关键的一点是,我们在设计系统的业务流程时,尽量不要在更新数据之后立即去查询更新后的数据。

6.MySQL主从数据库同步

​ 当客户端提交一个事务到 MySQL 的集群,直到客户端收到集群返回成功响应,在这个过程中,MySQL 集群需要执行很多操作:主库需要提交事务、更新存储引擎中的数据、把 Binlog 写到磁盘上、给客户端返回响应、把 Binlog 复制到所有从库上、每个从库需要把复制过来的 Binlog 写到暂存日志中、回放这个 Binlog、更新存储引擎中的数据、给主库返回复制成功的响应。

​ 性能最好的方法是异步复制,主节点上先记录操作日志,再更新状态数据,然后异步把操作日志复制到所有从节点上,并在从节点执行操作日志,得到和主节点相同的状态数据。

​ 异步复制的劣势是,可能存在主从延迟,如果主节点宕机,可能会丢数据。另外一种常用的策略是半同步复制,主节点等待操作日志最少成功复制到 N 个从节点上之后,再更新状态,这种方式在性能、高可用和数据可靠性几个方面都比较平衡,很多分布式存储系统默认采用的都是这种方式。

7.订单数据变多,数据库变慢怎么办

​ 查询耗费的时间主要在两个因素

​ 1.查找的时间复杂度

​ 2.数据总量

​ a.存档历史记录数据提高查询性能

​ 当单表的订单数据太多,多到影响性能的时候,首选方案是,归档历史订单(就是把大量的历史订单移到另外一张历史订单表中)

​ 大致流程

![image-20200802104900478](/Users/zhaoyu/Library/Application Support/typora-user-images/image-20200802104900478.png)

​ 1.首先创建一个和订单表结构一模一样的历史订单表

​ 2.把订单表中的历史数据分批查出来,插入到历史订单表中

​ 3.测试和上线支持历史订单表的新版本代码,查看是否有BUG,看是否需要回滚

​ 4.等新版本代码上线并验证无误之后,就可以删除订单表中的历史订单数据了

​ 5.上线一个定期迁移数据的程序或者脚本,定期吧过期订单从订单表搬到历史订单表中(迁移之前一定做好备份)

​ b.如何批量删除大量数据(一定要做好备份)

select max(id) from orders
    where timestamp < SUBDATE(CURDATE(),INTERVAL 3 month);

delete from orders
where id <= ?
order by id limit 1000;

​ 只需要先通过一次查询,找到符合条件的历史订单中最大的那个订单ID,然后在删除语句中吧删除条件换成主键删除,就不同每次都是时间戳上找符合条件的记录

​ 加order by主要就是为了B+ 树的数据结构,这样删除更加快一点

​ 对于平凡插入删除大量数据的这种表,如果能接受锁表,定期执行OPTIMIZE TABLE是非常有必要的。

8.分库分表

​ 如何规划分库分表:能不拆就不拆,能少拆就不多拆

​ 分库分表的目的主要解决两个问题

​ 1.数据量太大查询慢的问题。解决查询慢,只要减少每次查询的数据总量就可以了,也就是,分表就可以解决问题

​ 2.应对高并发的问题,一个数据库实例撑不住,就把并发请求分散到多个实例中去,所以,解决高炳达的问题需要分库

概括就是数据量大就分表,高并发就分库

​ 如何选择 sharding key? ​ 这是分表的依据,选择要考虑的因素取决于业务如何访问数据,让查询尽量落到一个分片中。如果分片无法兼容查询,可以把数据同步到其他存储中,供查询

​ 常见的分片算法 ​ 1.范围分片 ​ 对查询友好,适合并发量不大的场景。但容易产生热点数据 ​ 2.哈希分片 ​ 比较均匀 ​ 3.查表法 ​ 灵活,性能差因为多了一次查表

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值