服务端开发过程中,我们通常需要与mysql数据库进行数据交互。在大多数情况下,由于数据量过大、网络时延、mysql参数配置限制,以及业务逻辑的限制等,需要我们对所需的数据进行分页读取。尤其是需要读取的数据量过大时,我们经常会遇到下面这种错误类型。
text
代码解读
复制代码
vttablet: rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (48340144 vs. 33554432)')
这个时候就需要我们利用limit和offset参数来实现数据的分页读取。例如,我们需要按照更新时间降序的顺序按照每页5000条数据的规则分页读取全部数据。读取第一页的SQL语句如下所示:
sql
代码解读
复制代码
SELECT * FROM table_name order by update_time limit 5000 offset 0
一般情况下该语句可以按照我们的意愿返回正确的结果,但是在某些情况下可能会出现不同页中存在相同数据的问题。这种特殊情况主要分为两种情况:
- 一种发生在同时对数据库进行读和写操作时。
- 另一种发生在当数据无法按照排序字段完成准确排序时。
接下来我们分别介绍原因以及解决方案。
1、同时读写操作导致数据重复
数据重复原因
例如我们需要按照需求分页获取10条数据,每页获取5条。当我们执行前5条数据的指令时
sql
代码解读
复制代码
SELECT * FROM table_name order by update_time limit 5 offset 0
数据库返回前五条记录1-5。如果此时恰好数据库执行插入数据库操作。新插入了一条新的数据。当读取操作获取第二页数据时
sql
代码解读
复制代码
SELECT * FROM table_name order by update_time limit 5 offset 0
由于新插入的数据的时间最靠前,排序后新插入的数据将排在第一条的位置,此时获取的第二页数据为第6-10条数据。此刻的第六数据就是第一页数据的第5条。因此我们期望获取前10条数据,最终只获得了9条,其中第5条数据重复。
解决方案
这种情况我们通常利用时间戳来保证我们多次获取的数据是同一个时间分片状态下的数据。具体如下
golang
代码解读
复制代码
now := time.Now()
pageNum := 50
for i:=0;i<2;i++{
sqlStr := fmt.sprintf("SELECT * FROM table_name where create_tiem < '%s' order by update_time limit %d offset %d",now,pageNum,i*pageNum)
db.Exec(sqlStr).Scan(&result)
}
2、无法准确排序导致数据重复
重复原因
首先我们明确一点,mysql排序规则如下:
(1)mysql查询不指定排序规则时,会默认按照ID进行排序。
(2)一旦指定排序值,则按照排序值进行排序,排序值相同的记录,顺序则是随机的。
如上所述,当我们执行如下sql时
sql
代码解读
复制代码
SELECT * FROM table_name order by update_time limit 50 offset 0
如果部分数据是批量插入或者修改,就会存在update_time 相同的数据,此时这些数据将随机排序,如果恰好我们的分页位置处在这些具有相同update_time的数据之间时,可能存在第一次查询某条数据在第一页,第二次查询时某条数据又出现在第二页。从而导致数据重复。
解决方法
这种情况的解决方法非常简单,要么不指定排序字段,按照自增id排序,要么保证指定的排序规则可以使数据实现绝对排序,即不存在随机顺序的可能。具体sql如下:
sql
代码解读
复制代码
SELECT * FROM table_name order by update_time desc, id desc limit 50 offset 0
以上就是当mysql分页读取数据时产生数据重复问题的两种常见原因分析以及解决方案。