Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Cara Membuat Indeks di Django Tanpa Waktu Henti

Mengelola migrasi basis data merupakan tantangan besar dalam proyek perangkat lunak apa pun. Untungnya, pada versi 1.7, Django hadir dengan kerangka kerja migrasi bawaan. Kerangka kerja ini sangat kuat dan berguna dalam mengelola perubahan dalam basis data. Tetapi fleksibilitas yang diberikan oleh kerangka kerja membutuhkan beberapa kompromi. Untuk memahami batasan dari migrasi Django, Anda akan menangani masalah yang terkenal:membuat indeks di Django tanpa waktu henti.

Dalam tutorial ini, Anda akan mempelajari:

  • Bagaimana dan kapan Django menghasilkan migrasi baru
  • Cara memeriksa perintah yang dihasilkan Django untuk menjalankan migrasi
  • Cara memodifikasi migrasi dengan aman agar sesuai dengan kebutuhan Anda

Tutorial tingkat menengah ini dirancang untuk pembaca yang sudah terbiasa dengan migrasi Django. Untuk pengenalan topik itu, lihat Django Migrations:A Primer.

Bonus Gratis: Klik di sini untuk mendapatkan akses gratis ke tutorial dan sumber daya Django tambahan yang dapat Anda gunakan untuk memperdalam keterampilan pengembangan web Python Anda.


Masalah Dengan Membuat Indeks dalam Migrasi Django

Perubahan umum yang biasanya diperlukan saat data yang disimpan oleh aplikasi Anda bertambah adalah menambahkan indeks. Indeks digunakan untuk mempercepat kueri dan membuat aplikasi Anda terasa cepat dan responsif.

Di sebagian besar database, menambahkan indeks memerlukan kunci eksklusif di atas meja. Kunci eksklusif mencegah operasi modifikasi data (DML) seperti UPDATE , INSERT , dan DELETE , saat indeks dibuat.

Kunci diperoleh secara implisit oleh database saat menjalankan operasi tertentu. Misalnya, ketika pengguna masuk ke aplikasi Anda, Django akan memperbarui last_login bidang di auth_user meja. Untuk melakukan pembaruan, database terlebih dahulu harus mendapatkan kunci pada baris. Jika baris saat ini dikunci oleh koneksi lain, Anda mungkin mendapatkan pengecualian database.

Mengunci tabel dapat menimbulkan masalah jika diperlukan untuk menjaga sistem tetap tersedia selama migrasi. Semakin besar tabel, semakin lama waktu yang dibutuhkan untuk membuat indeks. Semakin lama waktu yang dibutuhkan untuk membuat indeks, semakin lama sistem tidak tersedia atau tidak responsif terhadap pengguna.

Beberapa vendor database menyediakan cara untuk membuat indeks tanpa mengunci tabel. Misalnya, untuk membuat indeks di PostgreSQL tanpa mengunci tabel, Anda dapat menggunakan CONCURRENTLY kata kunci:

CREATE INDEX CONCURRENTLY ix ON table (column);

Di Oracle, ada ONLINE opsi untuk mengizinkan operasi DML di atas tabel saat indeks dibuat:

CREATE INDEX ix ON table (column) ONLINE;

Saat menghasilkan migrasi, Django tidak akan menggunakan kata kunci khusus ini. Menjalankan migrasi apa adanya akan membuat database memperoleh kunci eksklusif pada tabel dan mencegah operasi DML saat indeks dibuat.

Membuat indeks secara bersamaan memiliki beberapa peringatan. Penting untuk memahami masalah khusus untuk backend database Anda terlebih dahulu. Misalnya, satu peringatan di PostgreSQL adalah membuat indeks secara bersamaan membutuhkan waktu lebih lama karena memerlukan pemindaian tabel tambahan.

Dalam tutorial ini, Anda akan menggunakan migrasi Django untuk membuat indeks pada tabel besar, tanpa menyebabkan waktu henti.

