본문 바로가기

Posts/Python

Python | Django Interview Q&A List

반응형

간단히 내용을 요약했기에 부족한 부분은 따로 찾아서 학습할 예정이며 지속적으로 업데이트 할 예정입니다. 보시는 분들도 참고해주시면 감사하겠습니다.

Python GIL이 무엇이고, 왜 성능 문제가 발생하는가?

GIL을 알기 전에 파이썬에서 멀티쓰레드를 먼저 이해해야합니다.

import logging
import random
import threading
import time

if __name__ == "__main__":
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt="%H:%M:%S")


def working(name):
    logging.info("Thread %s: starting", name)
    # key point :)
    # time.sleep(1)
    max([random.random() for _ in range(50000000)])
    logging.info("Thread %s: finishing", name)


# 1 Thread
s_time = time.time()
working(0)
working(1)
e_time = time.time()
print(f'{e_time - s_time:.5f}')

# 2 Threads
s_time = time.time()
threads = []
for i in range(2):
    logging.info("Main    : create and start thread %d.", i)
    threads.append(threading.Thread(target=working, args=(i,)))
    threads[-1].start()

for t in threads:
    t.join()

e_time = time.time()
print(f'{e_time - s_time:.5f}')

1번 쓰레드와 2번 쓰레드를 실행한 결과는 2번 쓰레드가 성능 효율이 떨어지는 것을 확인할 수 있습니다.
파이썬은 기본적으로 싱글쓰레드로 동작합니다.
파이썬 인터프리터가 한 스레드하나의 바이트코드를 실행시키도록 동작하는걸 GIL(Global Interpreter Lock)이라 부릅니다.

하나의 쓰레드만 동작하도록 허용하고 나머지는 Lock을 걸어 쓰레드를 실행할 수 없게 막는 것을 의미합니다.
이때 다중 쓰레드를 사용하게 되면 병렬적으로 동작할 것을 기대하지만 사실 GIL로 인해 쓰레드의 동작이 멈추게 되고
이때 context switch(작업이 진행중인 상태에서 현 상태를 보관 후 다른 상태로 적재하는 것을 의미)
비용도 발생하게 되어 오히려 더 성능적으로 비효율적인 상황이 발생하게 됩니다.

GIL 해결 방안

sleep() 함수를 통해 멀티쓰레드를 효율적으로 사용할 수 있습니다.
싱글쓰레드에선 sleep으로 인해 동작을 멈추고 대기를 하는 반면
멀티쓰레드에선 context switch의 효율성이 좋아져 오히려 성능이 개선이 되는 것을 확인할 수 있습니다.


Model의 seleted_related & prefetch_related

selected_related는 쿼리셋 반환시 OneToOne 또는 fk(외래키) 관계 모델을 함께 실행하는 ORM이고
prefetch_related는 seleted_related에서 추가로 ManyToMany 또는 ManyToOne 관계 모델을 가져오는 것을 말합니다.
추가로 selected_related는 inner join으로 쿼리셋을 가져오며 캐싱으로 인해 조금 더 효율적으로 쿼리셋을 불러올 수 있습니다.


List

  • 여러 데이터 동시 저장 가능한 자료구조 []
  • 인덱스로 특정요소 접근 가능
  • mutable (데이터 변경 가능)

Tuple

  • 여러 데이터 동시 저장 가능한 자료구조 ()
  • 인덱스로 특정요소 접근 가능
  • immutable (데이터 변경 불가능)
  • 리스트보다 조금 속도 빠름

Dict

  • key : value 로 이루어진 자료구조 {}
  • 하나의 키값엔 하나의 value만 대응
  • value는 변경 가능하나 key값은 변경 불가

Set

  • 집합 자료형
  • 순서가 없고 모든 데이터는 unique (중복 허용 x)
  • 중괄호 사용하는 표현 방식은 dict과 같지만 key없이 value만 존재
  • list, dic, tuple로 변환해서 사용

Generator

일반적으로 함수는 return을 만날때까지 혹은 함수내에 마지막 줄까지 실행됩니다.
이때 호출이 종료되는 순간 내부 함수에 선언된 함수나 변수들은 메모리를 반환하게 됩니다.
함수를 다시 재호출해도 해당 행위는 다시 한번 중복으로 발생하게 되고
이러한 점을 개선하기 위해 함수 자체가 흐름을 기억한 후 재호출시 기억한 부분부터 사용할 수 있도록 구현한 것이 제네레이터입니다.


Explain

database에서 튜닝중 가장 중요한 것은 쿼리 최적화입니다.

db 설계는 한번 진행되면 테이블과 관계된 모든 테이블의 쿼리에 영향을 주기 때문에 변경하기 힘들지만

