2014-11-16 9 views
8

ja niedawno zmodernizowane od Django 1.4 Django 1.7, a ponieważ wciąż otrzymuję następujący komunikat o błędzie dla niektórych skryptów, czasami:Django, po aktualizacji: serwer MySQL zniknęło

OperationalError: (2006, 'MySQL server has gone away')

Skrypty są bardzo długie lub nieprzerwanie działające zadania, które mogą obejmować fazy braku komunikacji z bazą danych przez kilka minut, więc czasy połączenia są przekroczone. Jednak przed aktualizacją nie było problemu, ponieważ Django wydawał się automatycznie przywracać połączenie. Teraz nie oznacza to, że często zadania kończą się niepowodzeniem w środku.

Czy ktoś wie, co się zmieniło i jak mogę to naprawić?

Czy to być może związane z tym numerem/fix: https://code.djangoproject.com/ticket/21463

Dzięki wielkie!

+0

wyboru [to] (https://code.djangoproject.com/ticket/21597 # comment: 29) i [this] (http://piwik.org/faq/troubleshooting/faq_183/). –

Odpowiedz

16

Powodem takiego zachowania jest trwałe połączenie z bazą danych, która została wprowadzona w Django 1.6.

Aby zapobiec błąd przekroczenia limitu czasu połączenia należy ustawić CONN_MAX_AGE w settings.py do wartości, która jest mniejsza niż wait_timeout w MySQL config (my.cnf). W takim przypadku Django wykryje, że połączenie musi zostać ponownie otwarte wcześniej niż MySQL je wyrzuci. Domyślna wartość dla MySQL 5.7 to 28800 sekund.

settings.py:

DATABASES = { 
    'default': { 
     'ENGINE': 'django.db.backends.mysql', 
     'CONN_MAX_AGE': 3600, 
     <other params here> 
    } 
} 

Dokumentacja: https://docs.djangoproject.com/en/1.7/ref/settings/#conn-max-age

my.cnf:

wait_timeout = 28800 

Dokumentacja: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout

3

W django 1.6, gdy upłynął czas wait_timeout (mysql), dostęp do bazy danych spowodował błąd (2006, serwer MySQL zniknął). To nie było w przypadku Django 1.5.1

Zauważyłem ten błąd przy użyciu pracowników, które działają kodu django (za pomocą gearman).

Aby odtworzyć:

Ustaw limit czasu na niską wartość edytując /etc/mysql/my.cnf dodać następujące pod [mysqld]

wait_timeout = 10 
interactive_timeout = 10 

Następnie

% python manage.py shell

>>> # access DB 
>>> import django.contrib.auth.models 
>>> print list(django.contrib.auth.models.User.objects.all()) 
>>> import time 
>>> time.sleep(15) 
>>> print list(django.contrib.auth.models.User.objects.all()) 

Teraz pojawia się błąd.

Proste rozwiązanie znalazłem w internecie jest wywołanie django.db.close_connection() przed dostępem

>>> import django.db 
>>> django.db.close_connection() 
>>> print list(django.contrib.auth.models.User.objects.all()) 

działa OK.

7

Mam procesu uruchomionego w tle rqworker WH Ich wykonuje oddzielne zadania, aby odświeżyć niektóre dane po niektórych działaniach użytkownika.

Zawsze otrzymuję OperationalError: (2006, 'MySQL server has gone away') , jeśli nie było żadnych działań użytkownika przez więcej niż wait_timeout sekund. Nawet jeśli ustawię CONN_MAX_AGE mniej niż MySQL wait_timeout.

Jak rozumiem, zmiana CONN_MAX_AGE może pomóc, jeśli Django sprawdzi i zamknie swoje połączenia automatycznie przez ten czas. Ale Django 1.7.x sprawdza to przed i po każdym żądaniu (zobacz django/db/init.py#L101-L112).

Jak opisano w Django ticket 15119, widzimy, że Django wykonał ping, aby sprawdzić, czy połączenie było żywe przed wykonaniem każdego zapytania. To zachowanie zostało naprawione w commit 282b2f4.

deweloperzy Django dał jedno krótkie odpowiedzi na wszystkie pytania, jak to w https://code.djangoproject.com/ticket/21597#comment:29

Dlatego moja rqworker proces musi potwierdzić połączenie sama dla każdej nowej pracy. (Uwaga: jeśli zamkniemy połączenie, Django utworzy nowe).

Mam zamiar użyć Django do podejścia do zlecenia dla zleceń i zadzwonić pod numer django.db.close_old_connections() przed i po każdej pracy. I tak, CONN_MAX_AGE powinien być mniejszy niż MySQL wait_timeout, ponieważ Django nie sprawdza, czy metoda MySQL server has gone away w django.db.close_old_connections().

1

Być może limit czasu jest problemem dla niektórych osób, ale napotkałem ten problem podczas próby napisania bardzo dużego pola BLOB. Postanowiłem go poprzez zwiększenie rozmiaru pakietu max dopuszczalne w pliku konfiguracyjnym mysql ...

max_allowed_packet = 4m

Nie zapomnij, aby ponownie uruchomić mysql po zmianie /etc/my.cnf. Ta strona pomogła ...

http://dev.mysql.com/doc/refman/5.5/en/packet-too-large.html

0

Zauważyliśmy to zbyt. Powyższa odpowiedź ustawienia CONN_MAX_AGE na coś mniej niż wait_timeout MySQL/MariaDB działa - dla sieci.

W przypadku długotrwałych zadań to jednak nie działa. Zamiast tego zapakowaliśmy go i zamknęliśmy połączenie za każdym razem, gdy jeden z naszych długich uruchomionych zadań zostanie wykonany.

Łączymy to z naszym własnym niestandardowym basenem. Weź to lub odejdź - domyślny Django ma zerową kontrolę - nie jest to coś, co lubiliśmy w produkcji. Ustawiamy maksymalną pulę, aby zabić serwer, zanim zabije DB z zbyt wieloma połączeniami.Użyj go jako dekorator dla zadań:

@close_db_connection() def task_do_something(): print 'hello'

''' 
Created on Dec 23, 2017 

@author: Kevin 
''' 
from functools import wraps 

def close_db_connection(ExceptionToCheck=Exception, raise_exception=False, notify=False): 
    """Close the database connection when we're finished, django will have to get a new one...""" 
    def deco_wrap(f): 
     @wraps(f) 
     def f_wrap(*args, **kwargs): 
      try: 
       return f(*args, **kwargs) 
      except Exception as e: 
       raise e 
      finally: 
       from django.db import connection; 
       connection.close(); 

     return f_wrap 
    return deco_wrap 
Powiązane problemy