powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / [PERL] Улучшить алгоритм синхронизации
4 сообщений из 4, страница 1 из 1
[PERL] Улучшить алгоритм синхронизации
    #38009338
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вопрос в данном разделе, т.к. скрипт для синхронизации написан на Perl и будут использоваться его возможности.
Доступа к БД у меня нет, для работы с БД предоставляется API, реализующий SQL-подобные команды. Возможности создания временных таблиц тоже нет, т.е. добавлять новые записи в БД нужно построчно.
Поэтому я вначале считываю локально все данные из таблиц БД, сравниваю эти данные с теми, которые нужно внести в БД, и по результатам сравнения формирую пакеты команд для INSERT, UPDATE и DELETE запросов.

К примеру у меня есть хеш с данными из БД:
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
$remote =
{
  rows =>
  [
    {id=>1, field1=>'fld1', field2=>'fld2', ... },
    {id=>2, field1=>'fld1', field2=>'fld2', ... },
    {id=>3, field1=>'fld1', field2=>'fld2', ... },
    {id=>4, field1=>'fld1', field2=>'fld2', ... },
    ...
  ],
  index =>
  {
    1=>$remote->{rows}->[0],
    2=>$remote->{rows}->[1],
    3=>$remote->{rows}->[2],
    4=>$remote->{rows}->[3],
    ...
  }
}



Есть аналогичный по структуре хеш $local с новыми данными, которые должны оказаться в БД.
Я предполагаю делать следующее:
1. Цикл foreach my $row (@{$remote->{rows}}).
1.1. Если !exists($local->{index}->{$row->{id}}), то $row->{id} добавить в список ID, подлежащих удалению.
1.2. Если запись существует, то сравнить остальные поля. Если хоть одно отличается, то добавить в список ID, подлежащих обновлению.
2. Цикл foreach my $row (@{$local->{rows}}).
2.1. Если !exists($remote->{index}->{$row->{id}}), то $row->{id} добавить в список новых записей.

В качестве оптимизаций я рассматриваю следующие варианты.
A. Сравнивать количество строк в $remote и $local. Где больше строк, использовать в первом цикле, где меньше строк, использовать во втором цикле.
B. Перед первым циклом сделать %id = map {$_=>1} keys($local->{index}). В первом цикле выполнять delete $id{$row->{id}}. То что осталось в %id подлежит добавлению в БД.

Есть ли смысл в подобных оптимизациях?

Модератор: Есть смысл самостоятельно указывать язык программирования в названии темы обсуждения.

________________________
Мы смотрим с оптимизмом...
...в оптический прицел.
...
Рейтинг: 0 / 0
[PERL] Улучшить алгоритм синхронизации
    #38009914
madbear
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

а индексы в $remote и $local ты сам строишь или они из сервиса уже приезжают? если сам, можно попробовать сэкономить на их построении и последующей проверке exists, как-то так:
Код: perl
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
my %intersect = ();

