sqlalchemy performance tuning[수정중]

jaeyoung0509·2022년 1월 23일
0

db

목록 보기
2/2

인턴중에 , 크롤링한 데이터를 정제후 db에 넣기위해 sqlalchemy orm 을 사용했는데 무지성으로 쓰다가 공부할 시간이 생겨서 글을 쓰기로 했습니다.

위 글은 해당 글들을 참조해서 만들어 졌습니다

SQLAlchemy Performance Anti-Patterns and Their Fixes
SQLAlchemy Anti-Patterns

단순히 쿼리 결과를 카운트할때

anti pattern

count = len(User.query.filter_by(acct_active=True).all()) 

appropriate pattern

count = user.query.filter_by(acct_active=True).count()

why

  • all()메소드를 사용하게 되면 , 모델객체의 인스턴스가 발생하고 data row가 많은 경우 더 많은 비용 , 더 많은 시간 소요
  • count는 orm을 통해 query로 변경되어 db에서 실행 되기 때문에 더 효율적

특정 column만 필요로 할때

anti pattern

result = User.query.all()
for user in reuslt : print(user.name , user.email)

appropriate pattern

result = User.query.with_entities(User.name , User.email).all()
for(username,  email) in result: print(username , email)

하나의 객체(여러 데이터)를 업데이트 시킬 때

  • anti pattern

for user in user_to_update:
    user.acct_active = True
    db.session.add(user)
  • appropriate pattern

query = User.query.filter(user.id.in_([user.id for user in users_to_update]))
query.update({"acct_activate" : True} , synchronize_session = False)
  • why
    잦은 호출을 피하고 , 일괄 업데이트 권장

casecade 삭제 트리거

  • anti pattern

Class Artist(Base):
    __tablename__ = "artist"
    id = Column(Integer , primary_key = True)
    songs = relationship("song" , casecade = "all ,delete")
Class Song(Base):
    __tablename__ = "song"
    id = Column(Integer, primary_key = True)
    artist_id = Column(Integer , ForeginKey("artist_id" , ondelete ="CASECADE"))
  • appropriate pattern

songs = relationship("song" ,casecade = "all ,delete" , passive_deletes = True)
  • why
    공부중...

commit or flush 조기 호출

  • anti pattern

artist = Artist(name = "Bob")
song = Song(title = "Mr man")
db.session.add(artist)
db.session.flush()
song.artist_id = artis.id
  • appropriate pattern

artist = Artist(name = "Bob")
song = Song(title="Mr man")
artist.song.append(song)
  • why:

    요청당 두번 이상커밋하거나 플러시하는 것은 일반적으로 불필요 ,
    db 플러시에는 db 서버에서 강제로 디스크 쓰기가 포함되며 대부분 경우 클라이언트는 서버에서 데이터가 기록됨을 확인할 때까지 차단

대량의 record를 가져올 때

  • anti pattern

form sqlalchemy.orm import joinedload
artists =Artist.query.options(joinedload(Artist.songs))
print(artists.songs)
  • appropriate pattern

batch = []
insert_stmt = Song.__table__.insert()
for song in songs:
    if len(batch) > 1000:
        db.session.execute(insert_stmt , batch)
        batch.clear()
    batch.append(song)
if batch:
    db.session.execute(insert_stmt , batch) 

Abusing lazily loaded relationships

  • anti pattern

Class Customer(Base):
    @property
    def has_valid_tosast(self):
        return any(toast.kind == 'brioche' for toast in self.toaster.toasts)
  • appropriate pattern

Class Customer(Base):
    @proprety
    def has_valid_toast(self):
        query = (session.query(Toaster).join(Toast).with_parent(self).filter(Toast.kind =='brioche'))
        return session.query(query.exists()).scalar()
profile
매일 한걸음씩 걷자

0개의 댓글