Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Cách tạo chỉ mục trong Django mà không có thời gian ngừng hoạt động

Quản lý việc di chuyển cơ sở dữ liệu là một thách thức lớn trong bất kỳ dự án phần mềm nào. May mắn thay, kể từ phiên bản 1.7, Django đi kèm với một khung di chuyển tích hợp sẵn. Khung này rất mạnh mẽ và hữu ích trong việc quản lý sự thay đổi trong cơ sở dữ liệu. Nhưng sự linh hoạt được cung cấp bởi khuôn khổ yêu cầu một số thỏa hiệp. Để hiểu những hạn chế của việc di chuyển Django, bạn sẽ giải quyết một vấn đề nổi tiếng:tạo chỉ mục trong Django mà không có thời gian chết.

Trong hướng dẫn này, bạn sẽ học:

  • Cách thức và thời điểm Django tạo ra các chuyển đổi mới
  • Cách kiểm tra các lệnh mà Django tạo ra để thực hiện quá trình di chuyển
  • Cách sửa đổi quá trình di chuyển một cách an toàn để phù hợp với nhu cầu của bạn

Hướng dẫn cấp độ trung cấp này được thiết kế cho những độc giả đã quen thuộc với việc di chuyển Django. Để có phần giới thiệu về chủ đề đó, hãy xem Django Migrations:A Primer.

Tiền thưởng miễn phí: Nhấp vào đây để có quyền truy cập miễn phí vào các tài nguyên và hướng dẫn Django bổ sung mà bạn có thể sử dụng để nâng cao kỹ năng phát triển web Python của mình.


Vấn đề với việc tạo chỉ mục trong Django Migrations

Một thay đổi phổ biến thường trở nên cần thiết khi dữ liệu được lưu trữ bởi ứng dụng của bạn tăng lên là thêm chỉ mục. Các chỉ mục được sử dụng để tăng tốc các truy vấn và làm cho ứng dụng của bạn hoạt động nhanh và nhạy.

Trong hầu hết các cơ sở dữ liệu, việc thêm chỉ mục yêu cầu một khóa riêng trên bảng. Một khóa độc quyền ngăn các hoạt động sửa đổi dữ liệu (DML) như UPDATE , INSERTDELETE , trong khi chỉ mục được tạo.

Các khóa được cơ sở dữ liệu hoàn toàn có được khi thực hiện các hoạt động nhất định. Ví dụ:khi người dùng đăng nhập vào ứng dụng của bạn, Django sẽ cập nhật last_login trong trường auth_user bàn. Để thực hiện cập nhật, trước tiên cơ sở dữ liệu sẽ phải có được một khóa trên hàng. Nếu hàng hiện đang bị khóa bởi một kết nối khác, thì bạn có thể nhận được một ngoại lệ cơ sở dữ liệu.

Việc khóa bảng có thể gây ra sự cố khi cần thiết để giữ cho hệ thống luôn sẵn sàng trong quá trình di chuyển. Bảng càng lớn, có thể mất nhiều thời gian hơn để tạo chỉ mục. Thời gian tạo chỉ mục càng lâu, hệ thống không khả dụng hoặc không phản hồi với người dùng càng lâu.

Một số nhà cung cấp cơ sở dữ liệu cung cấp cách tạo chỉ mục mà không cần khóa bảng. Ví dụ:để tạo chỉ mục trong PostgreSQL mà không khóa bảng, bạn có thể sử dụng CONCURRENTLY từ khóa:

CREATE INDEX CONCURRENTLY ix ON table (column);

Trong Oracle, có một ONLINE tùy chọn để cho phép các hoạt động DML trên bảng trong khi chỉ mục được tạo:

CREATE INDEX ix ON table (column) ONLINE;

Khi tạo di chuyển, Django sẽ không sử dụng các từ khóa đặc biệt này. Việc chạy quá trình di chuyển như vậy sẽ làm cho cơ sở dữ liệu có được một khóa độc quyền trên bảng và ngăn các hoạt động DML trong khi chỉ mục được tạo.

Việc tạo chỉ mục đồng thời có một số lưu ý. Điều quan trọng là phải hiểu trước các vấn đề cụ thể đối với phần phụ trợ cơ sở dữ liệu của bạn. Ví dụ:một lưu ý trong PostgreSQL là việc tạo chỉ mục đồng thời mất nhiều thời gian hơn vì nó yêu cầu quét bảng bổ sung.

