【问题标题】:Lost connection to MySQL server during query查询期间失去与 MySQL 服务器的连接
【发布时间】:2010-12-25 11:39:49
【问题描述】:

我有一个巨大的表,我需要处理其中的所有行。我总是收到这个丢失的连接消息,我无法重新连接并将光标恢复到它的最后一个位置。这基本上是我在这里的代码:

#
import MySQLdb

class DB:
  conn = None

  def connect(self):
    self.conn = MySQLdb.connect('hostname', 'user', '*****', 'some_table', cursorclass=MySQLdb.cursors.SSCursor)

  def query(self, sql):
    try:
     cursor = self.conn.cursor()
     cursor.execute(sql)
   except (AttributeError, MySQLdb.OperationalError):
     self.connect()
     cursor = self.conn.cursor()
     cursor.execute(sql)
   return cursor
#

#
db = DB()
sql = "SELECT bla FROM foo"
data = db.query(sql)

for row in data:
    do_something(row)
#

但我总是得到这个:

#
Traceback (most recent call last):
  File "teste.py", line 124, in <module>
   run()
 File "teste.py", line 109, in run
   for row in data:
 File "/usr/lib64/python2.5/site-packages/MySQLdb/cursors.py", line 417, in next
   row = self.fetchone()
 File "/usr/lib64/python2.5/site-packages/MySQLdb/cursors.py", line 388, in fetchone
   r = self._fetch_row(1)
 File "/usr/lib64/python2.5/site-packages/MySQLdb/cursors.py", line 285, in _fetch_row
   return self._result.fetch_row(size, self._fetch_type)
   _mysql_exceptions.OperationalError: (2013, 'Lost connection to MySQL server during query')
    Exception _mysql_exceptions.OperationalError: (2013, 'Lost connection to MySQL server during query') in <bound method SSCursor.__del__ of <MySQLdb.cursors.SSCursor object at 0x7f7e3c8da410>> ignored
#

你有什么想法吗?

【问题讨论】:

  • 从 connect() 调用中删除“cursorclass=MySQLdb.cursors.SSCursor”就足够了。它现在工作得很好。谢谢。
  • 我遇到了同样的问题,但是我有大约 1B 行数据,所以我想使用 SSCursor 在 mysqld 端而不是我的 python 应用程序上缓存查询的数据。将 net_write_timeout 扩大到 1 小时解决了这个问题 :)
  • 致从 Google 来到这里的人们:如果您使用多线程,则需要为每个线程提供自己的连接。

标签: python mysql


【解决方案1】:

mysql 文档有一整页专门针对此错误: http://dev.mysql.com/doc/refman/5.0/en/gone-away.html

值得注意的是

  • 如果您向服务器发送不正确或太大的查询,也可能会出现这些错误。如果 mysqld 收到一个太大或乱序的数据包,它会假设客户端出现问题并关闭连接。如果您需要大查询(例如,如果您使用大 BLOB 列),您可以通过设置服务器的 max_allowed_pa​​cket 变量来增加查询限制,该变量的默认值为 1MB。您可能还需要增加客户端的最大数据包大小。有关设置数据包大小的更多信息,请参见第 B.5.2.10 节“数据包太大”。

  • 您可以通过使用 --log-warnings=2 选项启动 mysqld 来获取有关丢失连接的更多信息。这会在 hostname.err 文件中记录一些断开连接的错误

【讨论】:

  • 另一个原因可能是mysqld崩溃了。
【解决方案2】:

mysql服务器的max_allowed_pa​​cket有三种放大方式:

  1. 在mysql服务器机器上修改/etc/mysql/my.cnf文件中的max_allowed_packet=64M并重启服务器
  2. 在mysql服务器上执行sql:set global max_allowed_packet=67108864;
  3. python连接mysql后执行sql:
connection.execute('set max_allowed_packet=67108864')