쿼리의 경우는 개별적으로 수정할 수 있는 장점을 가지고 있습니다. Slow Query를 제거하는 것은 성능 향상에 매우 도움이되며

애플리케이션의 성능을 개선하고 싶을 때 가장 먼저 접근하여 살펴볼 필요가 있습니다.

이때 사용하게 되는 것이 Explain으로 작성한 쿼리의 과정을 알아보기 쉽게 결과로 나타나게 됩니다. 이를 통해 성능 분석, 인덱스 전략 수립등 최적화에 관련된 업무를 처리할 수 있습니다.


MySQL Explain 사용 방법

EXPLAIN [extended] SELECT ... FROM ... WHERE

구분 설명
id select 아이디로 SELECT를 구분하는 번호
table 참조 테이블
select_type select 타입
type 조인 or 조회 타입
possible_keys 테이터를 조회시 db에서 사용할 수 있는 인덱스 리스트
key 실제 사용할 인덱스
key_len 실제 사용할 인덱스 길이
ref key 안에 인덱스와 비교하는 컬럼값
rows 쿼리 실행시 확인하는 행의 갯수

이때 다른 것들도 중요하지만 type을 가장 눈여겨 확인해야합니다.

구분 설명
system 테이블에 하나의 데이터만 존재시
const select에서 pk 혹은 unique를 실수로 조회하는 경우
eq_ref 조인할 때 pk
ref 조인할 때 pk or unique가 아닌 key로 매핑하는 경우
ref_or_null ref와 동일한 조건에 null이 추가되어 검색되는 경우
index_merge 두개의 인덱스가 병합되어 검색이 되는 경우
range 특정 범위내 인덱스를 사용해 원하는 데이터를 추출하는 경우
index 인덱스를 처음부터 끝까지 검색하는 경우로 인덱스 풀스캔
all 테이블을 처음부터 끝까지 검색하는 경우로 테이블 풀스캔 (최악)

Django의 inner join 과 outer join 선택 기준

  • model.ForeignKey(null=False) 이면 Inner Join
  • model.ForeignKey(null=True) 이면 Left Outer Join
null=True not used inner join?

애초에 불가능해야합니다. null=True인 엔티티를 QuerySet이 inner join으로 조회한다면 join되는

테이블쪽이 null일때 sql 결과에서 누락이 되기 때문입니다. 해결 방안으로는 크게 2가지가 존재합니다.

Filter(isnull=False)
  • 앱이름.objects.filter(속성명__isnull=False)
Model의 ForeignKey(null=False) 수정
  • 앱에서 코드 수정만 하고 마이그레이션을 하지 않으면 실제 Table은 null=True지만 QuerySet조회시 inner join

웹 서비스 응답이 느린 원인 및 해결책

원인
  • 슬로우 쿼리
  • 느린 라우팅
  • cpu 리소스 부족
  • 메모리 부족
  • 많은 http 호출
해결책
  • 애플리케이션 로직 확인 후 서버 응답시간 200ms 아래로 줄이기

  • http 요청 줄이기

  • 쿼리 최적화

  • 캐시서버 사용

  • static file 스프라이트

    • 여러개 이미지를 하나로 모아 파일의 갯수를 줄이는 기법
  • CSS Minify

  • CDN 사용 (효율적 콘텐츠 제공)

  • 캐시 만료일 설정


단위 테스트, 통합 테스트

단위 테스트

단위 테스트는 하나의 모듈을 기준으로 독립적 실행되는 가장 작은 단위의 테스트를 말합니다.

모듈이란 의미는 애플리케이션에서 실행되는 하나의 기능 또는 메소드로 이해할 수 있습니다.

결과적으로 단위테스트는 애플리케이션에서 하나의 기능이 올바르게 동작하는지 독립적 테스트를 하는 것으로

어떤 기능을 실행시 어떤 결과를 기대하는지 정도로 테스트를 진행합니다.

통합 테스트

모듈을 통합하는 과정에서 모듈간 호환성을 확인하기 위한 테스트를 말합니다.

일반적으로 애플리케이션은 여러 모듈들로 구성되고 서로 함수를 호출하며 실행됩니다.

여러 관계를 맺은 모듈들이 올바르게 동작하는지 검증이 필요한데 이러한 목적으로 진행되는 테스트가 통합테스트입니다.

한마디로 하나의 서비스를 이용할 때 API를 호출하고 이후 동작이 정상적으로 작동하는지 확인하는 작업입니다.

단위 테스트 필요성
  • 테스트에 대한 시간과 비용 절감
  • 새 기능 추가시 빠르게 테스트 가능
  • 리팩토링시 안전성 확보
  • 코드에 대한 문서 역할

반응형