Trong hướng dẫn này, bạn sẽ sử dụng di chuyển Django để tạo chỉ mục trên một bảng lớn mà không gây ra bất kỳ thời gian chết nào.

Lưu ý: Để làm theo hướng dẫn này, bạn nên sử dụng chương trình phụ trợ PostgreSQL, Django 2.x và Python 3.

Nó cũng có thể làm theo cùng với các phụ trợ cơ sở dữ liệu khác. Ở những nơi sử dụng các tính năng SQL duy nhất cho PostgreSQL, hãy thay đổi SQL để phù hợp với phần phụ trợ cơ sở dữ liệu của bạn.



Thiết lập

Bạn sẽ sử dụng Sale được trang bị sẵn mô hình trong một ứng dụng có tên là app . Trong tình huống thực tế, các mô hình như Sale là các bảng chính trong cơ sở dữ liệu và chúng thường sẽ rất lớn và lưu trữ nhiều dữ liệu:

# models.py

from django.db import models

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

Để tạo bảng, hãy tạo quá trình di chuyển ban đầu và áp dụng nó:

$ 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

Sau một thời gian, bảng doanh số trở nên rất lớn và người dùng bắt đầu phàn nàn về sự chậm chạp. Trong khi theo dõi cơ sở dữ liệu, bạn nhận thấy rằng rất nhiều truy vấn sử dụng sold_at cột. Để đẩy nhanh tiến độ, bạn quyết định rằng bạn cần một chỉ mục trên cột.

Để thêm chỉ mục trên sold_at , bạn thực hiện thay đổi sau đối với mô hình:

# 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()

Nếu bạn chạy quá trình di chuyển này như hiện tại, thì Django sẽ tạo chỉ mục trên bảng và nó sẽ bị khóa cho đến khi hoàn thành chỉ mục. Có thể mất một lúc để tạo chỉ mục trên một bảng rất lớn và bạn muốn tránh thời gian chết.

Trên môi trường phát triển cục bộ với một tập dữ liệu nhỏ và rất ít kết nối, quá trình di chuyển này có thể xảy ra ngay lập tức. Tuy nhiên, trên các tập dữ liệu lớn có nhiều kết nối đồng thời, việc lấy khóa và tạo chỉ mục có thể mất một lúc.

Trong các bước tiếp theo, bạn sẽ sửa đổi các di chuyển được tạo bởi Django để tạo chỉ mục mà không gây ra bất kỳ thời gian chết nào.



Di chuyển giả mạo

Cách tiếp cận đầu tiên là tạo chỉ mục theo cách thủ công. Bạn sẽ tạo ra quá trình di chuyển, nhưng bạn sẽ không thực sự cho phép Django áp dụng nó. Thay vào đó, bạn sẽ chạy SQL theo cách thủ công trong cơ sở dữ liệu và sau đó làm cho Django nghĩ rằng quá trình di chuyển đã hoàn tất.

Đầu tiên, tạo quá trình di chuyển:

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

Sử dụng sqlmigrate lệnh xem SQL Django sẽ sử dụng để thực hiện quá trình di chuyển này:

$ 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;

Bạn muốn tạo chỉ mục mà không khóa bảng, vì vậy bạn cần sửa đổi lệnh. Thêm CONCURRENTLY từ khóa và thực thi trong cơ sở dữ liệu:

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

CREATE INDEX

Lưu ý rằng bạn đã thực thi lệnh mà không có BEGINCOMMIT các bộ phận. Bỏ qua các từ khóa này sẽ thực hiện các lệnh mà không cần giao dịch cơ sở dữ liệu. Chúng ta sẽ thảo luận về các giao dịch cơ sở dữ liệu ở phần sau của bài viết.

Sau khi thực hiện lệnh, nếu bạn cố gắng áp dụng các phép di chuyển, thì bạn sẽ gặp lỗi sau:

$ 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 phàn nàn rằng chỉ mục đã tồn tại, vì vậy nó không thể tiếp tục di chuyển. Bạn vừa tạo chỉ mục trực tiếp trong cơ sở dữ liệu, vì vậy bây giờ bạn cần làm cho Django nghĩ rằng quá trình di chuyển đã được áp dụng.

Cách giả mạo di chuyển

Django cung cấp một cách tích hợp để đánh dấu quá trình di chuyển là được thực thi, mà không thực sự thực thi chúng. Để sử dụng tùy chọn này, hãy đặt --fake gắn cờ khi áp dụng di chuyển:

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

