2012-05-03 29 views
8

Tôi có ứng dụng Google App Engine sử dụng phiên bản Google Cloud SQL để lưu trữ dữ liệu. Tôi cần trường hợp của tôi để có thể phục vụ hàng trăm khách hàng tại một thời điểm, thông qua các cuộc gọi yên tĩnh, mỗi kết quả trong một hoặc một số truy vấn DB. Tôi đã gói các phương thức cần truy cập DB và lưu trữ xử lý vào kết nối DB trong os.environ. Xem this SO câu hỏi/câu trả lời về cơ bản cách tôi đang thực hiện.Giới hạn kết nối cho Google Cloud SQL từ Máy ứng dụng là gì và cách tốt nhất để sử dụng lại các kết nối DB?

Tuy nhiên, ngay sau khi một vài trăm khách hàng kết nối với ứng dụng và kích hoạt cuộc gọi cơ sở dữ liệu của tôi, tôi bắt đầu nhận được những sai sót trong Google App bản ghi lỗi Engine (và ứng dụng của tôi trả 500, tất nhiên):

could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run 

Bất kỳ mẹo nào từ người dùng có kinh nghiệm về Google App Engine và Google Cloud SQL? Cảm ơn trước.

Dưới đây là các mã cho các trang trí tôi sử dụng xung quanh phương pháp đòi hỏi phải có kết nối DB:

def with_db_cursor(do_commit = False): 
    """ Decorator for managing DB connection by wrapping around web calls. 
    Stores connections and open connection count in the os.environ dictionary 
    between calls. Sets a cursor variable in the wrapped function. Optionally 
    does a commit. Closes the cursor when wrapped method returns, and closes 
    the DB connection if there are no outstanding cursors. 

    If the wrapped method has a keyword argument 'existing_cursor', whose value 
    is non-False, this wrapper is bypassed, as it is assumed another cursor is 
    already in force because of an alternate call stack. 

    Based mostly on post by : Shay Erlichmen 
    At: https://stackoverflow.com/a/10162674/379037 
    """ 

    def method_wrap(method): 
     def wrap(*args, **kwargs): 
      if kwargs.get('existing_cursor', False): 
       #Bypass everything if method called with existing open cursor 
       vdbg('Shortcircuiting db wrapper due to exisiting_cursor') 
       return method(None, *args, **kwargs) 

      conn = os.environ.get("__data_conn") 

      # Recycling connection for the current request 
      # For some reason threading.local() didn't work 
      # and yes os.environ is supposed to be thread safe 
      if not conn:      
       conn = _db_connect() 
       os.environ["__data_conn"] = conn 
       os.environ["__data_conn_ref"] = 1 
       dbg('Opening first DB connection via wrapper.') 
      else: 
       os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] + 1) 
       vdbg('Reusing existing DB connection. Count using is now: {0}', 
        os.environ["__data_conn_ref"])   
      try: 
       cursor = conn.cursor() 
       try: 
        result = method(cursor, *args, **kwargs) 
        if do_commit or os.environ.get("__data_conn_commit"): 
         os.environ["__data_conn_commit"] = False 
         dbg('Wrapper executing DB commit.') 
         conn.commit() 
        return result       
       finally: 
        cursor.close()      
      finally: 
       os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] - 
         1) 
       vdbg('One less user of DB connection. Count using is now: {0}', 
        os.environ["__data_conn_ref"]) 
       if os.environ["__data_conn_ref"] == 0: 
        dbg("No more users of this DB connection. Closing.") 
        os.environ["__data_conn"] = None 
        db_close(conn) 
     return wrap 
    return method_wrap 

def db_close(db_conn): 
    if db_conn: 
     try: 
      db_conn.close() 
     except: 
      err('Unable to close the DB connection.',) 
      raise 
    else: 
     err('Tried to close a non-connected DB handle.') 
+0

Bạn có chủ đề an toàn: true trong app.yaml? – proppy

+0

@proppy yes, tôi có. Cảm ơn. – JJC

+0

Sử dụng 'threadsafe: true' sẽ không hoạt động tốt với việc sử dụng os.environ vì các kết nối không thể được chia sẻ giữa các luồng. Xem câu trả lời của tôi http://stackoverflow.com/a/10438622/1373093 để biết giải pháp luồng an toàn. –

Trả lời

14

Câu trả lời ngắn: truy vấn của bạn có lẽ là quá chậm và máy chủ mysql không có đủ bài để xử lý tất cả các các yêu cầu bạn đang cố gắng gửi.

dài trả lời:

