2016-02-15 15 views
7

Tôi đang tìm kiếm giải thích chi tiết về việc thực hiện các truy vấn được biên dịch. Tôi không thể hiểu làm thế nào họ chỉ cần biên dịch một lần và lợi thế đằng sau việc sử dụng chúngCác truy vấn được biên dịch trong slick thực sự hoạt động như thế nào?

+0

Không phải là chúng thực thi một lần. Nó được biên dịch một lần. – pedrofurla

+0

Tại sao họ cần được biên soạn nhiều lần? Và bạn có quen thuộc với các phát biểu đã chuẩn bị không? – pedrofurla

+0

@perdrofurla yep! chỉnh sửa ... bạn có thể cho tôi một lời giải thích rõ ràng? – imen

Trả lời

10

Giả sử câu hỏi này là về cách sử dụng, không phải là thực hiện nội bộ của các truy vấn biên soạn, đây là câu trả lời của tôi:

Khi bạn viết một Slick truy vấn, Slick thực sự tạo ra một cấu trúc dữ liệu nội bộ cho tất cả các biểu thức liên quan - một cây cú pháp trừu tượng (AST). Khi bạn muốn chạy truy vấn này, Slick lấy cấu trúc dữ liệu và dịch (hoặc nói cách khác là biên dịch) nó thành một chuỗi SQL. Đây có thể là một quá trình tốn thời gian khá nhiều thời gian hơn so với thực sự thực hiện các truy vấn SQL nhanh trên DB. Vì vậy, lý tưởng là chúng ta không nên làm bản dịch này cho SQL mỗi lần truy vấn cần phải được thực hiện. Nhưng làm thế nào để tránh nó? Bằng cách lưu trữ truy vấn SQL đã dịch/biên dịch.

Slick có thể làm điều gì đó như chỉ biên dịch nó lần đầu tiên và lưu nó vào lần sau. Nhưng nó không, bởi vì điều đó làm cho người dùng khó hơn lý do về thời gian thực hiện của Slick, bởi vì cùng một mã sẽ bị chậm lần đầu tiên, nhưng nhanh hơn sau đó. (Ngoài ra Slick sẽ cần phải nhận ra các truy vấn khi chúng được chạy lần thứ hai và tra cứu SQL trong một số bộ đệm ẩn nội bộ, điều này sẽ làm phức tạp việc triển khai).

Vì vậy, thay vào đó, Slick sẽ biên dịch truy vấn mọi lúc, trừ khi bạn lưu trữ bộ nhớ cache một cách rõ ràng. Điều này làm cho hành vi rất dễ dự đoán và cuối cùng dễ dàng hơn. Để lưu trữ bộ nhớ cache, bạn cần sử dụng Compiled và lưu trữ kết quả ở một nơi sẽ KHÔNG được tính toán lại vào lần tiếp theo bạn cần truy vấn. Vì vậy, sử dụng def như def q1 = Compiled(...) không có ý nghĩa nhiều, bởi vì nó sẽ biên dịch nó mọi lúc. Nó phải là val hoặc lazy val. Ngoài ra bạn có thể không muốn đặt val đó vào một lớp bạn khởi tạo nhiều lần. Thay vào đó, một địa điểm tốt là một số val trong một singleton Scala singleton cấp cao nhất object, chỉ được tính một lần và được giữ cho thời gian hoạt động của JVM.

Vì vậy, về mặt khác, Compiled không có gì huyền diệu. Nó chỉ cho phép bạn kích hoạt trình biên dịch Scala-to-SQL của Slick một cách rõ ràng và trả về một giá trị có chứa SQL. Quan trọng hơn, điều này cho phép kích hoạt trình biên dịch riêng biệt với việc thực thi truy vấn, cho phép bạn biên dịch một lần, nhưng chạy nó nhiều lần.

+0

Cảm ơn bạn đã giải thích !!! – Sky

5

Lợi thế rất dễ giải thích: Biên dịch truy vấn cần có thời gian, cả trong Slick và trong máy chủ cơ sở dữ liệu. Nếu bạn thực hiện cùng một truy vấn nhiều lần, thì việc biên dịch chỉ một lần sẽ nhanh hơn.

Slick cần biên dịch AST với các hoạt động thu thập vào câu lệnh SQL. (Trên thực tế, không có truy vấn được biên dịch, bạn luôn phải xây dựng AST trước tiên nhưng so với thời gian biên dịch cực kỳ nhanh.)

