diff --git a/Gemfile.lock b/Gemfile.lock index 4609dc9..2507eea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,16 +4,18 @@ GEM activesupport (3.2.22.5) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) colorator (1.1.0) concurrent-ruby (1.1.4) em-websocket (0.5.1) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) + eventmachine (1.2.7) eventmachine (1.2.7-x64-mingw32) faraday (0.15.4) multipart-post (>= 1.2, < 3) + ffi (1.10.0) ffi (1.10.0-x64-mingw32) forwardable-extended (2.6.0) gemoji (3.0.0) @@ -23,7 +25,7 @@ GEM http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.8.5) + jekyll (3.8.7) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) @@ -63,7 +65,7 @@ GEM ruby_dep (~> 1.2) mercenary (0.3.6) mini_portile2 (2.4.0) - minimal-mistakes-jekyll (4.15.1) + minimal-mistakes-jekyll (4.16.5) jekyll (~> 3.7) jekyll-data (~> 1.0) jekyll-feed (~> 0.10) @@ -74,13 +76,16 @@ GEM jemoji (~> 0.10) multi_json (1.13.1) multipart-post (2.0.0) + nokogiri (1.10.1) + mini_portile2 (~> 2.4.0) nokogiri (1.10.1-x64-mingw32) mini_portile2 (~> 2.4.0) - octokit (4.13.0) + octokit (4.22.0) + faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (3.0.3) + public_suffix (4.0.7) rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) @@ -92,24 +97,23 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) tzinfo (2.0.0) concurrent-ruby (~> 1.0) tzinfo-data (1.2018.9) tzinfo (>= 1.0.0) - wdm (0.1.1) PLATFORMS x64-mingw32 + x86_64-linux DEPENDENCIES jekyll (~> 3.8.5) jekyll-feed (~> 0.6) minimal-mistakes-jekyll tzinfo-data - wdm (~> 0.1.0) BUNDLED WITH 2.0.1 diff --git a/_data/navigation.yml b/_data/navigation.yml index 4b318f5..ebb6bea 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -149,12 +149,12 @@ pop: # - title: "with" # url: /series/principles_of_python/ # -# - title: 고급 파이썬 문법 -# children: + - title: 고급 파이썬 문법 + children: # - title: "데코레이터" # url: /series/principles_of_python/decorator/ # - title: "프로퍼티" # url: /docs/pages/ -# - title: "async await" -# url: /series/principles_of_python/async_await/ + - title: "async await" + url: /series/principles_of_python/async_await/ diff --git a/_posts/2021-08-17-django-1-4-augmented-field.markdown b/_posts/2021-08-17-django-1-4-augmented-field.markdown new file mode 100644 index 0000000..acfef78 --- /dev/null +++ b/_posts/2021-08-17-django-1-4-augmented-field.markdown @@ -0,0 +1,111 @@ +--- +layout: single +title: "S3에 바로 올라가는 ORM 파일 필드 만들기" +date: 2021-08-07 11:10:03 +0900 +categories: [Postgresql, MVCC] +--- + + +## Environment +python 2.7.14 +Django 1.4 (고대 쟝고) +South 1.0.2 +기존 코드를 + + +## 쟝고에 FileField 는 있는데 왜 S3FileField 는 없을까!! + +기존 코드에서는 FileField 를 사용한 뒤, Form 의 save_new() 함수에서 s3 업로드를 진행한 뒤, +S3 경로의 FileField 의 url 을 바꿔치기 하는 방식을 사용하고 있었습니다. + +이 방식을 사용했을 때 + +* 필드가 해야 할 일을 form 이 대신한다. +* column 만 봤을 때는 파일이 s3 에 올라가는지, 로컬(그럴리 없지만)에 저장되는지 알 수 없다. + +라는 문제점이 남게 됩니다. + +```python +# coding=utf-8 +from datetime import datetime + +from django.forms.models import BaseInlineFormSet +from util import upload_image_to_s3 # s3 에 파일을 업로드 하는 util 함수 +from const import UPLOAD_DIRS # S3 버킷 안의 폴더 + +class ImageInlineFormset(BaseInlineFormSet): + + def save_new(self, form, commit=True): + # 기타 여러가지 form set 이 하는 작업들 ... + + # + S3 업로드 작업 + obj = super(ImageInlineFormset, self).save_new(form, commit=False) + image = form.files.get(u'{}-image_url'.format(form.prefix)) + + if image: + filename = u'{:%Y%m%d%H%M%S%f}_{}'.format( + datetime.now(), + image.name, + ) + s3_path = upload_image_to_s3(UPLOAD_DIRS, image, filename) + obj.image_url = s3_path + + if commit: + obj.save() + + return obj +``` + + +## S3FileField +그래서 S3FileField 를 만들었습니다~ + +```python +from datetime import datetime + +from django.db.models import FileField +from south.modelsinspector import add_introspection_rules + +from utils import upload_image_to_s3 + + +class S3FileField(FileField): + + def __init__(self, *args, **kwargs): + super(S3FileField, self).__init__(*args, **kwargs) + + def _upload_to_s3(self, image): + filename = u'{:%Y%m%d%H%M%S%f}_{}'.format( + datetime.now(), + image.name, + ) + return upload_image_to_s3(self.upload_to, image, filename) + + def pre_save(self, model_instance, add): + file = super(FileField, self).pre_save(model_instance, add) + if file and not file._committed: + file.name = self._upload_to_s3(file) + return file + + +add_introspection_rules([], ["^dowant\.promotion\.fields\.S3FileField"]) + +``` + +* pre_save() 를 override 합니다. 여기서 s3 에 업로드 합니다. +* add_introspection_rules 를 추가해야 마이그레이션 파일이 만들어집니다. + + +## 사용 +필드를 만든 후에는 일반 FileField 처럼 사용하면 됩니다. +```python + image = S3FileField( + max_length=300, + upload_to='s3-버킷-안의-폴더/', + storage=FileSystemStorage(location='', base_url='https://s3-bucket-경로'), + ) +``` + + + + diff --git a/_posts/2021-09-19-mysql_char_varchar_text.markdown b/_posts/2021-09-19-mysql_char_varchar_text.markdown new file mode 100644 index 0000000..2580397 --- /dev/null +++ b/_posts/2021-09-19-mysql_char_varchar_text.markdown @@ -0,0 +1,104 @@ +--- +layout: single +title: "Mysql Char vs Varchar vs TEXT 언제 쓰는게 좋을까?" +date: 2021-09-19 11:10:03 +0900 +categories: [Mysql] +--- + +# Char vs Varchar vs TEXT +언제 쓰는게 좋을까? 직접 테스트 해봤습니다. + +# TL; DR + +* (mysql8, utfmb4 에서...) 16000자 이하: varchar < 65535자 이하: TEXT (CHAR 는 왜 쓰는지 모르겠음) +* char 가 varchar 보다 성능이 좋다는 얘기가 있는데, 테스트 해봤을 때 유의미한 차이는 없었습니다. +* varchar 는 저장 공간이 가변이라 disk 낭비가 적으며, index도 됩니다. +* TEXT 는 index 도 안되고 default 값도 안 됩니다만 (^__^) varchar 보다 많이 저장할 수 있습니다. + + +# Char +* 고정 길이 +* 0~255 까지의 길이를 지정할 수 있습니다. (byte 가 아니라 길이) +* 고정 길이 만큼의 disk space 를 차지합니다. +* CHAR(10) 에 'abcd' 를 저장하면, 'abcd' 와 함께 빈 6자리를 채우기 위해 6개의 space (padding) 가 뒤에 채워집니다. +* 채워진 스페이스는 해당 row 를 가져올 때 빼고 가져옵니다. +* PAD_CHAR_TO_FULL_LENGTH 설정이 켜져 있다면 빼지 않고 스페이스도 가져옵니다. +* index 가능 + +# Varchar +* 저장된 문자열 만큼의 disk space 만 차지합니다. +* Char 와 다르게 prefix byte 를 사용합니다. (1~2 byte 남짓을 추가적으로 사용) +* 최대 65535 byte 를 저장합니다. (길이가 아니라 byte) 어떤 character set 을 사용하느냐에 따라 max 길이가 달라집니다. +* 테스트 해보니 (약)16000자가 최대였습니다. (mysql 8.0.25에서 utfmb4 charset 사용) +* index 가능 + +# Text +* varchar 보다 더 큰 값을 가질 수 있습니다. +* index 불가 +* default 값을 가질 수 없습니다. (눈물) +* 테스트 해보니 65535 자를 (길이) 저장할 수 있었습니다. (mysql 8.0.25에서 utfmb4 charset 사용) +* stack overflow 에서 65535 byte 를 저장한다는 글을 많이 볼 수 있었는데, 사실은 byte 가 아니라 글자 수가 65535자 입니다. + + +# VARCHAR 와 Text 길이 테스트 +* python 으로 고정 길이의 문자열을 만듭니다. `'a' * 16125` +* utf8mb4 에서 어떨 때는 16125 보다 긴 컬럼으로 ALTER 할 때 에러 발생, 어떨 때는 16179 보다 길 때 에러 발생... 정확한 기준을 모르겠습니다. +* varchar(16125) 의 경우 정말 `'a' * 16125` 는 할당되지만 `'a' * 16126` 는 할당 안 됬습니다. + + +# CHAR VS VARCHAR 테스트 +애초에 CHAR 의 최대 길이가 255 입니다. 이렇게 작은 값을 read 해 올 때 성능 비교는 의미가 없는 것 같습니다. +500 row 이상을 한 번에 불러오는 경우도 생각해 볼 수 있겠으나... 이건 성능의 문제보다는 pagination 을 안해서 +생기는 문제라고 생각합니다. +* CHAR(200) 과 VARCHAR(200) 을 컬럼을 만듭니다. +``` +create table string_table +( + my_char char(200) null, + my_varchar varchar(16179) null, + my_text text null, + id int auto_increment + primary key +); +``` +* my_char 에 'a' 200 개로 이루어진 문자열을 저장한 컬럼 250개 생성 +* my_carchar 에도 'a' 200 개로 이루어진 문자열을 저장한 컬럼 250개 생성 +* 두 쿼리의 성능 비교 + * `SELECT my_char FROM string_table WHERE my_char IS NOT NULL;` + * `SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL;` + +``` +> SELECT my_char FROM string_table WHERE my_char IS NOT NULL +[2021-09-18 23:07:11] 250 rows retrieved starting from 1 in 48 ms (execution: 8 ms, fetching: 40 ms) +> SELECT my_char FROM string_table WHERE my_char IS NOT NULL +[2021-09-18 23:07:36] 250 rows retrieved starting from 1 in 77 ms (execution: 8 ms, fetching: 69 ms) +> SELECT my_char FROM string_table WHERE my_char IS NOT NULL +[2021-09-18 23:07:37] 250 rows retrieved starting from 1 in 70 ms (execution: 9 ms, fetching: 61 ms) +> SELECT my_char FROM string_table WHERE my_char IS NOT NULL +[2021-09-18 23:07:38] 250 rows retrieved starting from 1 in 59 ms (execution: 9 ms, fetching: 50 ms) +> SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL +[2021-09-18 23:07:51] 252 rows retrieved starting from 1 in 57 ms (execution: 10 ms, fetching: 47 ms) +> SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL +[2021-09-18 23:07:52] 252 rows retrieved starting from 1 in 60 ms (execution: 8 ms, fetching: 52 ms) +> SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL +[2021-09-18 23:07:53] 252 rows retrieved starting from 1 in 51 ms (execution: 9 ms, fetching: 42 ms) +> SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL +[2021-09-18 23:07:54] 252 rows retrieved starting from 1 in 53 ms (execution: 9 ms, fetching: 44 ms) +> SELECT my_varchar FROM string_table WHERE my_varchar IS NOT NULL +[2021-09-18 23:07:54] 252 rows retrieved starting from 1 in 63 ms (execution: 9 ms, fetching: 54 ms) +``` + +결론: `또이또이` + +# Reference + +https://stackoverflow.com/questions/25300821/difference-between-varchar-and-text-in-mysql/25301046#25301046 + +https://dev.mysql.com/doc/refman/8.0/en/char.html + + + + + + +