【讨论】:

    【解决方案3】:

    对于派生子进程的应用程序,您也可能会遇到此错误,所有这些子进程都尝试使用与 MySQL 服务器的相同连接。这可以通过为每个子进程使用单独的连接来避免。

    叉子可能会撞到你。不过在这种情况下请注意。

    【讨论】:

    • 是的,这对我来说是个问题。我正在使用 uwsgi 为我的烧瓶应用程序提供服务,并且在 uwsi 配置文件中,我将 processes 指令设置为 5。我将其更改为 1 并且它有效,但我不明白这个问题。每个进程不应该生成自己的连接吗?
    • @Rockstar5645 这个问题是谁解决的?
    • @vishal 我不记得了,但看起来我将进程指令从 5 更改为 1。
    【解决方案4】:

    确保在连接前关闭光标。我已经解决了我的问题:

    if cur and con:                        
        cur.close() 
        con.close() 
    

    【讨论】:

    • 如果在连接之前关闭游标和连接,是不是一开始就无法连接?
    【解决方案5】:

    您需要增加连接的超时时间。如果由于某种原因您不能或不想这样做,您可以尝试致电:

    data = db.query(sql).store_result()
    

    这将立即获取所有结果,然后您的连接不会在遍历它们的中途超时。

    【讨论】:

    【解决方案6】:

    我的情况是什么原因

    ERROR 2013 (HY000): 查询期间丢失与 MySQL 服务器的连接

    错误是我的表的某些部分损坏了。我也无法mysqldump 我的表,因为有些行破坏了它。 该错误与上述任何内存问题等无关。

    好消息是 MySQL 向我返回了第一个失败的行号。有点像

    mysqldump:错误 2013:在查询期间在第 12723 行转储表 mytable 时丢失与 MySQL 服务器的连接

    解决方案是将数据复制到新表中。就我而言,我丢失了 10 行数据,因为我不得不跳过这些损坏的行。首先,我使用旧表的架构创建了一个“tmp”表。 SHOW CREATE TABLE 是你的朋友。例如

    SHOW CREATE TABLE mydatabase.mytable;
    

    我创建了新表。我们称之为mytabletmp。然后复制您可以通过例如复制的行

    insert into mysqltabletmp select * from mytable where id < 12723;
    insert into mysqltabletmp select * from mytable where id > 12733;
    

    删除旧表后,将 tmp-table 重命名为旧表名。

    也有some nice Information from Peter关于这个问题。

    【讨论】:

      【解决方案7】:

      这发生在我的 mariadb 上,因为我创建了一个 varchar(255) 列和一个 unique key.. 猜想这对于一个独特的来说太重了,因为插入超时。

      【讨论】:

        【解决方案8】:

        多处理和 Django DB 不能很好地结合使用。

        我最终在新进程中关闭了 Django DB 连接。

        这样就不会引用父级使用的连接了。

        from multiprocessing import Pool
        
        multi_core_arg = [[1,2,3], [4,5,6], [7,8,9]]
        n_cpu = 4
        pool = Pool(n_cpu)
        pool.map(_etl_, multi_core_arg)
        pool.close()
        pool.join()
        
        def _etl_(x):
            from django.db import connection 
            connection.close() 
            print(x)
        

        Process.start() 调用一个以

        开头的函数

        其他一些建议使用

        from multiprocessing.dummy import Pool as ThreadPool
        

        它解决了我的(2013,丢失连接)问题,但是线程使用 GIL,在做 IO 时,会在 IO 完成时释放它。

        相比之下,Process 会产生一组相互通信的工作人员,这可能会更慢。

        我建议你计时。 一个小技巧是使用由 scikit-learn 项目支持的joblib。 一些性能结果表明它执行了本机 Pool().. 尽管它让编码人员负责验证真正的运行时间成本。

        【讨论】:

        • > 多处理和 Django DB 不能很好地配合使用。今天也是这样,真是令人沮丧。 (2013, 'Lost connection to MySQL server during query') (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken Pipe'))") (ಥ﹏ಥ)
        【解决方案9】:

        将“max_allowed_pa​​cket”设置为 64M 并重新启动您的 MySql 服务器。如果这不能解决您的问题,则问题可能出在其他地方。

        我有一个执行同时查询的多线程 PHP CLI 应用程序,我最近注意到了这个问题。现在对我来说很明显,MySql 服务器将来自同一 IP 的所有连接视为“单个”连接,因此只要单个查询完成,就会丢弃所有连接。

        我想知道有没有办法让 MySql 允许来自同一个 IP 的 100 个连接,并将每个连接视为一个单独的连接。

        【讨论】:

        • 我不认为您关于“来自同一 IP 的所有连接都与 'sing'e' 连接”的说法是正确的。您可能会看到,因为 mysql 方法可能正在重用您的 PHP“线程”之间的持久连接。
        • "现在对我来说很明显,MySql 服务器将来自同一 IP 的所有连接视为“单个”连接" => 这当然是完全错误的,可以通过启动十并行 mysql 客户端进程在同一个机器上同时监控 mysql 连接 - 每个客户端当然都有自己的连接。
        【解决方案10】:

        如果有人或某事使用KILL command 终止您的连接,也会发生这种情况。

        【讨论】:

          【解决方案11】:

          当我尝试更新磁盘上的大小大于可用磁盘空间的表时,这发生在我身上。我的解决方案就是增加可用磁盘空间。

          【讨论】:

            【解决方案12】:

            在我的例子中,我在采购 SQL 转储时遇到了这个问题,该转储将表格放置在错误的顺序。有问题的 CREATE 包含一个 CONSTRAINT ... REFERENCES,它引用了一个尚未创建的表。

            我找到了有问题的表,并将其 CREATE 语句移到有问题的表上方,错误消失了。

            我遇到的与此错误转储有关的另一个错误是 ERROR 1005/errno: 150 -- "Can't create table" ,这又是表创建乱序的问题。

            【讨论】:

              【解决方案13】:

              我也遇到过类似的问题。就我而言,它是通过以这种方式获取光标来解决的:

              cursor = self.conn.cursor(buffered=True)
              

              【讨论】:

              • 我使用的是 mysql.connector 而不是 MySQLdb
              【解决方案14】:

              与@imxylz 相同,但我必须使用mycursor.execute('set GLOBAL max_allowed_packet=67108864'),因为在不使用 GLOBAL 参数的情况下出现只读错误。

              mysql.connector.__version__ 
              

              8.0.16

              【讨论】:

                【解决方案15】:

                当我的 CONSTRAINT 名称与其他 CONSTRAINT 名称相同时,我发生了这种情况。

                更改我的CONSTRAINT 名称解决了这个问题。

                【讨论】:

                  【解决方案16】:

                  我遇到了同样的问题。由于其他一些问题,我曾尝试将cnx.close() 行添加到我的其他函数中。相反,我删除了所有这些无关的关闭并像这样设置我的课程:

                  class DBase:
                  
                  config = {
                        'user': 'root',
                        'password': '',
                        'host': '127.0.0.1',
                        'database': 'bio',
                        'raise_on_warnings': True,
                        'use_pure': False,
                        }
                  
                  def __init__(self):
                      import mysql.connector
                      self.cnx = mysql.connector.connect(**self.config)
                      self.cur = self.cnx.cursor(buffered=True)
                      print(self.cnx)
                  def __enter__(self):
                      return DBase()
                  
                  def __exit__(self, exc_type, exc_val, exc_tb):
                      self.cnx.commit()
                      if self.cnx:
                          self.cnx.close()
                  

                  在此类中调用的任何函数都是连接、提交和关闭。

                  【讨论】:

                    【解决方案17】:

                    当我尝试对数百万条记录进行批量插入时,我遇到了“管道损坏”的错误。我最终解决了这个问题,将我的数据分块成更小的批量大小,然后为我需要做的每个插入运行一个带有 mysql 游标的 executemany 命令。这解决了问题,并且似乎没有以任何明显的方式影响性能。

                    例如。

                    def chunks(data):
                        for i in range(0, len(data), CHUNK_SIZE):
                            yield data[i:i + CHUNK_SIZE]
                    
                    
                    def bulk_import(update_list):
                        new_list = list(chunks(update_list))
                        for batch in new_list:
                             cursor.execute(#SQL STATEMENT HERE)
                    

                    【讨论】:

                      【解决方案18】:

                      你可以看到我对类似问题的回答:

                      https://stackoverflow.com/a/69610550/16647254

                      使用锁来解决这个问题

                      lock.acquire()
                      mysqlhelper.getconn()
                      result_db_num = mysqlhelper.update(sql, [businessid, md5_id])
                      mysqlhelper.end()
                      mysqlhelper.dispose()
                      lock.release()
                      

                      【讨论】:

                        【解决方案19】:

                        我在使用 mariadb sqlalchemypandas 时也遇到了同样的情况,就像上面的 @iamapotatoe 一样,我还创建了一个函数来将数据帧分解成块并将它们移植到 sql一点一点的数据库。 如果更改 mysql 配置选项中的 max_allowed_packet 对您不起作用,则可以使用此功能。

                        def load_large_df(table_name,df_to_load,batch_size,engine):
                            df_to_load = df_to_load.dropna(how='all')
                            with engine.connect() as conn:
                                conn.execute(f"DROP TABLE IF EXISTS {table_name}")
                                rows = df_to_load.shape[0]
                                batch = int(rows/batch_size)
                                
                        
                                strt = 0
                                while strt < rows:
                                    df = df_to_load[strt:].head(batch)
                                    df.to_sql(table_name,con=conn,if_exists='append')
                                    strt += batch
                        

                        【讨论】:

                        • Pandas 已经提供了一个专用(和优化的)参数chunksize 来分块导入大型数据帧。
                        【解决方案20】:

                        解决起来很简单,到你的phpadmin控制面板点击config/然后编辑你看到的.ini文件。查找端口 3306,如果这不是您用于连接的端口,请将 3306 更改为您正在使用的端口。在您的登录屏幕上,只需为您的服务器输入 localhost,如果它不是默认端口,或者您没有按原样更改 sql 配置 leavit 中的文件名 my.ini,则为您的端口。然后输入您的用户名:root 或您创建的用户名,然后输入密码:1234 或您分配的用户名。如果您正在本地连接,请不要检查 url 选项。然后键入要编辑的数据库的名称。注意:连接后,您将看到您的服务器或您要连接的服务器上的数据库列表。

                        【讨论】:

                        • 记得使用 % 赋予用户对你的表和数据库的通用访问权限
                        • 我不知道这个答案有什么关系。 OP 试图询问为什么他在执行查询时会失去连接。
                        • 这个答案是题外话。
                        猜你喜欢
                        • 2011-03-28
                        • 2012-11-15
                        • 2015-09-22
                        • 1970-01-01
                        • 1970-01-01
                        • 2015-11-22
                        • 1970-01-01
                        • 2020-07-11
                        • 2017-11-08
                        相关资源
                        最近更新 更多