Django không đưa ra lỗi lần này. Trên thực tế, Django không thực sự áp dụng bất kỳ sự di chuyển nào. Nó chỉ đánh dấu nó là đã thực thi (hoặc FAKED ).

Dưới đây là một số vấn đề cần xem xét khi di chuyển giả:

  • Lệnh thủ công phải tương đương với SQL được tạo bởi Django: Bạn cần đảm bảo rằng lệnh bạn thực thi tương đương với SQL do Django tạo ra. Sử dụng sqlmigrate để tạo ra lệnh SQL. Nếu các lệnh không khớp, thì bạn có thể dẫn đến sự mâu thuẫn giữa cơ sở dữ liệu và trạng thái mô hình.

  • Các di chuyển không được áp dụng khác cũng sẽ bị làm giả: Khi bạn có nhiều lần di chuyển chưa được áp dụng, tất cả chúng sẽ bị giả mạo. Trước khi bạn áp dụng di chuyển, điều quan trọng là phải đảm bảo rằng chỉ những di chuyển bạn muốn giả mạo mới không được áp dụng. Nếu không, bạn có thể gặp phải những mâu thuẫn. Một tùy chọn khác là chỉ định chính xác quá trình di chuyển bạn muốn giả mạo.

  • Cần có quyền truy cập trực tiếp vào cơ sở dữ liệu: Bạn cần chạy lệnh SQL trong cơ sở dữ liệu. Đây không phải lúc nào cũng là một lựa chọn. Ngoài ra, việc thực hiện các lệnh trực tiếp trong cơ sở dữ liệu sản xuất là rất nguy hiểm và nên tránh khi có thể.

  • Quy trình triển khai tự động có thể cần điều chỉnh: Nếu bạn đã tự động hóa quy trình triển khai (sử dụng CI, CD hoặc các công cụ tự động hóa khác), thì bạn có thể cần phải thay đổi quy trình để chuyển đổi giả mạo. Điều này không phải lúc nào cũng mong muốn.

Dọn dẹp

Trước khi chuyển sang phần tiếp theo, bạn cần đưa cơ sở dữ liệu trở lại trạng thái của nó ngay sau lần di chuyển đầu tiên. Để làm điều đó, hãy quay lại quá trình di chuyển ban đầu:

$ 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 đã bỏ áp dụng các thay đổi được thực hiện trong lần di chuyển thứ hai, vì vậy bây giờ có thể an toàn nếu xóa tệp:

$ rm app/migrations/0002_add_index_fake.py

Để đảm bảo rằng bạn đã làm đúng mọi thứ, hãy kiểm tra quá trình di chuyển:

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

Di chuyển ban đầu đã được áp dụng và không có di chuyển nào chưa được áp dụng.



Thực thi SQL thô trong Migrations

Trong phần trước, bạn đã thực thi SQL trực tiếp trong cơ sở dữ liệu và giả mạo quá trình di chuyển. Điều này hoàn thành công việc, nhưng có một giải pháp tốt hơn.

Django cung cấp một cách để thực thi SQL thô khi di chuyển bằng RunSQL . Hãy thử sử dụng nó thay vì thực hiện lệnh trực tiếp trong cơ sở dữ liệu.

Đầu tiên, tạo một di chuyển trống mới:

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

Tiếp theo, chỉnh sửa tệp di chuyển và thêm RunSQL hoạt động:

# 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");',
        ),
    ]

Khi bạn chạy quá trình di chuyển, bạn sẽ nhận được kết quả sau:

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

Điều này có vẻ tốt, nhưng có một vấn đề. Hãy thử tạo lại các lần di chuyển:

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

Django đã tạo lại quá trình di chuyển tương tự. Tại sao nó lại làm như vậy?

Dọn dẹp

Trước khi chúng tôi có thể trả lời câu hỏi đó, bạn cần phải dọn dẹp và hoàn tác các thay đổi bạn đã thực hiện đối với cơ sở dữ liệu. Bắt đầu bằng cách xóa lần di chuyển cuối cùng. Nó không được áp dụng, vì vậy có thể xóa:

$ rm app/migrations/0003_leftover_migration.py

Tiếp theo, liệt kê các di chuyển cho ứng dụng app ứng dụng:

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

Lần di chuyển thứ ba đã biến mất, nhưng lần thứ hai được áp dụng. Bạn muốn trở lại trạng thái ngay sau lần di chuyển đầu tiên. Cố gắng di chuyển trở lại quá trình di chuyển ban đầu như bạn đã làm trong phần trước:

