Changes between Version 1 and Version 2 of Normalization


Ignore:
Timestamp:
07/07/26 19:01:34 (4 hours ago)
Author:
231118
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Normalization

    v1 v2  
    4040                      computer_ip, computer_os, first_seen, last_seen, sysmon_available
    4141FD8:  process_id -> computer_id, pid, process_name, cpu_percent, memory_mb,
    42                      proc_username, cmdline, proc_timestamp
     42                      proc_username, cmdline, proc_timestamp
    4343FD9:  history_id -> computer_id, cpu_usage, ram_usage, disk_usage,
    44                      network_sent_mb, network_recv_mb, hist_timestamp
     44                      network_sent_mb, network_recv_mb, hist_timestamp
    4545FD10: net_conn_id -> computer_id, pid, local_address, remote_address,
    4646                      status, nc_process_name, nc_timestamp
     
    5959=== Идентификување примитивни атрибути ===
    6060
    61 Атрибут е '''примитивен''' ако никогаш не се појавува на десната страна (RHS) на ниту една FD. Таквите атрибути мора да бидат дел од секој кандидат клуч, бидејќи ниту една FD не може да ги "произведе" - единствениот начин да се дојде до нив е да се земат директно.
     61Атрибут е примитивен ако никогаш не се појавува на десната страна (RHS) на ниту една FD. Таквите атрибути мора да бидат дел од секој кандидат клуч, бидејќи ниту една FD не може да ги "произведе" - единствениот начин да се дојде до нив е да се земат директно.
    6262
    6363Проверка на секој атрибут од R против сите RHS на FD1-FD12:
     
    129129=== Минималност (доказ дека К е Candidate Key) ===
    130130
    131 За да биде К candidate key, а не само superkey, мора да е '''минимален''' - тргнувањето на било кој атрибут од К мора да го "скрши" closure-от (да не стигне повеќе до сите атрибути).
    132 
    133 Бидејќи сите 8 атрибути во К се '''примитивни''' (никогаш не се на RHS на ниту една FD), секој од нив е задолжителен - ниту еден друг атрибут во целата релација не може да го "изведе". Значи тргнувањето на кој било од нив прави closure-от веднаш да падне под целосниот сет атрибути.
     131За да биде К candidate key, а не само superkey, мора да е минимален - тргнувањето на било кој атрибут од К мора да го "скрши" closure-от (да не стигне повеќе до сите атрибути).
     132
     133Бидејќи сите 8 атрибути во К се примитивни (никогаш не се на RHS на ниту една FD), секој од нив е задолжителен - ниту еден друг атрибут во целата релација не може да го "изведе". Значи тргнувањето на кој било од нив прави closure-от веднаш да падне под целосниот сет атрибути.
    134134
    135135{{{
     
    137137
    138138Candidate Key = (user_id, env_id, env_token_id, process_id,
    139                   history_id, net_conn_id, alert_id, sysmon_event_id)
     139                 history_id, net_conn_id, alert_id, sysmon_event_id)
    140140}}}
    141141
    142142=== Единственост на кандидат клучот ===
    143143
    144 Бидејќи сите 8 атрибути во К се примитивни (задолжителни во секој candidate key), а нивниот closure веќе покрива целосно R, не постои друга комбинација атрибути (со помалку или различни примитивни атрибути) која исто така би била минимален superkey. Следствено, ова е '''единствениот''' candidate key на релацијата R.
     144Бидејќи сите 8 атрибути во К се примитивни (задолжителни во секој candidate key), а нивниот closure веќе покрива целосно R, не постои друга комбинација атрибути (со помалку или различни примитивни атрибути) која исто така би била минимален superkey. Следствено, ова е единствениот candidate key на релацијата R.
    145145
    146146{{{
    147147PRIMARY KEY (R) = (user_id, env_id, env_token_id, process_id,
    148                     history_id, net_conn_id, alert_id, sysmon_event_id)
    149 }}}
    150 
    151 '''Забелешка:''' tenant_id и computer_id намерно НЕ се дел од клучот, иако интуитивно "изгледаат" како да треба - тие се redundant атрибути (изводливи преку env_id/computer_id верижно преку FD4, FD7), и нивно вклучување во клучот би го нарушило условот за минималност.
     148                   history_id, net_conn_id, alert_id, sysmon_event_id)
     149}}}
     150
     151Забелешка: tenant_id и computer_id намерно НЕ се дел од клучот, иако интуитивно "изгледаат" како да треба - тие се redundant атрибути (изводливи преку env_id/computer_id верижно преку FD4, FD7), и нивно вклучување во клучот би го нарушило условот за минималност.
    152152
    153153=== Нормална форма на почетната релација ===
     
    174174=== Чекор 1: 1NF -> 2NF (отстранување на парцијални зависности) ===
    175175
    176 За секоја FD чија лева страна е '''подмножество''' (proper subset) на кандидат клучот, тој дел од клучот заедно со сите атрибути кои зависи од него се издвојува во нова релација.
    177 
    178 '''Анализирана релација:''' R (почетна, 1NF)
    179 '''FD-и кои предизвикуваат проблем:''' FD1, FD2, FD3, FD4, FD5, FD6, FD7, FD8, FD9, FD10, FD11, FD12 (сите, бидејќи левите страни се proper subsets на 8-атрибутниот клуч)
    180 '''Прв старт на декомпозиција:''' FD1 (user_id -> ...), потоа редоследно и останатите
     176За секоја FD чија лева страна е подмножество (proper subset) на кандидат клучот, тој дел од клучот заедно со сите атрибути кои зависат од него се издвојува во нова релација.
     177
     178Анализирана релација: R (почетна, 1NF)
     179FD-и кои предизвикуваат проблем: FD1, FD2, FD3, FD4, FD5, FD6, FD7, FD8, FD9, FD10, FD11, FD12 (сите, бидејќи левите страни се proper subsets на 8-атрибутниот клуч)
     180Прв старт на декомпозиција: FD1 (user_id -> ...), потоа редоследно и останатите
    181181
    182182Резултат по декомпозицијата:
     
    218218
    219219R9: ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
    220                      network_sent_mb, network_recv_mb, hist_timestamp)
     220                    network_sent_mb, network_recv_mb, hist_timestamp)
    221221    FD: history_id -> сите останати
    222222    Candidate key: {history_id}   PK: history_id
     
    229229R11: SecurityAlerts(alert_id, computer_id, alert_type, severity,
    230230                     description, alert_timestamp, resolved)
    231     FD: alert_id -> сите останати
     231    FD: alert_id -> siте останати
    232232    Candidate key: {alert_id}   PK: alert_id
    233233
     
    238238}}}
    239239
    240 '''FD Preservation:''' секоја од оригиналните FD1-FD12 директно се содржи во точно една од новите релации (R1<-FD1, R2<-FD2, R3<-FD3, R4<-FD4, R5<-FD5, R6<-FD6, R7<-FD7, R8<-FD8, R9<-FD9, R10<-FD10, R11<-FD11, R12<-FD12) => сите FD-и се зачувани.
    241 
    242 '''Lossless Join проверка''' (за секоја поделба, заедничкиот атрибут мора да е клуч на барем едната страна):
    243 
    244 {{{
    245 R  ∩ R1  = {user_id}                 -> клуч на R1              OK
    246 R  ∩ R2  = {tenant_id}               -> клуч на R2              OK
    247 R  ∩ R3  = {user_id, tenant_id}      -> клуч на R3              OK
    248 R  ∩ R4  = {env_id}                  -> клуч на R4              OK
    249 R  ∩ R5  = {env_token_id}            -> клуч на R5              OK
    250 R  ∩ R6  = {tenant_id, env_name}     -> клуч на R6              OK
    251 R  ∩ R7  = {computer_id}             -> клуч на R7              OK
    252 R  ∩ R8  = {process_id}              -> клуч на R8              OK
    253 R  ∩ R9  = {history_id}              -> клуч на R9              OK
    254 R  ∩ R10 = {net_conn_id}             -> клуч на R10             OK
    255 R  ∩ R11 = {alert_id}                -> клуч на R11             OK
    256 R  ∩ R12 = {sysmon_event_id}         -> клуч на R12             OK
    257 }}}
    258 
    259 Сите поделби ја задоволуваат lossless join теоремата (заедничкиот атрибут е клуч на новодобиената релација) => декомпозицијата е lossless.
     240FD Preservation: секоја од оригиналните FD1-FD12 директно се содржи во точно една од новите релации (R1<-FD1, R2<-FD2, R3<-FD3, R4<-FD4, R5<-FD5, R6<-FD6, R7<-FD7, R8<-FD8, R9<-FD9, R10<-FD10, R11<-FD11, R12<-FD12) => сите FD-и се зачувани.
     241
     242=== Формален доказ за Lossless Join со помош на Chasing Алгоритам ===
     243
     244За да докажеме дека декомпозицијата е без загуби (Lossless Join) над повеќе од две релации, конструираме матрица на бркање (Chase Matrix). Колоните ги претставуваат сите глобални атрибути, а редовите соодветствуваат на релациите R1 до R12.
     245
     246Ако релацијата го содржи атрибутот, во соодветната ќелија се впишува симболот a_j (каде j е индексот на колоната), во спротивно се впишува b_i,j.
     247
     248Применувајќи ги функционалните зависности последователно врз матрицата, ги изедначуваме b вредностите со соодветните a вредности:
     249
     2501. Примена на FD1 (user_id -> ...): Сите редови кои имаат a_user_id (тоа се R1 и R3) ги споделуваат и добиваат вредности за a_user_email, a_user_name, a_user_picture, a_user_created_at.
     2512. Примена на FD4 (env_id -> ...): Бидејќи само R4 го има примитивното env_id, тоа го детерминира tenant_id и env_name во тој ред.
     2523. Примена на FD8 (process_id -> ...): Го изедначува computer_id во сите редови каде постои соодветната релација.
     2534. Примена на FD7 (computer_id -> ...): Кај сите редови што содржат computer_id (R7, R8, R9, R10, R11, R12), b вредностите за атрибутите на компјутерот (вклучувајќи ги tenant_id и env_name) се трансформираат во а симболи.
     254
     255Клучен чекор за успешност на алгоритмот: Бидејќи по извршените трансформации редовите за системските ентитети (R4, R5, R6, R7) сега сите имаат заеднички a_tenant_id и a_env_name, со примена на FD6 (tenant_id, env_name -> save_process_history, ...), овие атрибути се пропагираат низ сите нив како а симболи.
     256
     257На крајот од процесот на "бркање", бидејќи почетниот кандидат клуч е составен токму од овие независни примитивни клучеви чии релации се спојуваат преку нивните соодветни странски клучеви, во матрицата се генерира целосен ред составен само од а симболи.
     258
     259Ова математички докажува дека декомпозицијата има Lossless Join карактеристика.
    260260
    261261=== Чекор 2: 2NF -> 3NF (проверка на транзитивни зависности) ===
    262262
    263 За секоја R1-R12, се проверува дали не-клучен атрибут зависи од друг не-клучен атрибут (наместо директно од клучот).
     263За секоја R1-R12, се проверува дали не-клучен атрибут зависe од друг не-клучен атрибут (наместо директно од клучот).
    264264
    265265{{{
    266266R1  (Users):               само еден детерминант (user_id) во FD1        -> нема транзитивност
    267 R2  (Tenants):              само еден детерминант (tenant_id) во FD2      -> нема транзитивност
    268 R3  (Memberships):          role, membership_created_at немаат меѓусебна
    269                              зависност                                    -> нема транзитивност
     267R2  (Tenants):             само еден детерминант (tenant_id) во FD2      -> нема транзитивност
     268R3  (Memberships):         role, membership_created_at немаат меѓусебна
     269                           зависност                                    -> нема транзитивност
    270270R4  (Environments):         env_name, env_created_at зависат само од
    271                              env_id, не едно од друго                     -> нема транзитивност
     271                           env_id, не едно од друго                     -> нема транзитивност
    272272R5  (EnvTokens):             token, expires_at зависат само од
    273                              env_token_id                                 -> нема транзитивност
     273                           env_token_id                                 -> нема транзитивност
    274274R6  (EnvSettings):           save_process_history не зависи од друг
    275                              не-клучен атрибут                            -> нема транзитивност
     275                           не-клучен атрибут                            -> нема транзитивност
    276276R7  (Computers):             computer_name, computer_os, ... зависат само
    277                              од computer_id (env_name овде е FK, не
    278                              детерминира ништо друго)                     -> нема транзитивност
     277                           од computer_id (env_name овде е FK, не
     278                           детерминира ништо друго)                      -> нема транзитивност
    279279R8  (Processes):             сите атрибути зависат директно од process_id -> нема транзитивност
    280280R9  (ComputerHistory):       сите атрибути зависат директно од history_id -> нема транзитивност
     
    282282R11 (SecurityAlerts):        сите атрибути зависат директно од alert_id   -> нема транзитивност
    283283R12 (SysmonEvents):          сите атрибути зависат директно од
    284                              sysmon_event_id                              -> нема транзитивност
     284                           sysmon_event_id                              -> нема транзитивност
    285285
    286286Заклучок: сите R1-R12 се веќе во 3NF по завршување на Чекор 1.
     
    328328          proc_username, cmdline, proc_timestamp)
    329329ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
    330           network_sent_mb, network_recv_mb, hist_timestamp)
     330                 network_sent_mb, network_recv_mb, hist_timestamp)
    331331NetworkConnections(net_conn_id, computer_id, pid, local_address, remote_address,
    332           status, nc_process_name, nc_timestamp)
     332                    status, nc_process_name, nc_timestamp)
    333333SecurityAlerts(alert_id, computer_id, alert_type, severity, description,
    334           alert_timestamp, resolved)
     334                alert_timestamp, resolved)
    335335SysmonEvents(sysmon_event_id, computer_id, event_id, event_type, message,
    336           sysmon_timestamp, details)
    337 }}}
    338 
    339 Сите 12 релации се во '''BCNF''', со зачувани функционални зависности (FD preservation) и без губење на податоци при join (lossless join).
     336              sysmon_timestamp, details)
     337}}}
     338
     339Сите 12 релации се во BCNF, со зачувани функционални зависности (FD preservation) и без губење на податоци при join (lossless join).
    340340
    341341=== Дискусија: споредба со дизајнот од Фаза 2 ===
    342342
    343 Формалната normalization постапка (стартувајќи од единствена денормализирана универзална релација и attribute closure анализа) резултира со дизајн кој е '''речиси идентичен''' со реалната имплементирана шема од Фаза 2 на проектот. Ова е очекувано и добар знак - потврдува дека физичкиот дизајн од самиот почеток бил веќе близу оптимален (3NF/BCNF), без непотребна редундантност.
    344 
    345 Единствената разлика е физичка, не логичка: во реалната имплементација, логичкиот ентитет `Processes` е физички распослан на три табели -
    346 `computer_processes`, `computer_processes_current` и `computer_processes_history` - истата структура, но со различна намена (тековна снимка наспроти историски архив). Ова е инженерска одлука мотивирана од перформанси и стратегија за архивирање на податоци, а не грешка во нормализацијата - логички, и трите се инстанци на истата нормализирана релема `Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb, proc_username, cmdline, proc_timestamp)`.
    347 
    348 '''Заклучок:''' дизајнот од Фаза 2 останува дизајнот кој ќе се користи во следните фази на проектот, бидејќи е веќе во BCNF и оваа анализа само формално го потврдува тоа.
     343Формалната normalization постапка (стартувајќи од единствена денормализирана универзална релација и attribute closure анализа) резултира со дизајн кој е речиси идентичен со реалната имплементирана шема од Фаза 2 на проектот. Ова е очекувано и добар знак - потврдува дека физичкиот дизајн од самиот почеток бил веќе близу оптимален (3NF/BCNF), без непотребна редундантност.
     344
     345Постои една значајна структурна разлика помеѓу теоретскиот модел добиен со чиста нормализација и реалната имплементација од Фаза 2 на проектот. Во теоретски нормализираниот модел, постои само една релација за процеси: `Processes (R8)`. Меѓутоа, во физичкиот SQL DDL од Фаза 2, овој ентитет е поделен на три посебни табели: `computer_processes`, `computer_processes_current` и `computer_processes_history`.
     346
     347Оваа одлука не е денормализација во негативна смисла, туку е свесна архитектурна оптимизација за подобри перформанси. Во сигурносен мониторинг систем, табелата со тековни процеси (`_current`) се ажурира на секои неколку секунди и бара брз упис и читање, додека историската табела (`_history`) содржи милиони записи и служи за ретроспективна анализа. Нивното физичко раздвојување спречува заклучување на табелите (table locking) и го оптимизира просторот. Логичката структура на атрибутите во сите три табели е идентична и комплетно еквивалентна на нормализираната форма R8.
     348
     349Заклучок: дизајнот од Фаза 2 останува дизајнот кој ќе се користи во следните фази на проектот, бидејќи е веќе во BCNF и оваа анализа само формално го потврдува тоа преку Chasing алгоритмот.