Catatan: Untuk mengikuti tutorial ini, disarankan agar Anda menggunakan backend PostgreSQL, Django 2.x, dan Python 3.

Dimungkinkan untuk mengikuti bersama dengan backend basis data lainnya juga. Di tempat di mana fitur SQL unik untuk PostgreSQL digunakan, ubah SQL agar sesuai dengan backend database Anda.



Penyiapan

Anda akan menggunakan Sale yang dibuat-buat model dalam aplikasi bernama app . Dalam situasi kehidupan nyata, model seperti Sale adalah tabel utama dalam database, dan biasanya berukuran sangat besar dan menyimpan banyak data:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
    )
    charged_amount = models.PositiveIntegerField()

Untuk membuat tabel, buat migrasi awal dan terapkan:

$ python manage.py makemigrations
Migrations for 'app':
  app/migrations/0001_initial.py
    - Create model Sale

$ python manage migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0001_initial... OK

Setelah beberapa saat, tabel penjualan menjadi sangat besar, dan pengguna mulai mengeluh tentang kelambatan. Saat memantau database, Anda melihat bahwa banyak kueri menggunakan sold_at kolom. Untuk mempercepat, Anda memutuskan bahwa Anda memerlukan indeks pada kolom.

Untuk menambahkan indeks pada sold_at , Anda membuat perubahan berikut pada model:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )
    charged_amount = models.PositiveIntegerField()

Jika Anda menjalankan migrasi ini sebagaimana adanya, maka Django akan membuat indeks pada tabel, dan itu akan dikunci hingga indeks selesai. Perlu beberapa saat untuk membuat indeks pada tabel yang sangat besar, dan Anda ingin menghindari waktu henti.

Pada lingkungan pengembangan lokal dengan kumpulan data kecil dan koneksi yang sangat sedikit, migrasi ini mungkin terasa instan. Namun, pada kumpulan data besar dengan banyak koneksi bersamaan, memperoleh kunci dan membuat indeks dapat memakan waktu cukup lama.

Pada langkah selanjutnya, Anda akan memodifikasi migrasi yang dibuat oleh Django untuk membuat indeks tanpa menyebabkan waktu henti apa pun.



Migrasi Palsu

Pendekatan pertama adalah membuat indeks secara manual. Anda akan menghasilkan migrasi, tetapi Anda tidak akan benar-benar membiarkan Django menerapkannya. Sebagai gantinya, Anda akan menjalankan SQL secara manual dalam database dan kemudian membuat Django berpikir bahwa migrasi telah selesai.

Pertama, buat migrasi:

$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
  app/migrations/0002_add_index_fake.py
    - Alter field sold_at on sale

Gunakan sqlmigrate perintah untuk melihat SQL Django yang akan digunakan untuk menjalankan migrasi ini:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Anda ingin membuat indeks tanpa mengunci tabel, jadi Anda perlu mengubah perintah. Tambahkan CONCURRENTLY kata kunci dan jalankan di database:

app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

CREATE INDEX

Perhatikan bahwa Anda menjalankan perintah tanpa BEGIN dan COMMIT bagian. Menghilangkan kata kunci ini akan menjalankan perintah tanpa transaksi database. Kami akan membahas transaksi database nanti di artikel.

Setelah Anda menjalankan perintah, jika Anda mencoba menerapkan migrasi, maka Anda akan mendapatkan kesalahan berikut:

$ python manage.py migrate

Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake...Traceback (most recent call last):
  File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)

psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists

Django mengeluh bahwa indeks sudah ada, sehingga tidak dapat melanjutkan migrasi. Anda baru saja membuat indeks secara langsung dalam database, jadi sekarang Anda perlu membuat Django berpikir bahwa migrasi telah diterapkan.

Cara Memalsukan Migrasi

Django menyediakan cara bawaan untuk menandai migrasi sebagai dieksekusi, tanpa benar-benar mengeksekusinya. Untuk menggunakan opsi ini, setel --fake tandai saat menerapkan migrasi:

$ python manage.py migrate --fake
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake... FAKED