$ 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 không thể đảo ngược quá trình di chuyển.



Thao tác di chuyển ngược

Để đảo ngược quá trình di chuyển, Django thực hiện một hành động ngược lại cho mọi hoạt động. Trong trường hợp này, mặt trái của việc thêm một chỉ mục là bỏ nó đi. Như bạn đã thấy, khi quá trình di chuyển có thể hoàn nguyên, bạn có thể hủy áp dụng nó. Giống như bạn có thể sử dụng checkout trong Git, bạn có thể đảo ngược quá trình di chuyển nếu bạn thực thi migrate sang lần di chuyển trước đó.

Nhiều hoạt động di chuyển tích hợp đã xác định một hành động ngược lại. Ví dụ:hành động ngược lại để thêm trường là bỏ cột tương ứng. Thao tác ngược lại để tạo mô hình là bỏ bảng tương ứng.

Một số hoạt động di chuyển không thể hoàn nguyên. Ví dụ:không có hành động ngược lại nào đối với việc xóa trường hoặc xóa mô hình, bởi vì khi quá trình di chuyển được áp dụng, dữ liệu sẽ biến mất.

Trong phần trước, bạn đã sử dụng RunSQL hoạt động. Khi bạn cố gắng đảo ngược quá trình di chuyển, bạn đã gặp lỗi. Theo lỗi, không thể hoàn tác một trong các thao tác trong quá trình di chuyển. Django không thể đảo ngược SQL thô theo mặc định. Bởi vì Django không có kiến ​​thức về những gì đã được thực thi bởi hoạt động, nó không thể tự động tạo ra một hành động ngược lại.

Cách thực hiện việc di chuyển có thể đảo ngược

Để quá trình di chuyển có thể hoàn nguyên, tất cả các hoạt động trong đó phải được đảo ngược. Không thể đảo ngược một phần của quá trình di chuyển, do đó, một thao tác không thể hoàn nguyên sẽ khiến toàn bộ quá trình di chuyển không thể hoàn nguyên.

Để tạo một RunSQL hoạt động có thể đảo ngược, bạn phải cung cấp SQL để thực thi khi hoạt động bị đảo ngược. SQL ngược được cung cấp trong reverse_sql đối số.

Hành động ngược lại để thêm một chỉ mục là bỏ nó đi. Để làm cho quá trình di chuyển của bạn có thể đảo ngược, hãy cung cấp reverse_sql để giảm chỉ mục:

# 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";',
        ),
    ]

Bây giờ hãy thử đảo ngược quá trình di chuyển:

$ 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

Lần di chuyển thứ hai đã bị đảo ngược và chỉ số này đã bị Django giảm xuống. Bây giờ, có thể an toàn để xóa tệp di chuyển:

$ rm app/migrations/0002_add_index_runsql.py

Bạn nên cung cấp reverse_sql . Trong các tình huống mà việc đảo ngược một hoạt động SQL thô không yêu cầu bất kỳ hành động nào, bạn có thể đánh dấu hoạt động là có thể đảo ngược bằng cách sử dụng sentinel migrations.RunSQL.noop đặc biệt :

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


Hiểu Trạng thái Mô hình và Trạng thái Cơ sở dữ liệu

Trong lần cố gắng tạo chỉ mục theo cách thủ công trước đó của bạn bằng cách sử dụng RunSQL , Django đã tạo lặp đi lặp lại cùng một lần di chuyển mặc dù chỉ mục đã được tạo trong cơ sở dữ liệu. Để hiểu tại sao Django lại làm như vậy, trước tiên bạn cần hiểu cách Django quyết định khi nào tạo ra các lần di chuyển mới.


Khi Django Tạo một Di chuyển Mới

Trong quá trình tạo và áp dụng di chuyển, Django đồng bộ hóa giữa trạng thái của cơ sở dữ liệu và trạng thái của các mô hình. Ví dụ:khi bạn thêm một trường vào một mô hình, Django sẽ thêm một cột vào bảng. Khi bạn xóa một trường khỏi mô hình, Django sẽ xóa cột khỏi bảng.

Để đồng bộ hóa giữa các mô hình và cơ sở dữ liệu, Django duy trì trạng thái đại diện cho các mô hình. Để đồng bộ cơ sở dữ liệu với các mô hình, Django tạo các hoạt động di chuyển. Các hoạt động di chuyển dịch sang một SQL cụ thể của nhà cung cấp có thể được thực thi trong cơ sở dữ liệu. Khi tất cả các hoạt động di chuyển được thực thi, cơ sở dữ liệu và các mô hình dự kiến ​​sẽ nhất quán.

