안녕하십니까. 좋은아빠되기입니다.

 

어제 오늘 일을 계기로 로드에버리지에 대한 생각이 확 바뀌었습니다.

 

저는 4코어(8스레드) CPU 와 램 16G 정도 되는 단독 서버를 운영중입니다.

디스크는 NVMe SSD에 데이터를 다 넣어 놓고 운영체제 부분은 일반 SSD에 넣어 뒀습니다.

 

예전..

같은 사양의 서버에 HDD로 운영을 할때

 

로드에버리지가 10을 넘으니깐...15, 20, 30, 40까지 마구 마구 치솟아서 결국은 다운되는 사태까지

늘 갔던 적이 있습니다.

서버 셋팅을 제가 한것이라서 아무래도 그게 문제가 있었기도 하겠고

HDD 등의 속도로 인하여 wait가 많이 걸렸던것 같기도 하고.. 이제와서 추론해 보자면 그렀습니다.

 

아무튼

저는 이제껏 8스레드면 로드에버리지가 8을 넘게되면 속도 저하 버벅임이 심해져서 사용에 불편함을 초래한다고 생각했고

 

로드에버리지가 20정도되면 8스레드 상황에서는 거의 다운이나 마찬가지 일꺼라 생각을 했습니다.

 

근데....

111.png

 

어제 저녁과 오늘 낮 상황입니다....

25까지 치솟을때도 서버의 느려짐은 거의 느껴지지 않았습니다.

 

제가 속도를 아주 중요하게 생각해서

 

클릭후 0.8초 이내에 반응하지 않으면 서버 다운같은 느낌이 들어서

사용자들이 안좋게 생각한다는 인식을 가지고 있는데

 

위 상황에서 클릭후 반응은 1.5초 이내로 원활 수준을 지속적으로 계속 유지하더군요..

 

제 경험으로는 로드가 한벗 치솟으면 점점점 올라서 60, 70 계속올라가서 서버가 다운된다는 생각을 가지고 있었는데

어제 오늘 완전히 생각이 바뀌었네요.. ㅎㅎㅎ

 

오늘 까지 확인결과 CPU 점유율이 500% 정도까지 올라 가고 거기서 계속... 버티더군요..

 

아마도 전문가 분의 셋팅의 위력이 아닐까 생각합니다.

 

로드에버리지가 서버 상태의 모든 지표는 아니라는 생각과..

8스레드면 로드에버리지도 한계가 8이란 생각이 바뀌게 되었네요.

 