Django tidak memunculkan kesalahan kali ini. Faktanya, Django tidak benar-benar menerapkan migrasi apapun. Itu baru saja menandainya sebagai dieksekusi (atau FAKED ).

Berikut beberapa masalah yang perlu dipertimbangkan saat memalsukan migrasi:

  • Perintah manual harus setara dengan SQL yang dihasilkan oleh Django: Anda perlu memastikan perintah yang Anda jalankan setara dengan SQL yang dihasilkan oleh Django. Gunakan sqlmigrate untuk menghasilkan perintah SQL. Jika perintah tidak cocok, Anda mungkin akan mengalami inkonsistensi antara database dan status model.

  • Migrasi lain yang belum diterapkan juga akan dipalsukan: Jika Anda memiliki beberapa migrasi yang belum diterapkan, semuanya akan dipalsukan. Sebelum Anda menerapkan migrasi, penting untuk memastikan bahwa hanya migrasi yang ingin Anda palsukan yang tidak diterapkan. Jika tidak, Anda mungkin berakhir dengan inkonsistensi. Opsi lainnya adalah menentukan migrasi persis yang ingin Anda palsukan.

  • Diperlukan akses langsung ke database: Anda perlu menjalankan perintah SQL di database. Ini tidak selalu merupakan pilihan. Selain itu, menjalankan perintah secara langsung dalam database produksi berbahaya dan harus dihindari jika memungkinkan.

  • Proses penerapan otomatis mungkin memerlukan penyesuaian: Jika Anda mengotomatiskan proses penerapan (menggunakan CI, CD, atau alat otomatisasi lainnya), Anda mungkin perlu mengubah proses menjadi migrasi palsu. Ini tidak selalu diinginkan.

Pembersihan

Sebelum melanjutkan ke bagian berikutnya, Anda perlu mengembalikan database ke statusnya tepat setelah migrasi awal. Untuk melakukannya, migrasi kembali ke migrasi awal:

$ python manage.py migrate 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_fake... OK

Django membatalkan penerapan perubahan yang dibuat dalam migrasi kedua, jadi sekarang aman juga untuk menghapus file:

$ rm app/migrations/0002_add_index_fake.py

Untuk memastikan Anda melakukan semuanya dengan benar, periksa migrasi:

$ python manage.py showmigrations app
app
 [X] 0001_initial

Migrasi awal telah diterapkan, dan tidak ada migrasi yang belum diterapkan.



Jalankan SQL Mentah dalam Migrasi

Di bagian sebelumnya, Anda mengeksekusi SQL langsung di database dan memalsukan migrasi. Ini menyelesaikan pekerjaan, tetapi ada solusi yang lebih baik.

Django menyediakan cara untuk mengeksekusi SQL mentah dalam migrasi menggunakan RunSQL . Mari kita coba menggunakannya daripada menjalankan perintah secara langsung di database.

Pertama, buat migrasi kosong baru:

$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
  app/migrations/0002_add_index_runsql.py

Selanjutnya, edit file migrasi dan tambahkan RunSQL operasi:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',
        ),
    ]

Saat Anda menjalankan migrasi, Anda akan mendapatkan output berikut:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_runsql... OK

Ini terlihat bagus, tapi ada masalah. Mari kita coba membuat migrasi lagi:

$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
  app/migrations/0003_leftover_migration.py
    - Alter field sold_at on sale

Django menghasilkan migrasi yang sama lagi. Mengapa melakukan itu?

Pembersihan

Sebelum kami dapat menjawab pertanyaan itu, Anda perlu membersihkan dan membatalkan perubahan yang Anda buat pada database. Mulailah dengan menghapus migrasi terakhir. Itu tidak diterapkan, jadi aman untuk dihapus:

$ rm app/migrations/0003_leftover_migration.py

Selanjutnya, buat daftar migrasi untuk app aplikasi:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

Migrasi ketiga hilang, tetapi yang kedua diterapkan. Anda ingin kembali ke keadaan setelah migrasi awal. Coba migrasi kembali ke migrasi awal seperti yang Anda lakukan di bagian sebelumnya:

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):

