Làm thế nào để chạy cơ sở dữ liệu thử nghiệm của Django chỉ trong bộ nhớ?


125

Các bài kiểm tra đơn vị Django của tôi mất nhiều thời gian để chạy, vì vậy tôi đang tìm cách để tăng tốc. Tôi đang xem xét việc cài đặt một ổ SSD , nhưng tôi biết nó cũng có nhược điểm. Tất nhiên, có những điều tôi có thể làm với mã của mình, nhưng tôi đang tìm cách sửa lỗi cấu trúc. Ngay cả khi chạy thử nghiệm đơn lẻ cũng chậm vì cơ sở dữ liệu cần phải được xây dựng lại / di chuyển về phía nam mỗi lần. Vì vậy, đây là ý tưởng của tôi ...

Vì tôi biết cơ sở dữ liệu kiểm tra sẽ luôn khá nhỏ, tại sao tôi không thể cấu hình hệ thống để luôn giữ toàn bộ cơ sở dữ liệu kiểm tra trong RAM? Không bao giờ chạm vào đĩa cả. Làm cách nào để định cấu hình cái này trong Django? Tôi muốn tiếp tục sử dụng MySQL vì đó là những gì tôi sử dụng trong sản xuất, nhưng nếu SQLite  3 hoặc thứ gì khác làm cho điều này trở nên dễ dàng, tôi sẽ đi theo hướng đó.

SQLite hoặc MySQL có tùy chọn chạy hoàn toàn trong bộ nhớ không? Có thể định cấu hình đĩa RAM và sau đó định cấu hình cơ sở dữ liệu kiểm tra để lưu trữ dữ liệu của nó ở đó, nhưng tôi không biết làm thế nào để nói với Django / MySQL sử dụng một thư mục dữ liệu khác cho một cơ sở dữ liệu nhất định, đặc biệt là khi nó bị xóa và tái tạo mỗi lần chạy. (Tôi đang dùng Mac FWIW.)

Câu trả lời:


164

Nếu bạn đặt công cụ cơ sở dữ liệu của mình thành sqlite3 khi bạn chạy thử nghiệm, Django sẽ sử dụng cơ sở dữ liệu trong bộ nhớ .

Tôi đang sử dụng mã như thế này settings.pyđể đặt công cụ thành sqlite khi chạy thử nghiệm:

if 'test' in sys.argv:
    DATABASE_ENGINE = 'sqlite3'

Hoặc trong Django 1.2:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'sqlite3'}

Và cuối cùng trong Django 1.3 và 1.4:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}

(Đường dẫn đầy đủ đến phần phụ trợ không thực sự cần thiết với Django 1.3, nhưng làm cho cài đặt tương thích về phía trước.)

Bạn cũng có thể thêm dòng sau, trong trường hợp bạn gặp vấn đề với việc di chuyển Nam:

    SOUTH_TESTS_MIGRATE = False

9
Đúng chính xác. Tôi nên đặt điều đó trong câu trả lời của tôi! Kết hợp điều đó với SOUTH_TESTS_MIGRATE = Sai và các bài kiểm tra của bạn sẽ nhanh hơn rất nhiều.
Etienne

7
này tuyệt vời. trên các thiết lập django mới hơn sử dụng dòng này: 'ĐỘNG CƠ': 'sqlite3' nếu 'thử nghiệm' trong sys.argv khác 'django.db.backends.mysql',
mjallday

3
@Tomasz Zielinski - Hmm, nó phụ thuộc vào những gì bạn đang thử nghiệm. Nhưng tôi hoàn toàn đồng ý rằng, cuối cùng và thỉnh thoảng, bạn cần chạy thử nghiệm với cơ sở dữ liệu thực của bạn (Postgres, MySQL, Oracle ...). Nhưng chạy thử nghiệm trong bộ nhớ với sqlite có thể giúp bạn tiết kiệm rất nhiều thời gian.
Etienne

3
Tôi đảo ngược -1 thành +1: như tôi thấy bây giờ, sử dụng sqlite nhanh hơn nhiều để chạy nhanh và chuyển sang MySQL ví dụ như các bài kiểm tra hàng ngày cuối cùng. (Lưu ý rằng tôi phải thực hiện một chỉnh sửa giả để mở khóa bỏ phiếu)
Tomasz Zieliński

12
Thận trọng với điều này "test" in sys.argv; nó có thể kích hoạt khi bạn không muốn nó, vd manage.py collectstatic -i test. sys.argv[1] == "test"là một điều kiện chính xác hơn không nên có vấn đề đó.
keturn

83

Tôi thường tạo một tệp cài đặt riêng cho các thử nghiệm và sử dụng nó trong lệnh thử nghiệm, vd

python manage.py test --settings=mysite.test_settings myapp

