Почему 60 ГБ памяти исчезает на соединителе MySQL fetchall ()? - PullRequest
0 голосов
/ 02 сентября 2018

MySQL 5.7.18
Python 2.7.5
Панды 0.17.1
CentOS 7.3

Таблица MySQL:

CREATE TABLE test (
  id varchar(12)
) ENGINE=InnoDB;

Размер 10 ГБ.

select round(((data_length) / 1024 / 1024 / 1024)) "GB"
from information_schema.tables 
where table_name = "test"

10GB

Коробка имеет 250 ГБ памяти:

$ free -hm
              total        used        free      shared  buff/cache   available
Mem:           251G         15G        214G        2.3G         21G        232G
Swap:          2.0G        1.2G        839M

Выберите данные:

import psutil
print '1 ' + str(psutil.phymem_usage())

import os
import sys
import time
import pyodbc 
import mysql.connector
import pandas as pd
from datetime import date
import gc
print '2 ' + str(psutil.phymem_usage())

db = mysql.connector.connect({snip})
c = db.cursor()
print '3 ' + str(psutil.phymem_usage())

c.execute("select id from test")
print '4 ' + str(psutil.phymem_usage())

e=c.fetchall()
print 'getsizeof: ' + str(sys.getsizeof(e))
print '5 ' + str(psutil.phymem_usage())

d=pd.DataFrame(e)
print d.info()
print '6 ' + str(psutil.phymem_usage())

c.close()
print '7 ' + str(psutil.phymem_usage())

db.close()
print '8 ' + str(psutil.phymem_usage())

del c, db, e
print '9 ' + str(psutil.phymem_usage())

gc.collect()
print '10 ' + str(psutil.phymem_usage())

time.sleep(60)
print '11 ' + str(psutil.phymem_usage())

Выход:

1 svmem(total=270194331648L, available=249765777408L, percent=7.6, used=39435464704L, free=230758866944L, active=20528222208, inactive=13648789504, buffers=345387008L, cached=18661523456)
2 svmem(total=270194331648L, available=249729019904L, percent=7.6, used=39472222208L, free=230722109440L, active=20563484672, inactive=13648793600, buffers=345387008L, cached=18661523456)
3 svmem(total=270194331648L, available=249729019904L, percent=7.6, used=39472222208L, free=230722109440L, active=20563484672, inactive=13648793600, buffers=345387008L, cached=18661523456)
4 svmem(total=270194331648L, available=249729019904L, percent=7.6, used=39472222208L, free=230722109440L, active=20563484672, inactive=13648793600, buffers=345387008L, cached=18661523456)
getsizeof: 1960771816
5 svmem(total=270194331648L, available=181568315392L, percent=32.8, used=107641655296L, free=162552676352L, active=88588271616, inactive=13656334336, buffers=345395200L, cached=18670243840)
<class 'pandas.core.frame.DataFrame'>
Int64Index: 231246823 entries, 0 to 231246822
Data columns (total 1 columns):
0    object
dtypes: object(1)
memory usage: 3.4+ GB
None
6 svmem(total=270194331648L, available=181571620864L, percent=32.8, used=107638353920L, free=162555977728L, active=88587603968, inactive=13656334336, buffers=345395200L, cached=18670247936)
7 svmem(total=270194331648L, available=181571620864L, percent=32.8, used=107638353920L, free=162555977728L, active=88587603968, inactive=13656334336, buffers=345395200L, cached=18670247936)
8 svmem(total=270194331648L, available=181571620864L, percent=32.8, used=107638353920L, free=162555977728L, active=88587603968, inactive=13656334336, buffers=345395200L, cached=18670247936)
9 svmem(total=270194331648L, available=183428308992L, percent=32.1, used=105781678080L, free=164412653568L, active=86735921152, inactive=13656334336, buffers=345395200L, cached=18670260224)
10 svmem(total=270194331648L, available=183428308992L, percent=32.1, used=105781678080L, free=164412653568L, active=86735921152, inactive=13656334336, buffers=345395200L, cached=18670260224)
11 svmem(total=270194331648L, available=183427203072L, percent=32.1, used=105782812672L, free=164411518976L, active=86736560128, inactive=13656330240, buffers=345395200L, cached=18670288896)

Я даже удалил соединение с базой данных и вызвал сборщик мусора.

Как таблица размером 10 ГБ могла использовать 60 ГБ памяти?

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Краткий ответ: накладные расходы памяти структур данных Python.

У вас есть таблица с ~ 231M строк, занимающая ~ 10 ГБ, поэтому каждая строка имеет около 4 байтов.

fetchall переведите это в список кортежей, подобных этому:

[('abcd',), ('1234',), ... ]

Ваш список содержит ~ 231M элементов и использует ~ 19 ГБ памяти: в среднем каждый кортеж использует 8,48 байта.

$ python
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys

Кортеж:

>>> a = ('abcd',)
>>> sys.getsizeof(a)
64

Список из одного кортежа:

>>> al = [('abcd',)]
>>> sys.getsizeof(al)
80

Список из двух кортежей:

>>> al2 = [('abcd',), ('1234',)]
>>> sys.getsizeof(al2)
88

Список из 10 кортежей:

>>> al10 = [ ('abcd',) for x in range(10)]
>>> sys.getsizeof(al10)
200

Список из 1М кортежей:

>>> a_realy_long = [ ('abcd',) for x in range(1000000)]
>>> sys.getsizeof(a_realy_long )
8697472

Почти наше число: 8,6 байта на кортеж в списке.

К сожалению, здесь мало что можно сделать: mysql.connector выбирает структуру данных, а диктованный курсор будет использовать еще больше памяти.

Если вам нужно уменьшить использование памяти, вы должны использовать fetchmany с аргументом подходящего размера.

0 голосов
/ 02 сентября 2018

Редактировать: pd.read_sql принимает только соединения SQLAlchemy. Начните с использования create_engine из SQLAlchemy для подключения к вашей базе данных:

from sqlalchemy import create_engine
engine = create_engine('mysql://database')

затем вызовите .connect() полученного объекта:

connection = engine.connect()

Передайте это соединение на pd.read_sql:

df = pd.read_sql("select id from test", connection)

Это должно уменьшить ваш объем памяти.

Не могли бы вы опубликовать результаты использования памяти после того, как попробуете выше?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...