NotImplementedError: You cannot reverse this operation

Django tidak dapat membalikkan migrasi.



Operasi Migrasi Terbalik

Untuk membalikkan migrasi, Django mengeksekusi tindakan yang berlawanan untuk setiap operasi. Dalam hal ini, kebalikan dari menambahkan indeks adalah dengan menjatuhkannya. Seperti yang telah Anda lihat, saat migrasi dapat dibalik, Anda dapat membatalkan penerapannya. Sama seperti Anda dapat menggunakan checkout di Git, Anda dapat membalikkan migrasi jika Anda menjalankan migrate ke migrasi sebelumnya.

Banyak operasi migrasi bawaan sudah menentukan tindakan sebaliknya. Misalnya, tindakan sebaliknya untuk menambahkan bidang adalah dengan menjatuhkan kolom yang sesuai. Tindakan sebaliknya untuk membuat model adalah dengan membuang tabel yang sesuai.

Beberapa operasi migrasi tidak dapat dibalik. Misalnya, tidak ada tindakan sebaliknya untuk menghapus bidang atau model, karena setelah migrasi diterapkan, data akan hilang.

Di bagian sebelumnya, Anda menggunakan RunSQL operasi. Saat mencoba membalikkan migrasi, Anda mengalami kesalahan. Menurut kesalahan, salah satu operasi dalam migrasi tidak dapat dibatalkan. Django tidak dapat membalikkan SQL mentah secara default. Karena Django tidak memiliki pengetahuan tentang apa yang dieksekusi oleh operasi, itu tidak dapat menghasilkan tindakan yang berlawanan secara otomatis.

Cara Membuat Migrasi Dapat Dibalik

Untuk migrasi menjadi reversibel, semua operasi di dalamnya harus reversibel. Bagian dari migrasi tidak dapat dibalik, jadi satu operasi yang tidak dapat dibalikkan akan membuat seluruh migrasi tidak dapat dibatalkan.

Untuk membuat RunSQL operasi reversibel, Anda harus menyediakan SQL untuk dijalankan ketika operasi dibalik. SQL terbalik disediakan di reverse_sql argumen.

Tindakan kebalikan dari menambahkan indeks adalah dengan menjatuhkannya. Untuk membuat migrasi Anda dapat dibalik, berikan reverse_sql untuk menjatuhkan indeks:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

Sekarang coba balikkan migrasi:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
 Unapplying app.0002_add_index_runsql... OK

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [ ] 0002_add_index_runsql

Migrasi kedua dibalik, dan indeks dijatuhkan oleh Django. Sekarang aman untuk menghapus file migrasi:

$ rm app/migrations/0002_add_index_runsql.py

Itu selalu merupakan ide yang baik untuk memberikan reverse_sql . Dalam situasi di mana membalikkan operasi SQL mentah tidak memerlukan tindakan apa pun, Anda dapat menandai operasi sebagai reversibel menggunakan penjaga khusus migrations.RunSQL.noop :

migrations.RunSQL(
    sql='...',  # Your forward SQL here
    reverse_sql=migrations.RunSQL.noop,
),


Memahami Status Model dan Status Basis Data

Dalam upaya Anda sebelumnya untuk membuat indeks secara manual menggunakan RunSQL , Django menghasilkan migrasi yang sama berulang-ulang meskipun indeks telah dibuat dalam database. Untuk memahami mengapa Django melakukan itu, pertama-tama Anda perlu memahami bagaimana Django memutuskan kapan menghasilkan migrasi baru.


Saat Django Menghasilkan Migrasi Baru

Dalam proses menghasilkan dan menerapkan migrasi, Django menyinkronkan antara status basis data dan status model. Misalnya, saat Anda menambahkan bidang ke model, Django menambahkan kolom ke tabel. Saat Anda menghapus bidang dari model, Django menghapus kolom dari tabel.