Nó có hai lợi ích:

  1. Bạn không cần phải kiểm tra testhoặc bất kỳ từ ma thuật nào như vậy trong sys.argv, test_settings.pycó thể chỉ đơn giản là

    from settings import *
    
    # make tests faster
    SOUTH_TESTS_MIGRATE = False
    DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}

    Hoặc bạn có thể điều chỉnh thêm cho nhu cầu của mình, tách biệt rõ ràng các cài đặt kiểm tra khỏi cài đặt sản xuất.

  2. Một lợi ích khác là bạn có thể chạy thử nghiệm với công cụ cơ sở dữ liệu sản xuất thay vì sqlite3 để tránh các lỗi tinh vi, vì vậy trong khi phát triển sử dụng

    python manage.py test --settings=mysite.test_settings myapp

    và trước khi cam kết chạy mã một lần

    python manage.py test myapp

    chỉ để chắc chắn rằng tất cả các bài kiểm tra đang thực sự vượt qua.


2
Tôi thích cách tiếp cận này. Tôi có một loạt các tệp cài đặt khác nhau và sử dụng chúng cho các môi trường máy chủ khác nhau nhưng tôi đã không nghĩ đến việc sử dụng phương pháp này để chọn cơ sở dữ liệu thử nghiệm khác. Cảm ơn ý kiến.
Alexis Bellido

Xin chào Anurag, tôi đã thử điều này nhưng các cơ sở dữ liệu khác của tôi được đề cập trong cài đặt cũng được thực thi. Tôi không thể tìm ra lý do chính xác.
Bhupesh Pant

Câu trả lời tốt đẹp. Tôi tự hỏi làm thế nào để chỉ định một tập tin cài đặt khi chạy thử nghiệm thông qua phạm vi bảo hiểm.
Wtower 10/03/2015

Đó là một cách tiếp cận tốt, nhưng không DRY. Django đã biết rằng bạn đang chạy thử nghiệm. Nếu bạn có thể 'kết nối' kiến ​​thức này bằng cách nào đó, bạn sẽ được thiết lập. Thật không may, tôi tin rằng cần phải mở rộng lệnh quản lý. Có lẽ sẽ hợp lý khi đặt cái chung này trong lõi của khung, ví dụ, bằng cách đặt một cài đặt QUẢN LÝ_COMMAND được đặt thành lệnh hiện tại bất cứ khi nào được gọi là Manage.txt hoặc một cái gì đó có hiệu lực.
DylanYoung

2
@DylanYoung bạn có thể làm cho nó khô bằng cách đưa các cài đặt chính vào test_sinstall và chỉ ghi đè những thứ bạn muốn kiểm tra.
Anurag Uniyal

22

MySQL hỗ trợ một công cụ lưu trữ có tên "MEMORY", mà bạn có thể định cấu hình trong cấu hình cơ sở dữ liệu của mình ( settings.py) như sau:

    'USER': 'root',                      # Not used with sqlite3.
    'PASSWORD': '',                  # Not used with sqlite3.
    'OPTIONS': {
        "init_command": "SET storage_engine=MEMORY",
    }

Lưu ý rằng công cụ lưu trữ MEMOR không hỗ trợ các cột blob / văn bản, vì vậy nếu bạn đang sử dụng công cụ django.db.models.TextFieldnày sẽ không hiệu quả với bạn.


5
+1 để đề cập đến việc thiếu hỗ trợ cho các cột blob / văn bản. Nó cũng dường như không hỗ trợ các giao dịch ( dev.mysql.com/doc/refman/5.6/en/memory-st Storage-engine.html ).
Tuukka Mustonen

Nếu bạn thực sự muốn kiểm tra trong bộ nhớ, có lẽ bạn nên đi với sqlite ít nhất là hỗ trợ các giao dịch.
nguyên tử77

15

Tôi không thể trả lời câu hỏi chính của bạn, nhưng có một vài điều bạn có thể làm để tăng tốc mọi thứ.

Trước tiên, hãy đảm bảo rằng cơ sở dữ liệu MySQL của bạn được thiết lập để sử dụng InnoDB. Sau đó, nó có thể sử dụng các giao dịch để khôi phục trạng thái của db trước mỗi thử nghiệm, theo kinh nghiệm của tôi đã dẫn đến việc tăng tốc độ lớn. Bạn có thể truyền lệnh init cơ sở dữ liệu trong cài đặt của bạn (cú pháp Django 1.2):

DATABASES = {
    'default': {
            'ENGINE':'django.db.backends.mysql',
            'HOST':'localhost',
            'NAME':'mydb',
            'USER':'whoever',
            'PASSWORD':'whatever',
            'OPTIONS':{"init_command": "SET storage_engine=INNODB" } 
        }
    }

Thứ hai, bạn không cần phải di chuyển miền Nam mỗi lần. Đặt SOUTH_TESTS_MIGRATE = Falsetrong cài đặt của bạn và cơ sở dữ liệu sẽ được tạo bằng syncdb đơn giản, sẽ nhanh hơn nhiều so với chạy qua tất cả các lần di chuyển lịch sử.


Mẹo tuyệt vời! Nó giảm các bài kiểm tra của tôi từ 369 tests in 498.704sđến 369 tests in 41.334s . Điều này nhanh hơn gấp 10 lần!
Gabi Purcaru

Có một công tắc tương đương trong settings.py để di chuyển trong Django 1.7+ không?
Edward Newell