foreach my $row (@{$remote->{rows}})) {
	my $id = $row->['id'];
	$intersect[$id]['remote'] = $row;
}
foreach my $row (@{$local->{rows}})) {
	my $id = $row->['id'];
	$intersect[$key]['local'] = $value;
}
while(($key, $value) = each(%intersect)) {
	if (!exists $value->['local']) {
		delete_record $value->['remote']
	}
	elseif (!exists $value->['remote']) {
		insert_record $value->['local']
	}
	else {
		$changed = compare_record $row->{remote}, $row->{local}
		if ($changed) {
			update_record_with_values  $row->['local']
		}
	}
}
...
Рейтинг: 0 / 0
[PERL] Улучшить алгоритм синхронизации
    #38010447
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Строю сам.
Примерно к такому я и пришел, разве что циклы по remote и local поменял местами (более удобно читать код, удобнее добавлять/обновлять записи в цикле по local, а удалять во втором цикле.
Если кто-то работает с сервисом IPTVPortal, может быть пригодится такой код:

Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
sub action_channel_sync($$)
{
  my $cache = shift;
  my $iptv = shift;
  print "Compare channels...\n";
  my $batch = {};
  $batch->{'insert'} = [];
  $batch->{'update'} = [];
  $batch->{'delete'} = [];
  $batch->{'local'}->{'url'} = {};
  $batch->{'remote'}->{'url'} = {};
  $batch->{'remote_ids'} = {};
  $batch->{'offset'} = {};
  foreach my $row (@{$cache->{'tables'}->{'media'}->{'rows'}})
  {
    $batch->{'remote'}->{'url'}->{$row->{'inet_addr'}} = $row;
    $batch->{'remote_ids'}->{$row->{'inet_addr'}} = $row->{'id'};
  }
  foreach my $row (@{$iptv->{'Channels'}->{'Channel'}})
  {
    print "  - processing $row->{address} ($row->{name})...\n";
    $batch->{'local'}->{'url'}->{$row->{'address'}} = $row;
    delete $batch->{'remote_ids'}->{$row->{'address'}};
    if ($row->{'category'})
    {
      $batch->{'offset'}->{$row->{'category'}} = 0 unless (exists($batch->{'offset'}->{$row->{'category'}}));
      $batch->{'offset'}->{$row->{'category'}}++;
    }
    my %val = ();
    $val{'index'} = $iptv->{'Categories'}->{'index'} + ($iptv->{'Categories'}->{'code'}->{$row->{'category'}}->{'offset'}||0) + $batch->{'offset'}->{$row->{'category'}};
    $val{'protocol'} = ($row->{'protocol'}||$iptv->{'Channels'}->{'default-protocol'});
    $val{'inet_addr'} = $row->{'address'};
    $val{'port'} = ($row->{'port'}||$iptv->{'Channels'}->{'default-port'});
    $val{'name'} = $row->{'name'} . ($row->{'suffix'} ? " ($row->{suffix})" : "");
    $val{'is_tv'} = 'true';
    $val{'encrypted'} = 'false';
    if (exists($batch->{'remote'}->{'url'}->{$row->{'address'}}))
    {
      my $data = $batch->{'remote'}->{'url'}->{$row->{'address'}};
      $row->{'id'} = $data->{'id'};
      my %changes = ();
      while (my ($f,$v) = each %val)
      {
        $changes{$f} = $v if ($data->{$f} ne $v);
      }
      push @{$batch->{'update'}}, { 'table' => 'media', 'set' => \%changes, 'where' => {'eq' => ['id',$row->{'id'}]} } if (%changes);
    }
    else
    {
      push @{$batch->{'insert'}}, { 'into' => 'media', 'columns' => [keys(%val)], 'values' => [values(%val)], 'returning' => 'id' };
      $row->{'id'} = undef;
    }
  }
  foreach my $id (values(%{$batch->{'remote_ids'}}))
  {
    foreach my $table (@{$meta->{'database'}->{'Tables'}->{'Table'}})
    {
      foreach my $field (grep {exists($_->{'link_table'}) && ($_->{'link_table'} eq 'media') && ($_->{'link_column'} eq 'id')} @{$table->{'Fields'}->{'Field'}})
      {
        push @{$batch->{'delete'}}, { 'from' => $table->{'name'}, 'where' => {'eq' => [$field->{'name'},$id]} }
                                    if (exists($cache->{'links'}->{$table->{'name'}}->{'media'}->{$id}));
      }
    }
    push @{$batch->{'delete'}}, { 'from' => 'media', 'where' => {'eq' => ['id',$id]} };
  }
  delete $batch->{'remote_ids'};
  delete $batch->{'local'};
  delete $batch->{'remote'};
  print "Apply changes...\n";
  {
    my $i = 0;
    foreach my $call (@{$batch->{'insert'}})
    {
      $i++; print "  - insert $i\n";
      my $res = $api->insert($call);
    }
  }
  {
    my $i = 0;
    foreach my $call (@{$batch->{'update'}})
    {
      $i++; print "  - update $i\n";
      my $res = $api->update($call);
    }
  }
  {
    my $i = 0;
    foreach my $call (@{$batch->{'delete'}})
    {
      $i++; print "  - delete $i\n";
      my $res = $api->delete($call);
    }
  }
}


Вспомогательные структуры $meta и $iptv беруться из XML-файла (XML::Mini::Document->new()->parse(...)->toHash()->{'Data'}):
Для $meta:

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
<Data>
  <Tables>
    <Table name="domain" alias="d">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="name" type="string" mode="key"/>
      </Fields>
    </Table>
    <Table name="profile" alias="r">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="name" type="string" mode="key"/>
        <Field name="title" type="string" mode="title"/>
        <Field name="require_password" type="boolean"/>
      </Fields>
    </Table>
    <Table name="playlist" alias="l">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="index" type="int"/>
        <Field name="name" type="string" mode="key"/>
        <Field name="title" type="string" mode="title"/>
        <Field name="is_tv" type="boolean"/>
        <Field name="is_vod" type="boolean"/>
        <Field name="is_nvod" type="boolean"/>
        <Field name="is_radio" type="boolean"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
      <Filters mode="and">
        <Filter op="eq" l="is_tv" r="true"/>
      </Filters>
    </Table>
    <Table name="package" alias="p">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="name" type="string" mode="key"/>
        <Field name="paid" type="boolean"/>
        <Field name="cost" type="decimal"/>
        <Field name="expiration_period" type="datetime"/>
      </Fields>
    </Table>
    <Table name="media" alias="m">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="index" type="int" mode="key"/>
        <Field name="protocol" type="string"/>
        <Field name="inet_addr" type="string" mode="url"/>
        <Field name="port" type="int"/>
        <Field name="name" type="string" mode="title"/>
        <Field name="is_tv" type="boolean"/>
        <Field name="is_vod" type="boolean"/>
        <Field name="is_nvod" type="boolean"/>
        <Field name="is_radio" type="boolean"/>
        <Field name="path" type="boolean"/>
        <Field name="encrypted" type="boolean"/>
        <Field name="paid" type="boolean"/>
        <Field name="cost" type="decimal"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
      <Filters mode="and">
        <Filter op="eq" l="is_tv" r="true"/>
        <Filter op="like" l="inet_addr" r="239.0.%"/>
      </Filters>
    </Table>
    <Table name="subscriber" alias="s">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="username" type="string" mode="key"/>
        <Field name="password" type="string"/>
        <Field name="first_name" type="string"/>
        <Field name="email" type="string"/>
        <Field name="max_terminal" type="int"/>
        <Field name="language_id" type="int"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="terminal" alias="t">
      <Fields>
        <Field name="id" type="int" mode="id"/>
        <Field name="mac_addr" type="string" mode="key"/>
        <Field name="inet_addr" type="string"/>
        <Field name="subscriber_id" type="int" link_table="subscriber" link_column="id"/>
        <Field name="profile_id" type="int"/>
        <Field name="registration_id" type="int"/>
        <Field name="session_id" type="int"/>
        <Field name="language_id" type="int"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="profile_media" alias="rm">
      <Fields>
        <Field name="domain_id" type="int"/>
        <Field name="profile_id" type="int" link_table="profile" link_column="id"/>
        <Field name="media_id" type="int" link_table="media" link_column="id"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="playlist_media" alias="lm">
      <Fields>
        <Field name="playlist_id" type="int" link_table="playlist" link_column="id"/>
        <Field name="media_id" type="int" link_table="media" link_column="id"/>
        <Field name="index" type="int"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="package_media" alias="pm">
      <Fields>
        <Field name="package_id" type="int" link_table="package" link_column="id"/>
        <Field name="media_id" type="int" link_table="media" link_column="id"/>
        <Field name="disabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="subscriber_package" alias="sp">
      <Fields>
        <Field name="subscriber_id" type="int" link_table="subscriber" link_column="id"/>
        <Field name="package_id" type="int" link_table="package" link_column="id"/>
        <Field name="expired_on" type="datetime"/>
        <Field name="enabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="subscriber_media" alias="sm">
      <Fields>
        <Field name="subscriber_id" type="int" link_table="subscriber" link_column="id"/>
        <Field name="media_id" type="int" link_table="media" link_column="id"/>
        <Field name="expired_on" type="datetime"/>
        <Field name="enabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="terminal_profile" alias="tr">
      <Fields>
        <Field name="terminal_id" type="int" link_table="terminal" link_column="id"/>
        <Field name="profile_id" type="int" link_table="profile" link_column="id"/>
        <Field name="password" type="string"/>
      </Fields>
    </Table>
    <Table name="terminal_package" alias="tp">
      <Fields>
        <Field name="terminal_id" type="int" link_table="terminal" link_column="id"/>
        <Field name="package_id" type="int" link_table="package" link_column="id"/>
        <Field name="expired_on" type="datetime"/>
        <Field name="enabled" type="boolean"/>
      </Fields>
    </Table>
    <Table name="terminal_media" alias="tm">
      <Fields>
        <Field name="terminal_id" type="int" link_table="terminal" link_column="id"/>
        <Field name="media_id" type="int" link_table="media" link_column="id"/>
        <Field name="expired_on" type="datetime"/>
        <Field name="enabled" type="boolean"/>
      </Fields>
    </Table>
  </Tables>
</Data>



Для $iptv:

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<Data>
  <Packages prefix="custom-">
    <Package code="..." name="..."/>
    ...
  </Packages>
  <Subscribes>
    <Subscribe code="default">
      <PackageList>
        <PackageItem code="..."/>
        ...
      </PackageList>
    </Subscribe>
  </Subscribes>
  <Categories index="10000">
    <Category code="area" name="..." offset="1000"/>
    ...
  </Categories>
  <Channels default-protocol="udp" default-port="1234">
    <Channel category="..." address="239.0.x.x"   name="..."/>
    ...
  </Channels>
</Data>



В $cache загруженные данные из БД, структура примерно такая:
$cache->{'tables'}->{$table} = {id=>$id,field1=>$field1,...}
$cache->{'links'}->{$link_table}->{$foreign_table}->{foreign_column} = [@rows]
...
Рейтинг: 0 / 0
[PERL] Улучшить алгоритм синхронизации
    #38011997
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Оказывается зря старался.
API-запросы на серверной стороне обрабатываются дополнительно и сохранение ссылочной целостности обеспечивается на стороне сервера. То есть рекурсивно удалять или проверять существование записи перед ее добавлением не надо, это сделает сам сервер.
...
Рейтинг: 0 / 0
4 сообщений из 4, страница 1 из 1
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / [PERL] Улучшить алгоритм синхронизации
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]