暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

fetchone和fetchall对MySQL来说有区别吗?

mysql code tracer 2021-04-25
2015

导读:

今天开发问了我一个问题,大概问题是php程序执行了一条SQL,然后fetchone去取跟fetchall去取有什么区别吗?

  • 首先,他认为mysql执行一条SQL,并且把结果都存储在了MySQL的内存某个区域中了。

  • 其次,他认为去fetch的时候是从MySQL这个内存区域中去获取数据,无非就是fetchone是单条单条去获取,fetchall是将数据全部过去到。

实验:

这个问题我之前也疑惑过,因为之前也写了很多的python脚本用来获取MySQL数据,但是基本上都是按套路写下来,也没有去深究这个数据获取是怎么操作的。那带着这个问题,我首先看了pymysql里面的cursor类,关于execute、fetchone和fetchall定义如下:

    #调用self._query方法进行查询
    def execute(self, query, args=None):
    """Execute a query
    :param str query: Query to execute.
    :param args: parameters used with query. (optional)
    :type args: tuple, list or dict
    :return: Number of affected rows
    :rtype: int
    If args is a list or tuple, %s can be used as a placeholder in the query.
    If args is a dict, %(name)s can be used as a placeholder in the query.
    """
    while self.nextset():
    pass


    query = self.mogrify(query, args)


    result = self._query(query)
    self._executed = query
    return result


    #调用_do_get_result获取查询结果
    def _query(self, q):
    conn = self._get_db()
    self._last_executed = q
    self._clear_result()
    conn.query(q)
    self._do_get_result()
    return self.rowcount


    #这里获取结果
    def _do_get_result(self):
    conn = self._get_db()


    self._result = result = conn._result


    self.rowcount = result.affected_rows
    self.description = result.description
    self.lastrowid = result.insert_id
    self._rows = result.rows
    self._warnings_handled = False


    if not self._defer_warnings:
    self._show_warnings()

    其实看到这里代码逻辑已经很清楚了,在调用cursor.execute执行SQL的时候,就将MySQL查询的结果放到result这个变量里了,也就是说结果集放到了客户端的内存变量里,那么获取数据的方式也就是从这个内存变量里去获取数据,只是获取的行为有所不同而已了。

      def fetchone(self):
      """Fetch the next row"""
      self._check_executed()
      if self._rows is None or self.rownumber >= len(self._rows):
      return None
      result = self._rows[self.rownumber]
      self.rownumber += 1
      return result


      def fetchmany(self, size=None):
      """Fetch several rows"""
      self._check_executed()
      if self._rows is None:
      return ()
      end = self.rownumber + (size or self.arraysize)
      result = self._rows[self.rownumber:end]
      self.rownumber = min(end, len(self._rows))
      return result


      def fetchall(self):
      """Fetch all the rows"""
      self._check_executed()
      if self._rows is None:
      return ()
      if self.rownumber:
      result = self._rows[self.rownumber:]
      else:
      result = self._rows
      self.rownumber = len(self._rows)
      return result

      口说无凭,我们直接通过Wireshark抓包来证明一下,首先我在本地执行脚本如下,然后我监听本地的网卡流量

        import pymysql


        conn = pymysql.connect(host='xxx', port=3306,
        user='xucl', password='xuclxucl', database='xucl')
        cursor = conn.cursor()


        cursor.execute("select * from t")

        注意,我这里并没有执行fetch操作,如果监听到的包里面包含了结果,那么就证明我们前面的分析是正确的,话不多说开始实验,Wireshark抓包如下:

        果然跟我们之前的预测是一致的,即使没有进行fetch操作,MySQL也将结果集发送到客户端了。另外关于结果集发送,可以参考我另外一篇文章:《由一个抓瞎的SQL引申出去》

        结论:

        • 客户端执行SQL的时候,MySQL一次性将结果集发送给了客户端

        • 客户端接受到结果集以后存储本地内存变量中

        • fetch结果只是从这个本地变量中获取,fetchone/fetchmany/fetchall只是获取行为的不通,因此对于MySQL来说并没有什么不通


        文章转载自mysql code tracer,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论