Многочисленные базы данных Django. Возврат к главному, если подчиненный отключен.
У меня есть настройка master-slave-репликации для MySQL db backend для Django.
В настоящее время я читаю и пишу только для Master DB, но мои панели инструментов довольно интенсивные.
Я искал вариант, где я могу определить как следующее
БАЗ
DATABASES = {
'default_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
для панелей мониторинга, отчетов и других приложений, я хочу:
Попробуйте подключиться: default_slave: используя default_slave
, если доступно другое, используя default
То есть, если ведомое устройство завершено, выберите отчеты из самой подчиненной базы данных, если не извлекать отчеты из основной базы данных.
Поймать, ведомый может быть вверх или вниз, и я хочу, чтобы это было динамически выбираемым, в зависимости от того, какую базу данных использовать для извлечения отчетов на основе возможности репликации.
Возможно ли это? Могу ли я проверить соединение перед рукой и двигаться вперед?
С этим я бы написал и sync_db в Master и всегда читал из Slave, если ведомое устройство работает.
Требуется некоторое решение/подсказка для raw queries
, а также orm queries
Концепция маршрутизатора кажется приятной, но резервное копирование на подчиненное устройство недоступно, я не знаю возможности.
UPDATE
Как сделать MULTI-DATABASE
БАЗ
DATABASES = {
'default_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'linux': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'linux_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'mac': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'mac_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'pc': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
'pc_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'PASSWORD': '',
'HOST': '',
'PORT': '3306',
},
}
Теперь у меня есть
1. Статические данные
2. Динамические данные
Статические данные должны быть сохранены в "по умолчанию", которые будут реплицированы на "default_slave"
Для динамических данных сначала необходимо определить, где могут быть динамические данные: в "mac" или "pc" или в "linux"
Чтобы добиться этого, я добавил одно поле в "статическую таблицу": "query_on", который содержит либо ['mac', либо 'linux' или 'pc']
Теперь, когда я использую набор запросов, я просто пишу
static = Static.objects.get(pk = 1)
query_on = static.query_on
dynamic = Dynamic.objects.get(static = static).using(alias=query_on)
Это хорошо работает, маршруты запросов к базе данных, которые необходимо выполнить, здесь мне нужно судить:
- Если
<'query_on'>_slave
: соединение вверх: используйте: <'query_on'>_slave
ИЛИ
- Если
<'query_on'>_slave
: соединение не работает: используйте: <'query_on'>
Как это сделать?
Дополнительные сведения о приложении:
- Существует одна база данных: по умолчанию (база данных конфигурации и аналитики): для сохранения данных конфигурации и отчетов аналитических данных
- Существует 20 баз данных (исходные базы данных): например, пример: mac, linux, rhel, windows, pc.... (имя примера): для сбора необработанных данных, которые не являются процессами для аналитики
- каждая база данных имеет одно или несколько подчиненных устройств, соглашение об именах будет: default_slave_0, default_slave_1, default_slave_2 и, следовательно, для других баз данных
Теперь данные аналитики должны быть сначала запрошены за 5 минут, 30 минут, 1 час.... и этот запрос нужно отправить в конкретную базу данных, поскольку не каждая база данных будет нести определенный набор данных, необходимый для аналитики.
Для этого нам нужно
- получить данные конфигурации из (по умолчанию или любого из его подчиненных (подчиненная часть - вопрос))
- Как только у нас будет конфигурация, мы сможем легко найти, где "сырые" данные могут быть
- для необработанных данных и сбора результатов и анализа → сохранить его в базе данных по умолчанию.
Теперь для всех 30 (raw) и 1 базы данных по умолчанию потребуется "синхронизация", поскольку мы поддерживаем ту же структуру abse данных по всем узлам.
Теперь, поскольку мы рассматриваем всплески CPU во всех базах данных, имеет смысл использовать "подчиненные" базы данных для запроса "сырых" данных.
следовательно, требование using
. Я не могу представить, как маршрутизаторы могут помочь здесь?
Ответы
Ответ 1
Вы находитесь на правильном пути с использованием маршрутизатора. Я предполагаю, что ваши два определения db идентичны, это просто опечатка.
(FYI, я буду ссылаться на иерархию базы данных, используя более чувствительный ведущий- > последователь)
В ваших функциях db_for_read() вы можете проверить возможность подключения к вашему подписчику. Это может повлечь за собой дополнительные накладные расходы, но расходы на автоматическое переключение на резервную базу для базы данных. Пример определения базы данных:
DATABASES = {
'follower': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'follower',
'USER': 'root',
'HOST': '54.34.65.24',
'PORT': '3306',
},
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'application',
'USER': 'root',
'HOST': '54.34.65.23',
'PORT': '3306',
},
}
Вы можете проверить соединение с помощью быстрой попытки/кроме этого примера. Маршрутизатор, использующий это, который делает то, что вам нужно, будет выглядеть так:
from django.conf import settings
import socket
def test_connection_to_db(database_name):
try:
db_definition = getattr(settings, 'DATABASES')[database_name]
s = socket.create_connection((db_definition['HOST'], db_definition['PORT']), 5)
s.close()
return True
except (AttributeError, socket.timeout) as e:
return False
class FailoverRouter(object):
"""A router that defaults reads to the follower but provides a failover back to the default"""
def db_for_read(self, model, **hints):
if test_connection_to_db('follower'):
return 'follower'
return 'default'
def db_for_write(self, model, **hints):
"Point all writes to the default db"
return 'default'
def allow_syncdb(self, db, model):
"Make sure only the default db allows syncdb"
return db == 'default'
Это все равно синхронизируется с мастером, как вы хотите. Кроме того, вы могли бы сделать логику как для db_for_read()
, так и db_for_write()
более сложной (например, выбрать последователя db только для определенных моделей, которые запрашиваются для ваших отчетов.
Я не знаю, какие накладные расходы это test_connection()
будет вызывать для каждого чтения, поскольку это будет зависеть от сервера MySQL и таймаута. Возможно, лучшей архитектурой является кэширование этих отчетов с помощью memcached или просто устранение проблем с ведомым, когда-либо снижающимся, и сначала обновлять определения баз данных в настройках.