使用MySQL Server Side Cursor解决查询数据量过大造成OOM

96
阿里加多
2017.07.30 20:45* 字数 692

一、前言

前面介绍了MyBaits中两种使用游标的方式来避免搜查内容过大导致OOM,这两种方式被称为是客户端side的游标,因为mysql client每次从自己的接受buffer获取一条记录,这虽然解决了OOM,但是会造成sqlserver服务器推送记录到client的时候阻塞,那么有没有一种方式可以让mysql server减少阻塞那,答案是肯定的。

阿里巴巴长期招聘Java研发工程师p6,p7,p8等上不封顶级别,有意向的可以发简历给我,注明想去的部门和工作地点:1064454834@qq.com

二、MySQL Server Side Cursor

2.1 使用

要使用MySQL Server Side游标需要满足下面条件:

  • 必须是select语句

  • 设置了fetchSize>0
    在mapper文件里面设置

  • 设置了useCursorFetch=true

  • 数据集类型为ResultSet.TYPE_FORWARD_ONLY

  • 数据集并发设置为ResultSet.CONCUR_READ_ONLY
    在数据库链接后面设置:jdbc:mysql://localhost:3306/test?useCursorFetch=true"

  • Server versions 5.0.5 or newer

这是因为代码层面做了下面判断:

// we only create cursor-backed result sets if
// a) The query is a SELECT
// b) The server supports it
// c) We know it is forward-only (note this doesn't preclude updatable result sets)
// d) The user has set a fetch size
if (this.resultFields != null && this.useCursorFetch && getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
        && getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY && getFetchSize() > 0) {
    packet.writeByte(OPEN_CURSOR_FLAG);
    //                  usingCursor = true;
} else {
    packet.writeByte((byte) 0); // placeholder for flags
}

2.2 原理简单介绍

先说下发送原理,client发送select请求给Server后,Server根据条件筛选符合条件的记录,然后就会把记录发送到自己的发送buffer,等buffer满了就flush缓存(这里要注意的是如果client的接受缓存满了,那么Server的发送就会阻塞主,直到client的接受缓存空闲。),通过网络发送到client的接受缓存,当不用游标时候MySqIo就会从接受缓存里面逐个读取记录到resultset。就这样client 从自己的接受缓存读取数据到resultset,同时Server端不断通过网络向client接受缓存发送数据,直到所有记录都放到了resultset。

服务器边的游标则是mysqlclient一次从自己的接受缓存读取fetchSize个记录(如果buffer不够fetchSize也没关系,因为Server一直在向这个buffer 刷新数据)。mysqlclient获取fetchSize个记录放到mysqlclient的游标内部的数组里面,游标获取的时候是从数组里面获取数据,如果数组为空了,在向buffer获取fetchSize个记录。

三、总结对比

服务器边的游标的使用的确可以减少server端阻塞,这是因为client一次从接受缓存读取fetchsize个记录,所以大概率情况下给Server写入腾出了空间。

想获取更多阿里ATA干货,请关注微信公众号:‘技术原始积累’

image.png
othrer