Là nền, Cloud SQL có hai giới hạn đó đang ở đây liên quan:

  • Connections: Những tương ứng với các đối tượng 'Conn' trong mã của bạn. Có một datastructure tương ứng trên máy chủ. Khi bạn có quá nhiều đối tượng này (hiện được định cấu hình là 1000), ít nhất gần đây được sử dụng sẽ tự động bị đóng. Khi kết nối bị đóng bên dưới bạn, bạn sẽ gặp lỗi kết nối không xác định (ApplicationError: 1007) vào lần tiếp theo bạn cố gắng sử dụng kết nối đó.
  • Yêu cầu đồng thời: Đây là các truy vấn đang thực hiện trên máy chủ. Mỗi truy vấn thực thi liên kết một chuỗi trong máy chủ, do đó, có giới hạn là 100. Khi có quá nhiều yêu cầu đồng thời, các yêu cầu tiếp theo sẽ bị từ chối với lỗi bạn đang nhận được (ApplicationError: 1033)

không có vẻ như giới hạn kết nối đang ảnh hưởng đến bạn, nhưng tôi muốn đề cập đến nó chỉ trong trường hợp.

Khi nói đến Yêu cầu đồng thời, việc tăng giới hạn có thể hữu ích, nhưng điều này thường làm cho vấn đề trở nên tồi tệ hơn. Có hai trường hợp chúng tôi đã thấy trong quá khứ:

  • Bế tắc: Truy vấn chạy dài đang khóa một hàng quan trọng của cơ sở dữ liệu. Tất cả các truy vấn tiếp theo đều chặn trên khóa đó. Các ứng dụng lần ra trên các truy vấn, nhưng họ tiếp tục chạy trên máy chủ, buộc lên những chủ đề cho đến khi kích hoạt deadlock timeout.
  • Truy vấn chậm: Mỗi truy vấn thực sự rất chậm. Điều này thường xảy ra khi truy vấn yêu cầu sắp xếp tệp tạm thời. Ứng dụng sẽ hết thời gian và thử lại truy vấn trong khi lần thử đầu tiên của truy vấn vẫn chạy và đếm ngược với giới hạn yêu cầu đồng thời. Nếu bạn có thể tìm thấy thời gian truy vấn trung bình của mình, bạn có thể ước tính số lượng QPS mà cá thể mysql của bạn có thể hỗ trợ (ví dụ: 5 ms cho mỗi truy vấn có nghĩa là 200 QPS cho mỗi chuỗi. mỗi truy vấn có nghĩa là 2000 QPS.)

Bạn nên sử dụng EXPLAINSHOW ENGINE INNODB STATUS để xem vấn đề nào trong hai vấn đề đang diễn ra.

Tất nhiên, cũng có thể bạn chỉ đang lái một tấn lưu lượng truy cập tại trường hợp của bạn và chỉ có không đủ chủ đề. Trong trường hợp đó, bạn có thể sẽ được tối đa ra cpu cho ví dụ anyway, do đó, thêm nhiều chủ đề sẽ không giúp đỡ.

+0

Cảm ơn Ken. Tôi không thấy bất kỳ lỗi nào 1007, và tôi tin rằng tôi đang đóng các kết nối một cách chính xác. Tôi cũng không tin rằng các truy vấn chậm là thủ phạm, vì tất cả các truy vấn khá đơn giản (không có tham gia, vài nhóm-by) các bảng nhỏ (cả theo chiều ngang và chiều dọc), có các chỉ mục (hoặc các ràng buộc duy nhất tương đương) trên tất cả cột đang được tìm kiếm, vv Maxing ra CPU trên trường hợp không có khả năng là một vấn đề, hoặc từ trạng thái bảng điều khiển. Vì vậy, tôi nghĩ rằng điều này thu hẹp nó xuống deadlocks. Tôi đang tìm hiểu thêm về nó. Có thể ping cho bạn lời khuyên sau này, nếu đó là okay. – JJC

+0

Trong đoạn mã của bạn ở trên, tôi không thấy vị trí đóng kết nối. Có lẽ bạn không bao gồm tất cả? –

+1

Ngoài ra, một GROUP BY vẫn có thể dẫn đến một filesort, do đó, ngay cả một có thể làm tổn thương bạn. –

4

tôi đọc từ tài liệu và nhận thấy có một kết nối 12/Ví dụ giới hạn:

Look cho "Mỗi dụ App Engine không thể có nhiều hơn 12 kết nối đồng thời đến một trường hợp Google Cloud SQL." trong https://developers.google.com/appengine/docs/python/cloud-sql/

+0

Cảm ơn. Tôi đã không nhìn thấy điều này trong các tài liệu trở lại khi tôi đăng câu hỏi hai năm + trước, nhưng có lẽ nó đã có và tôi chỉ bỏ lỡ nó. :-D Trong mọi trường hợp, nó giúp giải thích lý do tại sao dự án của tôi sẽ không quy mô và cuối cùng phải bị bỏ rơi. :-( – JJC

+0

Vì vậy, bạn đã giải quyết vấn đề? Ngoài việc thanh toán bù trừ các kết nối cũ là bạn có thể tìm ra bất cứ điều gì khác? Kinda chạy vào cùng một vấn đề gần đây – smaura777

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