@EdwardNewell Không chính xác. Nhưng bạn có thể sử dụng --keepđể duy trì cơ sở dữ liệu và không yêu cầu bộ di chuyển hoàn chỉnh của bạn được áp dụng lại trên mỗi lần chạy thử. Di cư mới vẫn sẽ chạy. Nếu bạn thường xuyên chuyển đổi giữa các chi nhánh, thật dễ dàng để chuyển sang trạng thái không nhất quán (bạn có thể hoàn nguyên các lần di chuyển mới trước khi chuyển đổi bằng cách thay đổi cơ sở dữ liệu sang cơ sở dữ liệu kiểm tra và chạy migrate, nhưng điều đó hơi khó).
DylanYoung

10

Bạn có thể thực hiện điều chỉnh kép:

  • sử dụng các bảng giao dịch: trạng thái đồ đạc ban đầu sẽ được thiết lập bằng cách sử dụng cơ sở dữ liệu rollback sau mỗi TestCase.
  • đưa thư mục dữ liệu cơ sở dữ liệu của bạn lên ramdisk: bạn sẽ đạt được nhiều như liên quan đến việc tạo cơ sở dữ liệu và chạy thử nghiệm sẽ nhanh hơn.

Tôi đang sử dụng cả hai thủ thuật và tôi khá hạnh phúc.

Cách thiết lập nó cho MySQL trên Ubuntu:

$ sudo service mysql stop
$ sudo cp -pRL /var/lib/mysql /dev/shm/mysql

$ vim /etc/mysql/my.cnf
# datadir = /dev/shm/mysql
$ sudo service mysql start

Coi chừng, nó chỉ để thử nghiệm, sau khi khởi động lại cơ sở dữ liệu của bạn từ bộ nhớ bị mất!


cảm ơn! làm việc cho tôi Tôi không thể sử dụng sqlite, vì tôi đang sử dụng các tính năng dành riêng cho mysql (chỉ mục toàn văn). Đối với người dùng Ubuntu, bạn sẽ phải chỉnh sửa cấu hình apparmor của mình để cho phép truy cập mysqld vào / dev / shm / mysql
Ivan Virabyan

Chúc mừng cho những người đứng đầu Ivan và Potr. Vô hiệu hóa hồ sơ cá nhân mysql AppArmor cho bây giờ, nhưng không tìm thấy một hướng dẫn để tùy chỉnh hồ sơ địa phương có liên quan: blogs.oracle.com/jsmyth/entry/apparmor_and_mysql
trojjer

Hừm. Tôi đã thử tùy chỉnh cấu hình cục bộ để cấp cho mysqld quyền truy cập vào đường dẫn / dev / shm / mysql và nội dung của nó, nhưng dịch vụ chỉ có thể bắt đầu ở chế độ 'khiếu nại' (lệnh aa-phàn nàn) và không 'thực thi' đối với một số Lý do ... Một câu hỏi cho một diễn đàn khác! Điều tôi không thể hiểu là làm thế nào mà không có "khiếu nại" nào khi nó hoạt động, ngụ ý rằng mysqld không vi phạm hồ sơ ...
trojjer

4

Một cách tiếp cận khác: có một phiên bản khác của MySQL chạy trong tempfs sử dụng RAM RAM. Hướng dẫn trong bài đăng trên blog này: Tăng tốc MySQL để thử nghiệm trong Django .

Ưu điểm:

  • Bạn sử dụng cùng một cơ sở dữ liệu mà máy chủ sản xuất của bạn sử dụng
  • không cần thay đổi cấu hình mysql mặc định của bạn

2

Mở rộng câu trả lời của Anurag Tôi đã đơn giản hóa quy trình bằng cách tạo cùng test_sinstall và thêm phần sau vào Manage.txt

if len(sys.argv) > 1 and sys.argv[1] == "test":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.test_settings")
else:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

có vẻ sạch hơn vì sys đã được nhập và Manage.txt chỉ được sử dụng thông qua dòng lệnh, do đó không cần phải làm lộn xộn cài đặt


2
Thận trọng với điều này "test" in sys.argv; nó có thể kích hoạt khi bạn không muốn nó, vd manage.py collectstatic -i test. sys.argv[1] == "test"là một điều kiện chính xác hơn không nên có vấn đề đó.
keturn

2
@keturn theo cách này, nó tạo ra một ngoại lệ khi chạy ./manage.pymà không có đối số (ví dụ: để xem plugin nào có sẵn, giống như --help)
Antony Hatchkins

1
@AntonyHatchkins Điều đó không quan trọng để giải quyết:len(sys.argv) > 1 and sys.argv[1] == "test"
DylanYoung

1
@DylanYoung Vâng, đó chính xác là những gì tôi muốn Alvin thêm vào giải pháp của mình nhưng anh ấy không đặc biệt quan tâm đến việc cải thiện nó. Dù sao, nó trông giống như một hack nhanh hơn là giải pháp hợp pháp.
Antony Hatchkins

1
Không nhìn vào câu trả lời này trong một thời gian, tôi đã cập nhật đoạn trích để phản ánh sự cải thiện của @ DylanYoung
Alvin

0

Sử dụng dưới đây trong của bạn setting.py

DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.