| 38 | | FD6: (tenant_id, env_name) -> save_process_history, es_created_at, es_updated_at |
| 39 | | FD7: computer_id -> tenant_id, env_name, computer_name, computer_user, |
| 40 | | computer_ip, computer_os, first_seen, last_seen, sysmon_available |
| 41 | | FD8: process_id -> computer_id, pid, process_name, cpu_percent, memory_mb, |
| | 44 | FD6: (tenant_id, env_name) -> save_process_history, save_metrics_history, |
| | 45 | save_network_history, es_created_at, es_updated_at |
| | 46 | FD7: network_id -> tenant_id, env_name, network_name, cidr, gateway_ip, vlan |
| | 47 | FD8: group_id -> tenant_id, group_name, group_description, parent_group_id |
| | 48 | FD9: computer_id -> tenant_id, env_name, computer_name, computer_user, |
| | 49 | computer_ip, computer_os, first_seen, last_seen, |
| | 50 | sysmon_available, network_id, group_id |
| | 51 | FD10: device_id -> network_id, device_name, device_type, device_ip, mac, device_status |
| | 52 | FD11: process_id -> computer_id, pid, process_name, cpu_percent, memory_mb, |
| 86 | | Применуваме FD1 (user_id): |
| 87 | | K+ = K+ U { user_email, user_name, user_picture, user_created_at } |
| 88 | | |
| 89 | | Применуваме FD4 (env_id): |
| 90 | | K+ = K+ U { tenant_id, env_name, env_created_at } |
| 91 | | |
| 92 | | Применуваме FD5 (env_token_id): |
| 93 | | K+ = K+ U { token, et_created_at, expires_at } |
| 94 | | (tenant_id, env_name веќе се во K+) |
| 95 | | |
| 96 | | Применуваме FD8 (process_id): |
| 97 | | K+ = K+ U { computer_id, pid, process_name, cpu_percent, memory_mb, |
| 98 | | proc_username, cmdline, proc_timestamp } |
| 99 | | |
| 100 | | Применуваме FD9 (history_id): |
| 101 | | K+ = K+ U { cpu_usage, ram_usage, disk_usage, |
| 102 | | network_sent_mb, network_recv_mb, hist_timestamp } |
| 103 | | |
| 104 | | Применуваме FD10 (net_conn_id): |
| 105 | | K+ = K+ U { local_address, remote_address, status, |
| 106 | | nc_process_name, nc_timestamp } |
| 107 | | |
| 108 | | Применуваме FD11 (alert_id): |
| 109 | | K+ = K+ U { alert_type, severity, description, alert_timestamp, resolved } |
| 110 | | |
| 111 | | Применуваме FD12 (sysmon_event_id): |
| 112 | | K+ = K+ U { event_id, event_type, message, sysmon_timestamp, details } |
| 113 | | |
| 114 | | Сега computer_id е веќе во K+ -> применуваме FD7: |
| 115 | | K+ = K+ U { computer_name, computer_user, computer_ip, computer_os, |
| 116 | | first_seen, last_seen, sysmon_available } |
| 117 | | |
| 118 | | Сега (tenant_id, env_name) се двете во K+ -> применуваме FD6: |
| 119 | | K+ = K+ U { save_process_history, es_created_at, es_updated_at } |
| 120 | | |
| 121 | | Сега (user_id, tenant_id) се двете во K+ -> применуваме FD3: |
| 122 | | K+ = K+ U { role, membership_created_at } |
| | 110 | FD1 (user_id): + user_email, user_name, user_picture, user_created_at |
| | 111 | FD4 (env_id): + tenant_id, env_name, env_created_at |
| | 112 | FD5 (env_token_id): + token, et_created_at, expires_at |
| | 113 | FD10 (device_id): + network_id, device_name, device_type, device_ip, mac, device_status |
| | 114 | FD11 (process_id): + computer_id, pid, process_name, cpu_percent, memory_mb, |
| | 115 | proc_username, cmdline, proc_timestamp |
| | 116 | FD12 (history_id): + cpu_usage, ram_usage, disk_usage, |
| | 117 | network_sent_mb, network_recv_mb, hist_timestamp |
| | 118 | FD13 (net_conn_id): + nc_pid, local_address, remote_address, conn_status, |
| | 119 | nc_process_name, nc_timestamp |
| | 120 | FD15 (availability_id):+ service_id, is_available, response_time_ms, sa_timestamp |
| | 121 | FD16 (alert_id): + alert_type, severity, description, alert_timestamp, resolved |
| | 122 | FD17 (sysmon_event_id):+ event_id, event_type, message, sysmon_timestamp, details |
| | 123 | |
| | 124 | Сега network_id e во K+ -> FD7: |
| | 125 | + network_name, cidr, gateway_ip, vlan |
| | 126 | Сега service_id e во K+ -> FD14: |
| | 127 | + service_name, port, protocol, service_status, last_checked |
| | 128 | Сега computer_id e во K+ -> FD9: |
| | 129 | + computer_name, computer_user, computer_ip, computer_os, |
| | 130 | first_seen, last_seen, sysmon_available, group_id |
| | 131 | Сега group_id e во K+ -> FD8: |
| | 132 | + group_name, group_description, parent_group_id |
| | 133 | Сега (tenant_id, env_name) се двете во K+ -> FD6: |
| | 134 | + save_process_history, save_metrics_history, |
| | 135 | save_network_history, es_created_at, es_updated_at |
| | 136 | Сега (user_id, tenant_id) се двете во K+ -> FD3: |
| | 137 | + role, membership_created_at |
| 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 -> ...), потоа редоследно и останатите |
| 181 | | |
| 182 | | Резултат по декомпозицијата: |
| 183 | | |
| 184 | | {{{ |
| 185 | | R1: Users(user_id, user_email, user_name, user_picture, user_created_at) |
| 186 | | FD: user_id -> сите останати |
| 187 | | Candidate key: {user_id} PK: user_id |
| 188 | | |
| 189 | | R2: Tenants(tenant_id, tenant_name, owner_email, tenant_created_at) |
| 190 | | FD: tenant_id -> сите останати |
| 191 | | Candidate key: {tenant_id} PK: tenant_id |
| 192 | | |
| 193 | | R3: Memberships(user_id, tenant_id, role, membership_created_at) |
| 194 | | FD: (user_id, tenant_id) -> role, membership_created_at |
| 195 | | Candidate key: {user_id, tenant_id} PK: (user_id, tenant_id) |
| 196 | | |
| 197 | | R4: Environments(env_id, tenant_id, env_name, env_created_at) |
| 198 | | FD: env_id -> tenant_id, env_name, env_created_at |
| 199 | | Candidate key: {env_id} PK: env_id |
| 200 | | |
| 201 | | R5: EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at) |
| 202 | | FD: env_token_id -> сите останати |
| 203 | | Candidate key: {env_token_id} PK: env_token_id |
| 204 | | |
| 205 | | R6: EnvSettings(tenant_id, env_name, save_process_history, es_created_at, es_updated_at) |
| 206 | | FD: (tenant_id, env_name) -> save_process_history, es_created_at, es_updated_at |
| 207 | | Candidate key: {tenant_id, env_name} PK: (tenant_id, env_name) |
| 208 | | |
| 209 | | R7: Computers(computer_id, tenant_id, env_name, computer_name, computer_user, |
| 210 | | computer_ip, computer_os, first_seen, last_seen, sysmon_available) |
| 211 | | FD: computer_id -> сите останати |
| 212 | | Candidate key: {computer_id} PK: computer_id |
| 213 | | |
| 214 | | R8: Processes(process_id, computer_id, pid, process_name, cpu_percent, |
| 215 | | memory_mb, proc_username, cmdline, proc_timestamp) |
| 216 | | FD: process_id -> сите останати |
| 217 | | Candidate key: {process_id} PK: process_id |
| 218 | | |
| 219 | | R9: ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage, |
| | 192 | За секоја FD чија лева страна е proper subset на кандидат клучот, тој детерминант заедно со зависните атрибути се издвојува во нова релација. |
| | 193 | |
| | 194 | {{{ |
| | 195 | R1 Users(user_id, user_email, user_name, user_picture, user_created_at) |
| | 196 | PK: user_id |
| | 197 | |
| | 198 | R2 Tenants(tenant_id, tenant_name, owner_email, tenant_created_at) |
| | 199 | PK: tenant_id |
| | 200 | |
| | 201 | R3 Memberships(user_id, tenant_id, role, membership_created_at) |
| | 202 | PK: (user_id, tenant_id) |
| | 203 | |
| | 204 | R4 Environments(env_id, tenant_id, env_name, env_created_at) |
| | 205 | PK: env_id |
| | 206 | |
| | 207 | R5 EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at) |
| | 208 | PK: env_token_id |
| | 209 | |
| | 210 | R6 EnvSettings(tenant_id, env_name, save_process_history, save_metrics_history, |
| | 211 | save_network_history, es_created_at, es_updated_at) |
| | 212 | PK: (tenant_id, env_name) |
| | 213 | |
| | 214 | R7 Networks(network_id, tenant_id, env_name, network_name, cidr, gateway_ip, vlan) |
| | 215 | PK: network_id |
| | 216 | |
| | 217 | R8 ComputerGroups(group_id, tenant_id, group_name, group_description, parent_group_id) |
| | 218 | PK: group_id (parent_group_id -> ComputerGroups.group_id, само-референца) |
| | 219 | |
| | 220 | R9 Computers(computer_id, tenant_id, env_name, computer_name, computer_user, |
| | 221 | computer_ip, computer_os, first_seen, last_seen, sysmon_available, |
| | 222 | network_id, group_id) |
| | 223 | PK: computer_id |
| | 224 | |
| | 225 | R10 NetworkDevices(device_id, network_id, device_name, device_type, |
| | 226 | device_ip, mac, device_status) |
| | 227 | PK: device_id |
| | 228 | |
| | 229 | R11 Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb, |
| | 230 | proc_username, cmdline, proc_timestamp) |
| | 231 | PK: process_id |
| | 232 | |
| | 233 | R12 ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage, |
| 221 | | FD: history_id -> сите останати |
| 222 | | Candidate key: {history_id} PK: history_id |
| 223 | | |
| 224 | | R10: NetworkConnections(net_conn_id, computer_id, pid, local_address, |
| 225 | | remote_address, status, nc_process_name, nc_timestamp) |
| 226 | | FD: net_conn_id -> сите останати |
| 227 | | Candidate key: {net_conn_id} PK: net_conn_id |
| 228 | | |
| 229 | | R11: SecurityAlerts(alert_id, computer_id, alert_type, severity, |
| 230 | | description, alert_timestamp, resolved) |
| 231 | | FD: alert_id -> siте останати |
| 232 | | Candidate key: {alert_id} PK: alert_id |
| 233 | | |
| 234 | | R12: SysmonEvents(sysmon_event_id, computer_id, event_id, event_type, |
| 235 | | message, sysmon_timestamp, details) |
| 236 | | FD: sysmon_event_id -> сите останати |
| 237 | | Candidate key: {sysmon_event_id} PK: sysmon_event_id |
| 238 | | }}} |
| 239 | | |
| 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 со помош на Chasing Алгоритам === |
| 243 | | |
| 244 | | За да докажеме дека декомпозицијата е без загуби (Lossless Join) над повеќе од две релации, конструираме матрица на бркање (Chase Matrix). Колоните ги претставуваат сите глобални атрибути, а редовите соодветствуваат на релациите R1 до R12. |
| 245 | | |
| 246 | | Ако релацијата го содржи атрибутот, во соодветната ќелија се впишува симболот a_j (каде j е индексот на колоната), во спротивно се впишува b_i,j. |
| 247 | | |
| 248 | | Применувајќи ги функционалните зависности последователно врз матрицата, ги изедначуваме b вредностите со соодветните a вредности: |
| 249 | | |
| 250 | | 1. Примена на FD1 (user_id -> ...): Сите редови кои имаат a_user_id (тоа се R1 и R3) ги споделуваат и добиваат вредности за a_user_email, a_user_name, a_user_picture, a_user_created_at. |
| 251 | | 2. Примена на FD4 (env_id -> ...): Бидејќи само R4 го има примитивното env_id, тоа го детерминира tenant_id и env_name во тој ред. |
| 252 | | 3. Примена на FD8 (process_id -> ...): Го изедначува computer_id во сите редови каде постои соодветната релација. |
| 253 | | 4. Примена на 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 карактеристика. |
| 260 | | |
| 261 | | === Чекор 2: 2NF -> 3NF (проверка на транзитивни зависности) === |
| 262 | | |
| 263 | | За секоја R1-R12, се проверува дали не-клучен атрибут зависe од друг не-клучен атрибут (наместо директно од клучот). |
| 264 | | |
| 265 | | {{{ |
| 266 | | R1 (Users): само еден детерминант (user_id) во FD1 -> нема транзитивност |
| 267 | | R2 (Tenants): само еден детерминант (tenant_id) во FD2 -> нема транзитивност |
| 268 | | R3 (Memberships): role, membership_created_at немаат меѓусебна |
| 269 | | зависност -> нема транзитивност |
| 270 | | R4 (Environments): env_name, env_created_at зависат само од |
| 271 | | env_id, не едно од друго -> нема транзитивност |
| 272 | | R5 (EnvTokens): token, expires_at зависат само од |
| 273 | | env_token_id -> нема транзитивност |
| 274 | | R6 (EnvSettings): save_process_history не зависи од друг |
| 275 | | не-клучен атрибут -> нема транзитивност |
| 276 | | R7 (Computers): computer_name, computer_os, ... зависат само |
| 277 | | од computer_id (env_name овде е FK, не |
| 278 | | детерминира ништо друго) -> нема транзитивност |
| 279 | | R8 (Processes): сите атрибути зависат директно од process_id -> нема транзитивност |
| 280 | | R9 (ComputerHistory): сите атрибути зависат директно од history_id -> нема транзитивност |
| 281 | | R10 (NetworkConnections): сите атрибути зависат директно од net_conn_id -> нема транзитивност |
| 282 | | R11 (SecurityAlerts): сите атрибути зависат директно од alert_id -> нема транзитивност |
| 283 | | R12 (SysmonEvents): сите атрибути зависат директно од |
| 284 | | sysmon_event_id -> нема транзитивност |
| 285 | | |
| 286 | | Заклучок: сите R1-R12 се веќе во 3NF по завршување на Чекор 1. |
| | 235 | PK: history_id |
| | 236 | |
| | 237 | R13 NetworkConnections(net_conn_id, computer_id, nc_pid, local_address, |
| | 238 | remote_address, conn_status, nc_process_name, nc_timestamp) |
| | 239 | PK: net_conn_id |
| | 240 | |
| | 241 | R14 NetworkServices(service_id, computer_id, service_name, port, protocol, |
| | 242 | service_status, last_checked) |
| | 243 | PK: service_id Alternate key: (computer_id, port, protocol) |
| | 244 | |
| | 245 | R15 ServiceAvailability(availability_id, service_id, is_available, |
| | 246 | response_time_ms, sa_timestamp) |
| | 247 | PK: availability_id |
| | 248 | |
| | 249 | R16 SecurityAlerts(alert_id, computer_id, alert_type, severity, |
| | 250 | description, alert_timestamp, resolved) |
| | 251 | PK: alert_id |
| | 252 | |
| | 253 | R17 SysmonEvents(sysmon_event_id, computer_id, event_id, event_type, |
| | 254 | message, sysmon_timestamp, details) |
| | 255 | PK: sysmon_event_id |
| | 256 | }}} |
| | 257 | |
| | 258 | FD Preservation: секоја од оригиналните FD1-FD17 директно се содржи во точно една |
| | 259 | нова релација (R1<-FD1, ..., R17<-FD17), плус алтернативниот клуч на NetworkServices |
| | 260 | се чува во R14 => сите зависности се зачувани. |
| | 261 | |
| | 262 | === Формален доказ за Lossless Join (Chasing алгоритам) === |
| | 263 | |
| | 264 | Конструираме матрица на бркање (Chase Matrix): колоните се сите глобални атрибути, |
| | 265 | редовите се релациите R1-R17. Ако релацијата го содржи атрибутот, во ќелијата се |
| | 266 | впишува a_j, инаку b_i,j. Ги применуваме FD-ите и ги изедначуваме b со a вредности: |
| | 267 | |
| | 268 | 1. FD11/FD12/FD13/FD16/FD17 (примитивни клучеви process_id/history_id/net_conn_id/ |
| | 269 | alert_id/sysmon_event_id) го изедначуваат computer_id во сите нивни редови. |
| | 270 | 2. FD15 (availability_id) го изедначува service_id, а FD14 потоа го носи computer_id. |
| | 271 | 3. FD10 (device_id) го изедначува network_id. |
| | 272 | 4. FD9 (computer_id -> ...) кај сите редови што содржат computer_id (R9..R17) ги |
| | 273 | претвора b вредностите за атрибутите на компјутерот, вклучувајќи tenant_id, |
| | 274 | env_name, network_id и group_id, во a симболи. |
| | 275 | 5. FD7 (network_id) и FD8 (group_id) ги пропагираат мрежните и организациските |
| | 276 | атрибути како a симболи. |
| | 277 | 6. Клучен чекор: бидејќи по трансформациите редовите R4, R5, R6, R7, R9 сега имаат |
| | 278 | заеднички a_tenant_id и a_env_name, со FD6 овие атрибути (знамињата за историја) |
| | 279 | се пропагираат како a симболи низ сите нив. |
| | 280 | |
| | 281 | Бидејќи почетниот кандидат клуч е составен токму од независните примитивни клучеви, |
| | 282 | чии релации се спојуваат преку соодветните странски клучеви, на крајот се генерира |
| | 283 | целосен ред составен само од a симболи. Ова докажува дека декомпозицијата е |
| | 284 | Lossless Join. |
| | 285 | |
| | 286 | === Чекор 2: 2NF -> 3NF (транзитивни зависности) === |
| | 287 | |
| | 288 | За секоја R1-R17 се проверува дали не-клучен атрибут зависи од друг не-клучен атрибут. |
| | 289 | |
| | 290 | {{{ |
| | 291 | R1..R5, R7, R9..R17: секоја има единствен детерминант (сурогат/композитен PK); |
| | 292 | сите не-клучни атрибути зависат директно од целиот клуч. |
| | 293 | R6 (EnvSettings): трите знамиња зависат само од (tenant_id, env_name), не едно |
| | 294 | од друго -> нема транзитивност. |
| | 295 | R8 (ComputerGroups):parent_group_id е FK (не детерминира ништо во R8) -> нема |
| | 296 | транзитивност. |
| | 297 | R9 (Computers): network_id/group_id/env_name се FK-ови и не детерминираат |
| | 298 | други атрибути во R9 -> нема транзитивност. |
| | 299 | R14 (NetworkServices): постои алтернативен клуч (computer_id,port,protocol), но тоа е |
| | 300 | клуч (superkey), не не-клучен детерминант -> нема транзитивност. |
| | 301 | |
| | 302 | Заклучок: сите R1-R17 се веќе во 3NF по завршување на Чекор 1. |
| 291 | | За BCNF, за секоја нетривијална FD X -> Y во релацијата, X мора да е superkey. |
| 292 | | |
| 293 | | {{{ |
| 294 | | R1: единствена FD е user_id -> ... user_id е PK -> superkey BCNF OK |
| 295 | | R2: единствена FD е tenant_id -> ... tenant_id е PK -> superkey BCNF OK |
| 296 | | R3: единствена FD е (user_id,tenant_id)->... тоа е PK -> superkey BCNF OK |
| 297 | | R4: единствена FD е env_id -> ... env_id е PK -> superkey BCNF OK |
| 298 | | R5: единствена FD е env_token_id -> ... env_token_id е PK -> superkey BCNF OK |
| 299 | | R6: единствена FD е (tenant_id,env_name)->.. тоа е PK -> superkey BCNF OK |
| 300 | | R7: единствена FD е computer_id -> ... computer_id е PK -> superkey BCNF OK |
| 301 | | R8: единствена FD е process_id -> ... process_id е PK -> superkey BCNF OK |
| 302 | | R9: единствена FD е history_id -> ... history_id е PK -> superkey BCNF OK |
| 303 | | R10: единствена FD е net_conn_id -> ... net_conn_id е PK -> superkey BCNF OK |
| 304 | | R11: единствена FD е alert_id -> ... alert_id е PK -> superkey BCNF OK |
| 305 | | R12: единствена FD е sysmon_event_id -> ... sysmon_event_id е PK -> superkey BCNF OK |
| 306 | | |
| 307 | | Заклучок: сите R1-R12 се веќе во BCNF. |
| 308 | | Декомпозицијата завршува по еден единствен чекор (1NF -> 2NF), бидејќи |
| 309 | | истовремено ги отстранивме сите парцијални И транзитивни зависности. |
| | 307 | За BCNF, за секоја нетривијална FD X -> Y, X мора да е superkey. |
| | 308 | |
| | 309 | {{{ |
| | 310 | R1..R13, R15..R17: единствената детерминанта во секоја е нејзиниот PK -> superkey. BCNF OK |
| | 311 | R14 (NetworkServices): две нетривијални зависности - |
| | 312 | service_id -> ... (PK, superkey) BCNF OK |
| | 313 | (computer_id,port,protocol) -> service_id (алтерн. клуч, superkey) BCNF OK |
| | 314 | |
| | 315 | Заклучок: сите R1-R17 се во BCNF. |
| | 316 | Декомпозицијата завршува по еден чекор (1NF -> 2NF), бидејќи истовремено ги |
| | 317 | отстранивме сите парцијални И транзитивни зависности. |
| 336 | | sysmon_timestamp, details) |
| 337 | | }}} |
| 338 | | |
| 339 | | Сите 12 релации се во BCNF, со зачувани функционални зависности (FD preservation) и без губење на податоци при join (lossless join). |
| 340 | | |
| 341 | | === Дискусија: споредба со дизајнот од Фаза 2 === |
| 342 | | |
| 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 алгоритмот. |
| | 354 | sysmon_timestamp, details) |
| | 355 | }}} |
| | 356 | |
| | 357 | Сите 17 логички релации се во BCNF, со зачувани функционални зависности (FD |
| | 358 | preservation) и без губење на податоци при join (lossless join). |
| | 359 | |
| | 360 | === Дискусија: споредба со имплементираниот v04 дизајн === |
| | 361 | |
| | 362 | Формалната normalization постапка (од единствена денормализирана универзална |
| | 363 | релација, преку attribute closure и chasing анализа) резултира со дизајн кој е |
| | 364 | речиси идентичен со реалната имплементирана v04 шема. Ова потврдува дека дизајнот |
| | 365 | од почеток бил близу оптимален (BCNF), без непотребна редундантност. |
| | 366 | |
| | 367 | Разлики помеѓу теоретскиот модел и физичката имплементација: |
| | 368 | |
| | 369 | 1. '''Раздвојување тековна/историска состојба.''' Во нормализираниот модел постои |
| | 370 | по една логичка релација: Processes (R11), ComputerHistory (R12) и |
| | 371 | NetworkConnections (R13). Во физичкиот дизајн, секоја од нив е поделена на две |
| | 372 | табели со ИДЕНТИЧНА структура на атрибути: |
| | 373 | * computer_processes_current / computer_processes_history |
| | 374 | * computer_history_current / computer_history |
| | 375 | * network_connections_current / network_connections_history |
| | 376 | Ова НЕ е денормализација, туку свесна архитектурна оптимизација: табелата |
| | 377 | `_current` држи само последен snapshot (брз упис/читање, се препишува), додека |
| | 378 | `_history` расте со милиони записи за ретроспективна анализа. Дали се полни |
| | 379 | историјата се контролира по околина преку знамињата save_process_history, |
| | 380 | save_metrics_history и save_network_history во EnvSettings (R6). Логичката |
| | 381 | структура на атрибутите е идентична и еквивалентна на нормализираните R11/R12/R13. |
| | 382 | |
| | 383 | 2. '''Странски клучеви наместо изведени вредности.''' Атрибутите tenant_id, env_name, |
| | 384 | network_id, group_id, computer_id, service_id - кои во универзалната релација беа |
| | 385 | изведливи (redundant) - во физичкиот дизајн се реализираат како странски клучеви |
| | 386 | што ги реализираат BCNF релациите преку референци (нормален, посакуван резултат). |
| | 387 | |
| | 388 | 3. '''Само-референца.''' ComputerGroups.parent_group_id е странски клуч кон истата |
| | 389 | табела (хиерархија на групи). Тоа не нарушува BCNF - parent_group_id е обичен |
| | 390 | не-клучен атрибут што зависи само од group_id. |
| | 391 | |
| | 392 | 4. '''Алтернативен клуч.''' NetworkServices има уникатност (computer_id, port, |
| | 393 | protocol) покрај сурогат клучот service_id - алтернативен candidate key, во склад |
| | 394 | со BCNF. |
| | 395 | |
| | 396 | Заклучок: имплементираниот v04 дизајн останува дизајнот што ќе се користи во |
| | 397 | следните фази, бидејќи е веќе во BCNF; оваа анализа само формално го потврдува тоа |
| | 398 | преку attribute closure и chasing алгоритмот. |