Máy chủ cơ sở dữ liệu phải xây dựng kế hoạch thực hiện cho truy vấn. Điều này có nghĩa là phân tích cú pháp truy vấn, dịch nó thành các hoạt động cơ sở dữ liệu tự nhiên và tìm các tối ưu hóa dựa trên bố cục dữ liệu (ví dụ: chỉ mục để sử dụng). Phần này có thể tránh được ngay cả khi bạn không sử dụng các truy vấn được biên dịch trong Slick, chỉ đơn giản bằng cách sử dụng các biến liên kết, để bạn luôn nhận được cùng một mã SQL cho các bộ tham số khác nhau. Một máy chủ cơ sở dữ liệu giữ một bộ nhớ cache của các kế hoạch thực hiện được biên dịch gần đây đã được sử dụng/biên dịch, miễn là câu lệnh SQL giống hệt nhau, kế hoạch thực hiện chỉ là tìm kiếm băm và không cần tính lại. Slick dựa vào loại bộ nhớ đệm này. Không có thông tin liên lạc trực tiếp từ Slick đến máy chủ cơ sở dữ liệu để tái sử dụng truy vấn cũ.

Đối với cách thức chúng được thực hiện, có thêm một số phức tạp để đối phó với streaming/phi trực tuyến và biên soạn/áp dụng/ad-hoc truy vấn trong cùng một cách, nhưng điểm mấu chốt thú vị là trong Compiled:

implicit def function1IsCompilable[A , B <: Rep[_], P, U](implicit ashape: Shape[ColumnsShapeLevel, A, P, A], pshape: Shape[ColumnsShapeLevel, P, P, _], bexe: Executable[B, U]): Compilable[A => B, CompiledFunction[A => B, A , P, B, U]] = new Compilable[A => B, CompiledFunction[A => B, A, P, B, U]] { 
    def compiled(raw: A => B, profile: BasicProfile) = 
    new CompiledFunction[A => B, A, P, B, U](raw, identity[A => B], pshape.asInstanceOf[Shape[ColumnsShapeLevel, P, P, A]], profile) 
} 

Điều này cung cấp cho bạn một đối tượng ngụ ý Compilable cho mỗi Function. Các phương pháp tương tự cho các vị từ 2 đến 22 được tạo tự động. Bởi vì các tham số riêng lẻ chỉ cần Shape, chúng cũng có thể là các bộ dữ liệu lồng nhau, HLists hoặc bất kỳ kiểu tùy chỉnh nào. (Chúng tôi vẫn cung cấp trừu tượng cho tất cả arities chức năng vì nó là cú pháp thuận tiện hơn để viết, nói, một Function10 hơn một Function1 mà phải mất một Tuple10 như là đối số của nó.)

Có một phương pháp trong Shape rằng chỉ tồn tại để hỗ trợ chức năng biên soạn:

/** Build a packed representation containing QueryParameters that can extract 
    * data from the unpacked representation later. 
    * This method is not available for shapes where Mixed and Unpacked are 
    * different types. */ 
def buildParams(extract: Any => Unpacked): Packed 

Biểu diễn "được đóng gói" được tạo bằng phương pháp này có thể tạo ra một AST chứa QueryParameter nút có loại chính xác. Chúng được xử lý giống như các chữ khác trong quá trình biên dịch, ngoại trừ các giá trị thực tế không được biết. Bộ giải nén bắt đầu là identity ở cấp cao nhất và được tinh chỉnh để trích xuất các phần tử bản ghi theo yêu cầu. Ví dụ, nếu bạn có tham số Tuple2, AST sẽ kết thúc với hai nút QueryParameter biết cách trích xuất tham số đầu tiên và thứ hai của bộ túp tại một điểm sau đó.

Điểm sau này là khi truy vấn được biên dịch là áp dụng. Việc thực hiện như vậy AppliedCompiledFunction sử dụng câu lệnh SQL được biên dịch trước (hoặc biên dịch nó một cách nhanh chóng khi bạn sử dụng nó lần đầu tiên) và điền vào các tham số câu lệnh bằng cách luồng giá trị đối số thông qua các trình giải nén.

+1

cảm ơn rất nhiều câu trả lời của bạn. Bây giờ, tôi hiểu được tiện ích của việc sử dụng các truy vấn được biên dịch nhưng tôi không thể thử chúng trong một ví dụ. Bạn có thể cho tôi một ví dụ cụ thể không. cảm ơn – imen

+1

Bạn có thể tìm thấy nhiều ví dụ ở đây: https://github.com/slick/slick/blob/master/slick-testkit/src/main/scala/com/typesafe/slick/testkit/tests/TemplateTest.scala – szeiger

Các vấn đề liên quan