Để có được trạng thái của cơ sở dữ liệu, Django tổng hợp các hoạt động từ tất cả các lần di chuyển trước đây. Khi trạng thái tổng hợp của các lần di chuyển không phù hợp với trạng thái của các mô hình, Django sẽ tạo một lần di chuyển mới.

Trong ví dụ trước, bạn đã tạo chỉ mục bằng SQL thô. Django không biết bạn đã tạo chỉ mục vì bạn không sử dụng thao tác di chuyển quen thuộc.

Khi Django tổng hợp tất cả các lần di chuyển và so sánh chúng với trạng thái của các mô hình, nó nhận thấy rằng một chỉ mục bị thiếu. Đây là lý do tại sao, ngay cả sau khi bạn tạo chỉ mục theo cách thủ công, Django vẫn cho rằng nó bị thiếu và đã tạo một sự di chuyển mới cho nó.



Cách tách Cơ sở dữ liệu và Trạng thái trong Di chuyển

Vì Django không thể tạo chỉ mục theo cách bạn muốn, bạn muốn cung cấp SQL của riêng mình nhưng vẫn cho Django biết bạn đã tạo.

Nói cách khác, bạn cần thực thi một cái gì đó trong cơ sở dữ liệu và cung cấp cho Django thao tác di chuyển để đồng bộ trạng thái bên trong của nó. Để làm điều đó, Django cung cấp cho chúng tôi một hoạt động di chuyển đặc biệt có tên là SeparateDatabaseAndState . Thao tác này không được nhiều người biết đến và chỉ nên dành cho những trường hợp đặc biệt như thao tác này.

Chỉnh sửa di chuyển dễ dàng hơn nhiều so với viết chúng từ đầu, vì vậy hãy bắt đầu bằng cách tạo di chuyển theo cách thông thường:

$ 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

Đây là nội dung của quá trình di chuyển do Django tạo ra, giống như trước đây:

# 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 đã tạo một AlterField hoạt động trên trường sold_at . Thao tác sẽ tạo chỉ mục và cập nhật trạng thái. Chúng tôi muốn giữ lại thao tác này nhưng cung cấp một lệnh khác để thực thi trong cơ sở dữ liệu.

Một lần nữa, để nhận lệnh, hãy sử dụng SQL được tạo bởi 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;

Thêm CONCURRENTLY từ khóa ở nơi thích hợp:

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

Tiếp theo, chỉnh sửa tệp di chuyển và sử dụng SeparateDatabaseAndState để cung cấp lệnh SQL đã sửa đổi của bạn để thực thi:

# 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";
                """),
            ],
        ),

    ],

Thao tác di chuyển SeparateDatabaseAndState chấp nhận 2 danh sách hoạt động:

  1. state_operations là các hoạt động để áp dụng trên trạng thái mô hình bên trong. Chúng không ảnh hưởng đến cơ sở dữ liệu.
  2. database_operations là các phép toán áp dụng cho cơ sở dữ liệu.

Bạn đã giữ nguyên hoạt động ban đầu do Django tạo trong state_operations . Khi sử dụng SeparateDatabaseAndState , đây là những gì bạn thường muốn làm. Lưu ý rằng db_index=True đối số được cung cấp cho trường. Thao tác di chuyển này sẽ cho Django biết rằng có một chỉ mục trên trường.

Bạn đã sử dụng SQL do Django tạo ra và thêm CONCURRENTLY từ khóa. Bạn đã sử dụng hành động đặc biệt RunSQL để thực thi SQL thô trong quá trình di chuyển.

Nếu bạn cố gắng chạy quá trình di chuyển, bạn sẽ nhận được kết quả sau:

$ 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



Các cuộc di cư phi nguyên tử

Trong SQL, CREATE , DROP , ALTERTRUNCATE hoạt động được gọi là Ngôn ngữ định nghĩa dữ liệu (DDL). Trong cơ sở dữ liệu hỗ trợ DDL giao dịch, chẳng hạn như PostgreSQL, Django thực thi di chuyển bên trong giao dịch cơ sở dữ liệu theo mặc định. Tuy nhiên, theo lỗi ở trên, PostgreSQL không thể tạo chỉ mục đồng thời bên trong khối giao dịch.

Để có thể tạo chỉ mục đồng thời trong quá trình di chuyển, bạn cần yêu cầu Django không thực hiện di chuyển trong giao dịch cơ sở dữ liệu. Để làm điều đó, bạn đánh dấu di chuyển là phi nguyên tử bằng cách đặt atomic thành 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";
                """),
            ],
        ),

    ],

