오늘도 개발

update와 bulk_update 본문

웹 프로그래밍/Django

update와 bulk_update

Sueeeeeee 2023. 2. 27. 15:43

Writer 인스턴스의 id가 2, 4, 6, 8, 10인 것만 policy=True로 설정된 상태.

policy 필드가 True인 인스턴스의 policy_second 필드도 True로 업데이트하는 상황 가정.

 

1. iteration과 save 사용

업데이트 대상이 100개라면 UPDATE문이 100번 실행됨.

실행 속도가 느리고, 첫줄에서부터 save()를 실행할 때 까지 데이터에 변경이 일어날 수 있다는 것이 단점.

수많은 row에 한꺼번에 락을 걸지 않는다는 것이 장점.

writers = Writer.objects.filter(policy=True)
for writer in writers:
   writer.policy_second = True
   writer.save()
SELECT "writers"."id", "writers"."description", "writers"."policy", "writers"."policy_second" FROM "writers" WHERE "writers"."policy"; args=(); alias=default
UPDATE "writers" SET "description" = 'abc', "policy" = 1, "policy_second" = 1 WHERE "writers"."id" = 2; args=('abc', True, True, 2); alias=default
UPDATE "writers" SET "description" = 'abc', "policy" = 1, "policy_second" = 1 WHERE "writers"."id" = 4; args=('abc', True, True, 4); alias=default
UPDATE "writers" SET "description" = 'abc', "policy" = 1, "policy_second" = 1 WHERE "writers"."id" = 6; args=('abc', True, True, 6); alias=default
UPDATE "writers" SET "description" = 'abc', "policy" = 1, "policy_second" = 1 WHERE "writers"."id" = 8; args=('abc', True, True, 8); alias=default
UPDATE "writers" SET "description" = 'abc', "policy" = 1, "policy_second" = 1 WHERE "writers"."id" = 10; args=('abc', True, True, 10); alias=default

 

2. update 사용

UPDATE.. WHERE..문이 한 번만 실행됨.

WHERE로 잡힌 업데이트 대상이 100개라면 100개에 락이 걸림.

실행 속도가 빠르고 실행 중 데이터가 변경될 염려 없음.

업데이트 대상이 되는 row의 개수가 적을 때만 추천.

Writer.objects.filter(policy=True).update(policy_second=True)
UPDATE "writers" SET "policy_second" = 1 WHERE "writers"."policy"; args=(True,); alias=default

 

3. bulk_update 사용

1, 2번 절충 가능.

batch_size로 한 번에 업데이트할 row의 개수 지정할 수 있다.

업데이트 대상이 100개이고 batch_size를 20으로 지정하면

 - 20개 row에 락을 걸고

 - UPDATE를 실행하고

 - 락 해제

과정을 5번 반복한다. 

 

(Queryset.update()에는 트랜잭션이 적용된다.)

writers = Writer.objects.filter(policy=True)
writers_to_update = []

for writer in writers:
    writer.policy_second = True
    writers_to_update.append(writer)
    
Writer.objects.bulk_update(writers, ['policy_second'], batch_size=2)
# writer 인스턴스를 2개씩 끊어 업데이트

for writer in writers 시 호출

SELECT "writers"."id", "writers"."description", "writers"."policy", "writers"."policy_second" FROM "writers" WHERE "writers"."policy"; args=(); alias=default

Writer.objects.bulk_update(writers_to_update, ['policy_second'], batch_size=2) 시 호출

(0.000) BEGIN; args=None; alias=default
(0.000) UPDATE "writers" SET "policy_second" = CASE WHEN ("writers"."id" = 2) THEN 1 WHEN ("writers"."id" = 4) THEN 1 ELSE NULL END WHERE "writers"."id" IN (2, 4); args=(2, True, 4, True, 2, 4); alias=default
(0.000) UPDATE "writers" SET "policy_second" = CASE WHEN ("writers"."id" = 6) THEN 1 WHEN ("writers"."id" = 8) THEN 1 ELSE NULL END WHERE "writers"."id" IN (6, 8); args=(6, True, 8, True, 6, 8); alias=default
(0.000) UPDATE "writers" SET "policy_second" = CASE WHEN ("writers"."id" = 10) THEN 1 ELSE NULL END WHERE "writers"."id" IN (10); args=(10, True, 10); alias=default