Untuk menyinkronkan antara model dan database, Django mempertahankan status yang mewakili model. Untuk menyinkronkan database dengan model, Django menghasilkan operasi migrasi. Operasi migrasi diterjemahkan ke SQL khusus vendor yang dapat dieksekusi dalam database. Ketika semua operasi migrasi dijalankan, database dan model diharapkan konsisten.

Untuk mendapatkan status database, Django menggabungkan operasi dari semua migrasi sebelumnya. Ketika keadaan migrasi gabungan tidak konsisten dengan keadaan model, Django menghasilkan migrasi baru.

Pada contoh sebelumnya, Anda membuat indeks menggunakan SQL mentah. Django tidak tahu Anda membuat indeks karena Anda tidak menggunakan operasi migrasi yang sudah dikenal.

Ketika Django mengumpulkan semua migrasi dan membandingkannya dengan keadaan model, ditemukan bahwa sebuah indeks hilang. Inilah sebabnya, bahkan setelah Anda membuat indeks secara manual, Django masih berpikir itu hilang dan menghasilkan migrasi baru untuknya.



Cara Memisahkan Basis Data dan Status dalam Migrasi

Karena Django tidak dapat membuat indeks seperti yang Anda inginkan, Anda ingin memberikan SQL Anda sendiri tetapi tetap memberi tahu Django bahwa Anda yang membuatnya.

Dengan kata lain, Anda perlu mengeksekusi sesuatu dalam database dan menyediakan Django dengan operasi migrasi untuk menyinkronkan keadaan internalnya. Untuk melakukan itu, Django memberi kita operasi migrasi khusus yang disebut SeparateDatabaseAndState . Operasi ini tidak diketahui dengan baik dan harus disediakan untuk kasus khusus seperti ini.

Mengedit migrasi jauh lebih mudah daripada menulisnya dari awal, jadi mulailah dengan membuat migrasi dengan cara biasa:

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations for 'app':
  app/migrations/0002_add_index_separate_database_and_state.py
    - Alter field sold_at on sale

Ini adalah isi dari migrasi yang dihasilkan oleh Django, sama seperti sebelumnya:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='sale',
            name='sold_at',
            field=models.DateTimeField(
                auto_now_add=True,
                db_index=True,
            ),
        ),
    ]

Django menghasilkan AlterField operasi di bidang sold_at . Operasi akan membuat indeks dan memperbarui status. Kami ingin mempertahankan operasi ini tetapi memberikan perintah yang berbeda untuk dijalankan di database.

Sekali lagi, untuk mendapatkan perintah, gunakan SQL yang dihasilkan oleh Django:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Tambahkan CONCURRENTLY kata kunci di tempat yang sesuai:

CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

Selanjutnya, edit file migrasi dan gunakan SeparateDatabaseAndState untuk memberikan perintah SQL Anda yang dimodifikasi untuk dieksekusi:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """, reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

Operasi migrasi SeparateDatabaseAndState menerima 2 daftar operasi:

  1. operasi_status adalah operasi untuk diterapkan pada keadaan model internal. Mereka tidak mempengaruhi database.
  2. operasi_database adalah operasi untuk diterapkan ke database.

Anda menyimpan operasi asli yang dihasilkan oleh Django di state_operations . Saat menggunakan SeparateDatabaseAndState , inilah yang biasanya ingin Anda lakukan. Perhatikan bahwa db_index=True argumen disediakan ke lapangan. Operasi migrasi ini akan memberi tahu Django bahwa ada indeks pada bidang.

Anda menggunakan SQL yang dihasilkan oleh Django dan menambahkan CONCURRENTLY kata kunci. Anda menggunakan tindakan khusus RunSQL untuk mengeksekusi SQL mentah dalam migrasi.

Jika Anda mencoba menjalankan migrasi, Anda akan mendapatkan output berikut:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block



Migrasi Non-Atom

Dalam SQL, CREATE , DROP , ALTER , dan TRUNCATE operasi disebut sebagai Bahasa Definisi Data (DDL). Dalam basis data yang mendukung DDL transaksional, seperti PostgreSQL, Django mengeksekusi migrasi di dalam transaksi basis data secara default. Namun, menurut kesalahan di atas, PostgreSQL tidak dapat membuat indeks secara bersamaan di dalam blok transaksi.

Untuk dapat membuat indeks secara bersamaan di dalam migrasi, Anda perlu memberi tahu Django untuk tidak menjalankan migrasi dalam transaksi basis data. Untuk melakukannya, Anda menandai migrasi sebagai non-atom dengan menyetel atomic menjadi False :

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """,
                reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

