Скорость пакетной обработки в Python ниже, чем в Java?
Я работаю над переносом функции, реализованной на Java Spring Boot, на Python Django.
Я столкнулся с проблемой при переносе функции, которая получает список ключевых слов с помощью запроса select и обновляет эти ключевые слова с помощью запроса update.
В частности, обновление 450 строк занимает около 0,1 секунды в Java, а в Python - 2 секунды.
Ниже приведены методы, которые я использовал для измерения времени, а также код на Java и Python.
Метод измерения
// java
long startTime = System.nanoTime();
// Execute the update
long endTime = System.nanoTime();
double duration = (endTime - startTime) / 1_000_000_000.0;;
String formattedDuration = String.format("%.2f seconds", duration);
log.info("processing_time: {}", formattedDuration);
// python
start_time = time.perf_counter()
// Execute the update
end_time = time.perf_counter()
processing_time = end_time - start_time
processing_time_formatted = f"{processing_time:.2f}"
logger.info(f"processing_time: {processing_time_formatted} seconds")
Logic
Java
// application.yml
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb:...
username: ...
password: ...
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
show_sql: true
use_sql_comments: true
format_sql: true
dialect : org.hibernate.dialect.MariaDBDialect
open-in-view: false
sql:
init:
mode: never
// service
public void updateUserIntrKwd(String indvNum) {
// 2s
List<IntrKwdDto> kwdList = intrKwdMapper.selectIntrKwdList(indvNum);
// 0.001s
DatesAndMaxFrequency datesAndMaxFrequency = getDatesAndMaxFrequency(kwdList);
// 0.1s
intrKwdMapper.updateUserIntrKwd(kwdList, datesAndMaxFrequency);
}
// Mapper
<update id="updateUserIntrKwd" parameterType="map">
<foreach collection="kwdList" item="item" separator=";">
...
Python
// settings.py
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "...",
"USER": "...",
"PASSWORD": "...",
"HOST": "...",
"PORT": 3306,
},
// service
def update_user_interest_keywords(indv_num: str) -> int:
// 2s
user_kwd_list = select_interest_keywords_list(indv_num)
// 0.001s
dates_and_max_frequency = get_dates_and_max_frequency(user_kwd_list)
// 2s
updated_rows =
execute_user_interest_keywords_query(user_kwd_list, dates_and_max_frequency)
return updated_rows
// query
def execute_user_interest_keywords_query(userKwdList: list[IntrKwd], datesAndMaxFrequency: DatesAndMaxFrequency):
base_query = """
query...
"""
def get_query_params(item):
return (
item.last_aper_yrmo,
...
)
params = [get_query_params(item) for item in userKwdList]
try:
with connections['...'].cursor() as cursor:
cursor.executemany(base_query, params)
updated_rows = cursor.rowcount
...
return updated_rows
У них одна и та же база данных, и я структурировал код на Python так, чтобы он был максимально похож на код на Java.
Примечательно, что скорость обработки запроса SELECT одинакова. Разница в производительности возникает только при выполнении запроса UPDATE.
Я полагаю, что единственное различие заключается в среде, в которой работают серверы.
Исходя из этой мысли, я попробовал следующие методы:
Переключение библиотеки базы данных
Я попытался переключить библиотеку базы данных с 'pymysql' на 'mysqlclient', следуя совету, что 'mysqlclient' может работать лучше.Результат: Никаких существенных изменений.
Изменение опций в 'settings.py'
'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 'autocommit': True, 'connect_timeout': 10, 'charset': 'utf8mb4', 'use_unicode': True, }, 'CONN_MAX_AGE': 60,
Я добавил вышеуказанные опции в 'DATABASES'
Результат: Никаких существенных изменений.
Изменение
.executemany()
на.execute()
. Основываясь на совете, что.executemany()
может хорошо работать только с операторами INSERT и REPLACE, я изменил логику, чтобы использовать.execute()
вместо этого.Результат: Без существенных изменений.
Установка библиотеки 'mariadb'
Используемая база данных - MariaDB версии 10.11.1. Основываясь на совете, что установка библиотеки 'mariadb' может повысить производительность, я добавил библиотеку 'mariadb'.Результат: Никаких существенных изменений.
Новичку в кодировании отчаянно нужна ваша помощь! Почему запрос UPDATE выполняется в десять раз дольше, хотя логика точно такая же?
Слишком длинный комментарий:
Вы, очевидно, используете MariaDB Connector/Java, но не MariaDB Connector/Python, так что это все равно что сравнивать яблоки с грушами.
Все официальные коннекторы MariaDB поддерживают INSERT,UPDATE,DELETE в пакетном (bulk) режиме, эта возможность не поддерживается другими Python драйверами, такими как pymysql, mysqlclient или MySQL Connector/Python. В зависимости от типа соединения (удаленное или нет) пакетный режим работает до 15 раз быстрее.
Использование нескольких execute() с автокоммитом снизит производительность - вместо этого запустите транзакцию, выполните все утверждения и зафиксируйте их в конце.
К сожалению, Django не хватает нативной поддержки MariaDB, мой PR, который я подавал несколько лет назад, был закрыт как "не будет исправлен". Лучшей альтернативой для MariaDB может стать SQLAlchemy, который имеет собственный диалект для MariaDB Connector/Python и поддерживает родные массовые операции.