10년간 고정된 생각이 하루만에 바뀌게 되는군요. ㅎㅎㅎ

  • ?

    로드에버리지는 도대체 뭘까요?

     

    load.png

     

    현재 저런 상황인데..

    전혀 느려짐이나 버벅거림을 못느낍니다....

     

    제 생각이 완전히 잘못되었던거 같네요...

  • ? Lv37

    wa가 0이라는 것은 디스크 성능으로 인한 병목현상이 전혀 없다는 뜻입니다. 로드가 한 번 치솟으면 점점 더 느려지다가 완전히 뻗어버리는 경우는 대개 디스크가 버티지 못해서 그런 건데, NVMe SSD니까 그럴 일은 없는 거죠.

    물론 CPU 점유율이 500% 내외라면 로드애버리지도 5~6, 많아도 7~8 정도를 유지하는 것이 보통이므로 지금처럼 20을 넘어가는 것이 바람직한 상황은 아닙니다. 어디선가 불필요한 대기가 발생하고 있는 거예요. DB의 슬로우 쿼리 기준이 기본값인 10초로 되어 있을 텐데, 만약 또 비슷한 현상이 발생한다면 long_query_time을 1초로 낮춰놓고 걸리는 녀석들을 한번씩 다 점검해볼 필요가 있겠습니다. DB를 재시작할 필요 없이 root 권한으로 SET GLOBAL 명령을 사용해서 잠깐 바꿨다가 원래대로 되돌려 놓을 수 있는 설정입니다.

  • Lv37 ?

    한가지 추론해볼수 있는것이...
    평상시는 DB 쪽에서 Select 작업말고는 거의없습니다.

    insert, update는 1시간에 1개 정도 될까 말까 합니다.

    근데 이게 시험기간 되면 해설을 작성해서 사용자들이 제출을 하니깐..
    Insert 명령이 많아 집니다.
    현재 시간당 100개 insert 작업과
    100개의 update 작업이 동시에 되고 있는 상황인데요..

     

    그외 문제풀고 정답율도 갱신해서 문제를 많이 풀수록

    update 작업도 많아 지거든요. ㅎㅎ

    이게 문제가 되는걸까요?

    날코딩한쪽 문제 같아서 말씀 하신 쿼리 부분 설정해서 한번 찾아보도록 하겠습니다.

  • ? Lv37
    시간당 100개의 insert와 update가 많은 것은 아니예요. InnoDB 테이블이라면 테이블 락이 걸리지도 않을 테고요. (MyISAM은 테이블 락이 커널 CPU 점유율 상승의 주범입니다.) 그러나 InnoDB라도 동시다발적으로 insert나 update가 이루어지면 인덱스 레코드에 락이 걸리기 때문에 크게 느려질 수 있고, 심하면 2개 이상의 쿼리가 서로 락을 걸어놓아서 교착상태에 빠지기도 합니다.

    인덱스를 타지 않는 쿼리 같은 것도 찾아보시고요... 설마 수천 개의 테이블이 있는 DB에서 show tables 쿼리를 쓰고 계신 건 아니겠죠?
  • Lv37 ?
    헐 정답이십니다.....

    방금 1초 해서 확인했는데요...

    SHOW TABLES FROM db명 LIKE 'nd%';

    이런식으로 테이블명으로 뭔가를 구분해서 자료를 뽑는 문장이 많은데 저게 다 1초를 넘기고 문제를 일으키고 있네요..

    당장 수정은 어려운데..

    어떻게 설정을 바꾸어서 show tables 성능을 높일수는 없을까요?
  • ? Lv37

    MySQL/MariaDB에서 많은 수의 테이블을 빨리 검색하는 방법은 없습니다.
    information_schema에서 쿼리로 불러오더라도 인덱스를 타지 않기 때문에 느린 건 똑같아요.
    DB는 폴더, 테이블은 파일에 상응하는 원시적인 시스템이라;;
    수천 개의 파일이 들어 있는 폴더에서 ls 하는 꼴이예요.

    SHOW TABLES 하는 부분을 함수로 만들어 점진적으로 분리하세요.

    예:
    function showTables($prefix) {
    // $prefix로 시작하는 테이블 목록을 배열로 만들어 반환함
    }

    이 함수 내에서 SHOW TABLES 쿼리를 하되, 결과를 어딘가에 캐싱해 놓고 반환하세요.
    XE에서 쓰는 Memcached나 APC가 있다면 그걸 써도 되고, 별도의 파일에 캐싱하셔도 됩니다.

    만약 테이블을 추가하는 기능이 있다면, 추가 후 캐시를 비워주세요.

     

    아마 이것만 해도 서버 부하가 70~90% 줄어들 거라고 예상해 봅니다... ㅎㅎ

  • Lv37 ?
    대략 말씀 해주신 부분이 이해는 갑니다.

    근데..

    "이 함수 내에서 SHOW TABLES 쿼리를 하되, 결과를 어딘가에 캐싱해 놓고 반환하세요."

    캐싱은 어떻게 해야 하는건지.....제가 해야 하는건가요?

    아님

    Memcached나 APC

    여기서 자동으로 해주는 것인가요? ㅎㅎㅎ

    무지해서..
  • Lv37 ?
    우선...

    function showTables($link_id, $default_dbname,$dbname){

    $prefix=mysqli_query($link_id, "SHOW TABLES FROM ".$default_dbname." LIKE '".$dbname."%'");

    return $prefix;
    }

    이렇게 함수를 구성했는데요.. 캐싱하는법을 모르겠습니다...
    자동으로 캐싱이 되는건지....
  • ? Lv37

    자동은 아니죠... 캐싱하는 로직을 직접 구현해야지요. 캐시에서 데이터를 불러오고, 데이터가 있으면 그대로 반환하고, 없으면 DB에서 불러온 후 캐시에 저장하여 다음 번에 쓸 수 있도록 준비하고...

    사실 어플리케이션 작동에 필요한 정보를 "테이블 목록"에 저장해 둔다는 것부터가 이상한 발상입니다. 중요한 정보는 "테이블 안"에 저장해 놓아야지요. 해당 정보를 어디 그냥 배열로 만들어 두거나, 목록을 저장해 두는 테이블을 아예 따로 만들어서 적당히 인덱스를 걸어 놓는다면 별도의 캐싱은 필요없을 겁니다. 님 상황에서는 후자가 가장 자연스러울 것 같네요. 테이블 추가/삭제시 "목록 저장용 테이블"에도 마찬가지로 insert/delete하도록 하고, 그 밖의 SHOW TABLES는 모두 SELECT로 교체하면 될 테니까요. (이것도 일종의 캐싱이라고 할 수 있습니다. 불러오는 데 오래 걸리는 정보의 사본을 만들어 두는 거지요.)

  • Lv37 ?
    네 말씀 하신것처럼 구현하고 있었습니다.
    show tables 결과를 하나의 테이블에 넣어 두고..
    필요한 목록을 select로 빼내오는 방법을 쓰려구요..
    아.. 엄청난 실수를 해서...
    사서 고생이네요 ㅋㅋㅋ
  • Lv37 ?

    c1.png

    c2.png

    c3.png

    c4.png

    c5.png

     

    12시가 넘어서 사용자가 조금 빠진뒤 구현해서 수치가 크지는 않네요.

     

    로드에버리지가 11시까지만해도 61을 지속적으로 기록중이었는데.

     

    show tables 결과를 테이블 한곳으로 몰아서 거기에 쿼리 날려서 데이터를 가져 왔더니

    획기적으로 줄어드네요. ㅎㅎ

     

    역시 모르면 물어야 하고 배워야 합니다.

     

    도움 감사합니다.

     

    글 다적고 보니

     

    3.5 기록중입니다... 대단한 수치네요 ㅎㅎㅎ

     

    ps : 땜빵식 응급처리로는

     

    prefix=mysqli_query($link_id, "SELECT table_name FROM Information_schema.tables WHERE table_schema

     

    이런식으로 사용하니깐 되기는하는데..Like %를 써야 해서 큰 도움은 안되네요.

     

    테이블 만들때 테이블명 앞 2자를 따로 저장해서 인덱스 거는게 훨신 빠르네요 ㅎㅎㅎ

  • ? Lv37
    LIKE '앞글자%' 형태의 쿼리는 인덱스를 탈 수 있습니다. 'LIKE %중간%' 이건 안되고요.
    문제가... information_schema.tables 테이블에는 인덱스가 없습니다 ㅋㅋㅋ
  • Lv37 ?
    아 인덱스가 없어스 그렇군요
    인덱스만 있음 한줄 수정하는게 더편한데 아쉽네요 ㅋㅋ
  • Lv8
    안느려질 순 있지만 좋은 상황은 아닙니다.
  • Lv8 ?
    좋은 상황은 아니죠. ㅎㅎ

    제가 날코딩한 DB쪽 테이블이 과도해서 생긴것 같다고 기진님곰님께서 추론해 주셨는데 당장 저걸 바꿀수가 없어서요...

    일단은 참 신기하네요 ㅎㅎㅎ