Setelah Anda menandai migrasi sebagai non-atom, Anda dapat menjalankan migrasi:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state... OK

Anda baru saja menjalankan migrasi tanpa menyebabkan waktu henti.

Berikut adalah beberapa masalah yang perlu dipertimbangkan saat Anda menggunakan SeparateDatabaseAndState :

  • Operasi basis data harus setara dengan operasi status: Inkonsistensi antara database dan status model dapat menyebabkan banyak masalah. Titik awal yang baik adalah menjaga operasi yang dihasilkan oleh Django dalam state_operations dan edit output sqlmigrate untuk digunakan dalam database_operations .

  • Migrasi non-atomik tidak dapat dikembalikan jika terjadi kesalahan: Jika ada kesalahan selama migrasi, maka Anda tidak akan dapat melakukan rollback. Anda harus mengembalikan migrasi atau menyelesaikannya secara manual. Ada baiknya untuk menjaga agar operasi tetap dijalankan di dalam migrasi non-atomik seminimal mungkin. Jika Anda memiliki operasi tambahan dalam migrasi, pindahkan ke migrasi baru.

  • Migrasi mungkin khusus vendor: SQL yang dihasilkan oleh Django khusus untuk basis data backend yang digunakan dalam proyek. Ini mungkin bekerja dengan backend database lain, tetapi itu tidak dijamin. Jika Anda perlu mendukung beberapa backend database, Anda perlu melakukan beberapa penyesuaian pada pendekatan ini.



Kesimpulan

Anda memulai tutorial ini dengan sebuah tabel besar dan sebuah masalah. Anda ingin membuat aplikasi Anda lebih cepat bagi pengguna, dan Anda ingin melakukannya tanpa menyebabkan mereka downtime.

Di akhir tutorial, Anda berhasil membuat dan memodifikasi migrasi Django dengan aman untuk mencapai tujuan ini. Anda telah mengatasi berbagai masalah selama prosesnya dan berhasil mengatasinya menggunakan alat bawaan yang disediakan oleh kerangka kerja migrasi.

Dalam tutorial ini, Anda mempelajari hal berikut:

  • Bagaimana migrasi Django bekerja secara internal menggunakan model dan status basis data, dan ketika migrasi baru dihasilkan
  • Cara menjalankan SQL khusus dalam migrasi menggunakan RunSQL tindakan
  • Apa itu migrasi reversibel, dan cara membuat RunSQL tindakan dapat dibalik
  • Apa itu migrasi atom, dan bagaimana mengubah perilaku default sesuai kebutuhan Anda
  • Cara menjalankan migrasi kompleks dengan aman di Django

Pemisahan antara model dan status database merupakan konsep penting. Setelah Anda memahaminya, dan cara menggunakannya, Anda dapat mengatasi banyak keterbatasan operasi migrasi bawaan. Beberapa kasus penggunaan yang muncul dalam pikiran termasuk menambahkan indeks yang sudah dibuat dalam database dan memberikan argumen khusus vendor ke perintah DDL.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tingkat Isolasi Baca yang Dapat Diulang

  2. Metodologi Pengujian Kinerja:Menemukan Cara Baru

  3. Driver ODBC Easysoft dan Perpustakaan ODBINST

  4. Mitos Kinerja :Indeks Clustered vs. Non-Clustered

  5. Bawa Cloud Anda Sendiri (BYOC) vs. Hosting Khusus di ScaleGrid