인턴중에 , 크롤링한 데이터를 정제후 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에서 실행 되기 때문에 더 효율적
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
잦은 호출을 피하고 , 일괄 업데이트 권장
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
공부중...
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 서버에서 강제로 디스크 쓰기가 포함되며 대부분 경우 클라이언트는 서버에서 데이터가 기록됨을 확인할 때까지 차단
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)
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()