Skip to content
isdnetworks
Go back

재는 주기와 남기는 주기를 나눴다

장치 수십 대가 1초마다 값을 보냈다. 몇 주 지나니 MySQL 저장 공간이 찼고 값을 덜 재면 놓치는 것이 생긴다.

Table of contents

Open Table of contents

재는 것과 남기는 것을 나눴다

read_temp 는 1초마다 부르되 그 값을 다 보내지는 않기로 했다.

static float sum = 0;
static float min_v = FLT_MAX, max_v = -FLT_MAX;
static int cnt = 0;

void on_tick(void) {
    float t;
    if (read_temp(&t) != 0) return;

    sum += t;
    if (t < min_v) min_v = t;
    if (t > max_v) max_v = t;
    cnt++;

    if (cnt >= REPORT_INTERVAL) {          /* 60초 */
        send_report(sum / cnt, min_v, max_v, cnt);
        sum = 0; min_v = FLT_MAX; max_v = -FLT_MAX; cnt = 0;
    }
}

REPORT_INTERVAL 마다 한 번 보내고 평균과 최소와 최대와 몇 번 쟀는지를 같이 보낸다. 전송량이 60분의 1이 됐다.

자주 재는 것과 잰 것을 전부 저장하는 것은 다른 얘기다. 계속 자주 재되 보내는 것만 묶으면 짧은 변화를 놓치지 않으면서 저장량은 크게 준다.

평균만으로는 부족했다

sum / cnt 만 보내면 그 안의 변화가 사라진다. 30초 동안 60도였다가 30초 동안 0도여도 평균은 30도다.

최소와 최대를 같이 보내니 그 구간에 무슨 일이 있었는지가 보인다.

avg 24.5  min 24.1  max 24.9  n 60    → 안정
avg 30.0  min  0.2  max 59.8  n 60    → 뭔가 있었음

두 번째면 그 구간을 자세히 봐야 한다.

n 60 을 보내는 것도 중요했다. 1분에 60번 재야 하는데 40번이면 뭔가 놓친 것이다.

avg 24.5  min 24.1  max 24.9  n 40    ← 20번 실패

평균값만 보면 정상으로 보이는데 cnt 가 있으니 read_temp 실패가 드러난다. 서버에서 이 값을 감시해서 50 미만이면 알린다.

급변하면 즉시 보냈다

주기를 기다리는 동안 중요한 변화를 놓칠 수 있다.

if (fabsf(t - last_reported) > ALERT_DELTA) {
    send_alert(t);
    last_reported = t;
}

ALERT_DELTA 를 넘으면 주기와 무관하게 보낸다. 정기 보고와 사건 보고를 나눈 것이고 정기는 추세를 보고 사건은 놓치면 안 되는 것을 본다.

저장 기간의 단계

보내는 양을 줄여도 temp_1min 에 계속 쌓이므로 오래된 것을 줄였다.

최근 7일    1분 단위 그대로
7~30일     10분 단위로 묶음 (평균/최소/최대)
30일 이후   1시간 단위로 묶음
1년 이후    삭제

최근 것은 자세히 보고 오래된 것은 성기게 본다. 묶는 작업은 crontab 으로 새벽에 돌게 했다.

INSERT INTO temp_10min (device_id, ts, avg_v, min_v, max_v, cnt)
SELECT device_id,
       FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(ts) / 600) * 600),
       AVG(avg_v), MIN(min_v), MAX(max_v), SUM(cnt)
FROM temp_1min
WHERE ts < DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY device_id, FLOOR(UNIX_TIMESTAMP(ts) / 600);

최소와 최대는 묶어도 MINMAX 로 유지된다. 평균은 개수로 가중해야 정확한데 개수가 같으면 단순 평균으로 충분했다.

검증 — 저장량 계산과 빈 구간

바꾸기 전에 계산했다.

장치 40대 × 1초 × 1행 30바이트 = 하루 약 103MB
장치 40대 × 60초 × 1행 40바이트 = 하루 약 2.3MB

REPORT_INTERVAL 을 60으로 두면 45분의 1이고 1년치가 800메가 남짓이면 감당할 수 있었다. 계산을 먼저 하면 어느 주기가 맞는지 정해지고 감으로 정하면 나중에 또 바꾼다.

장치가 꺼져 있으면 그 구간에 자료가 없는데 Chart 화면에서는 선이 이어져 보인다.

if (next.ts - prev.ts > REPORT_INTERVAL * 2) {
    /* 선을 잇지 않는다 */
}

앞뒤 시각 차이가 주기보다 크면 끊어 그렸다. 이어진 선과 끊긴 선은 다른 뜻이고 자료가 없는 구간을 없는 것으로 보여 준다.

정리


Share this post on:

Previous Post
하드웨어에 디버거를 붙이며
Next Post
연구 과제로 개발할 때