Sau khi bạn đánh dấu quá trình di chuyển là không nguyên tử, bạn có thể chạy quá trình di chuyển:

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

Bạn vừa thực hiện quá trình di chuyển mà không gây ra bất kỳ thời gian chết nào.

Dưới đây là một số vấn đề cần xem xét khi bạn đang sử dụng SeparateDatabaseAndState :

  • Hoạt động cơ sở dữ liệu phải tương đương với hoạt động trạng thái: Sự không nhất quán giữa cơ sở dữ liệu và trạng thái mô hình có thể gây ra rất nhiều rắc rối. Điểm khởi đầu tốt là giữ các hoạt động do Django tạo ra ở state_operations và chỉnh sửa đầu ra của sqlmigrate để sử dụng trong database_operations .

  • Di chuyển không phải nguyên tử không thể khôi phục trong trường hợp có lỗi: Nếu có lỗi xảy ra trong quá trình di chuyển, thì bạn sẽ không thể khôi phục. Bạn sẽ phải quay lại quá trình di chuyển hoặc hoàn thành nó theo cách thủ công. Bạn nên giữ ở mức tối thiểu các hoạt động được thực thi bên trong quá trình di chuyển phi nguyên tử. Nếu bạn có các thao tác bổ sung trong quá trình di chuyển, hãy chuyển chúng sang một quá trình di chuyển mới.

  • Di chuyển có thể dành riêng cho nhà cung cấp: SQL được tạo bởi Django dành riêng cho phần phụ trợ cơ sở dữ liệu được sử dụng trong dự án. Nó có thể hoạt động với các phụ trợ cơ sở dữ liệu khác, nhưng điều đó không được đảm bảo. Nếu bạn cần hỗ trợ nhiều phụ trợ cơ sở dữ liệu, bạn cần thực hiện một số điều chỉnh đối với phương pháp này.



Kết luận

Bạn đã bắt đầu hướng dẫn này với một bảng lớn và một vấn đề. Bạn muốn làm cho ứng dụng của mình nhanh hơn cho người dùng và bạn muốn làm điều đó mà không gây ra bất kỳ thời gian chết cho họ.

Đến cuối hướng dẫn, bạn đã quản lý để tạo và sửa đổi một cách an toàn việc di chuyển Django để đạt được mục tiêu này. Bạn đã giải quyết các vấn đề khác nhau trong quá trình thực hiện và quản lý để khắc phục chúng bằng cách sử dụng các công cụ tích hợp do khung di chuyển cung cấp.

Trong hướng dẫn này, bạn đã học được những điều sau:

  • Cách di chuyển Django hoạt động nội bộ bằng cách sử dụng trạng thái cơ sở dữ liệu và mô hình cũng như khi các di chuyển mới được tạo
  • Cách thực thi SQL tùy chỉnh khi di chuyển bằng RunSQL hành động
  • Di chuyển có thể đảo ngược là gì và cách tạo RunSQL hành động có thể đảo ngược
  • Di chuyển nguyên tử là gì và cách thay đổi hành vi mặc định theo nhu cầu của bạn
  • Cách thực hiện an toàn các quá trình di chuyển phức tạp trong Django

Sự tách biệt giữa mô hình và trạng thái cơ sở dữ liệu là một khái niệm quan trọng. Khi bạn hiểu nó và cách sử dụng nó, bạn có thể khắc phục nhiều hạn chế của các hoạt động di chuyển tích hợp sẵn. Một số trường hợp sử dụng cần lưu ý bao gồm thêm chỉ mục đã được tạo trong cơ sở dữ liệu và cung cấp các đối số cụ thể của nhà cung cấp cho các lệnh 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. Sử dụng ODBC với Dịch vụ Đăng nhập Một lần của Salesforce và Active Directory (ADFS) (SSO)

  2. 3 mẹo hàng đầu bạn cần biết để viết chế độ xem SQL nhanh hơn

  3. Kết nối Truy vấn SQL FlySpeed ​​với Salesforce.com

  4. Sử dụng Docker trên Azure Container Service với Swarm Cluster

  5. Làm việc với dữ liệu Java trong Alteryx