Идея скрипта проста и прямолинейна. Шаг первый - считываем данные из таблиц (см функцию getfromdb). Далее, сопоставляем записи на основе ключей, и синхронизируем в четыре шага:
- удаляем записи, которые не находятся в мастер-таблицы;
- удаляем изменившиеся записи, маркируя записи мастер-таблицы для добавления;
- маркируем записи, находящиеся в мастер-таблице, но отсутствующие в слейв-таблице;
- добавляем маркированные записи из мастера в слейв.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Usage:
# sync('db_master', 'table_master', 'db_slave', 'table_slave', fields)
# where 'fields' is list of fields in tables to syncronize
#
# NOTE: first field in field list should be a key
import pg
HOST = 'localhost'
DBUSER = 'postgres'
DBPASS = 'postgres'
def getfromdb(db_name, table_name, fields):
# Forming fields
qfields = ''
for field in fields:
if qfields:
qfields += ', ' + field
else:
qfields = field
conn = pg.connect(db_name, HOST, 5432, None, None, DBUSER, DBPASS)
query = "SELECT %s FROM %s" % (qfields, table_name)
result = conn.query(query)
table = {}
for row in result.getresult():
table[row[0]] = row[1:]
conn.close()
return table
def sync(masterdb, mastertable, slavedb, slavetable, fields):
master_data = getfromdb(masterdb, mastertable, fields)
slave_data = getfromdb(slavedb, slavetable, fields)
slave_conn = pg.connect(slavedb, HOST, 5432, None, None, DBUSER, DBPASS)
table = []
# Remove outdated rows
for key, values in slave_data.items():
if key not in master_data:
print "Delete row id=%s" % key
query = "DELETE FROM %s WHERE %s = %s" % \
(slavetable, fields[0], key)
slave_conn.query(query)
elif master_data[key] != slave_data[key]:
print "Update row id=%s" % key
query = "DELETE FROM %s WHERE %s = %s" % \
(slavetable, fields[0], key)
slave_conn.query(query)
# Mark record to update
table.append([key] + list(master_data[key]))
# Mark new records
for key, values in master_data.items():
if key not in slave_data:
table.append([key] + list(values))
print "Insert row fid=%s" % key
# Insert new records
slave_conn.inserttable(slavetable, table)
slave_conn.close()
Пример использования. Следующий код реплицирует поля fields из мастер-таблицы table1 базы db1 в таблицу table2 базы db2. В списке полей f1 - ключевое поле.
fields = ('f1', 'f2', 'f3')
sync('db1', 'table1', 'db2', 'table2', fields)
Не уверен, что скрип будет хорошо работать на громадных таблицах, однако для остальных работает вполне неплохо.