Home

Tags

Тестирование затрат на подключение к БД в web

2010-02-23 web sqlite mysql

Существую некоторые web-платформы которые не позволяют держать открытое соединение к БД, например когда для каждого соединения создается поток. В этом случае нужно производить подключение к БД на каждое обращение клиента либо делать "вешний" механизм поддержки открытого соединения. В данной заметке производится измерение затрат на подключение к БД.

sqlite3

Исходник:
def connect():
    import sqlite3
    con = sqlite3.connect('sample.db')

from timeit import Timer
t = Timer("connect()", "from __main__ import connect")
print t.timeit(number=100000)
на моем хосте 100к подключений в среднем занимают 5,3 сек (0,053мс на подключение), если сравнить с временем генерации главной страницы (4мс на моем сайте), то подключение занимает 1,3% от времени генерации станицы.
Итого можно считать то время подключения к БД sqlite не дает существенную нагрузку.

MySQL 5.1.37

from timeit import Timer
t = Timer('_mysql.connect("localhost","user","1","test")', "import _mysql")
print t.timeit(number=100000)

Затраченное время в среднем составило 10.30 сек, это 0.1 мс на подключение

Перенесем на "реальный" стенд

Apache/2.2.12 + WSGI + MySQL 5.1.37
БД: 200к простых записей
Код с поддержкой открытого соединения
# coding: utf-8

import _mysql
import time
import random

db=_mysql.connect("localhost","user","x","test")

def application(environ, start_response):
    status = '200 OK'
    output = 'Hello world!!!<br/>'

    t1 = time.time()

    global db
    db.query('SELECT * FROM data WHERE val<%d' % random.randrange(1000,90000))
    r = db.store_result()

    output += 'time: %.2f<br/>' % ( (time.time() - t1) * 1000,)
    for row in r.fetch_row():
        pass

    response_headers = [('Content-type', 'text/html; charset=utf-8'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]

Код с открытием соединения при каждом подключении
# coding: utf-8

def application(environ, start_response):
    import _mysql
    import time
    import random

    status = '200 OK'
    output = 'Hello world!!!<br/>'

    db=_mysql.connect("localhost","user","x","test")

    t1 = time.time()
    db.query('SELECT * FROM data WHERE val<%d' % random.randrange(1000,90000))
    r = db.store_result()

    output += 'time: %.2f<br/>' % ( (time.time() - t1) * 1000,)
    for row in r.fetch_row():
        pass

    db.close()

    response_headers = [('Content-type', 'text/html; charset=utf-8'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]

Результаты

С поддержкой открытого соединения
 Complete requests:      10000
Failed requests:        2575
   (Connect: 0, Receive: 0, Length: 2575, Exceptions: 0)
Write errors:           0
Total transferred:      2247425 bytes
HTML transferred:       347425 bytes
Requests per second:    72.84 [#/sec] (mean)
Time per request:       13.729 [ms] (mean)

С открытием соединения при каждом подключении
 Complete requests:      10000
Failed requests:        1882
   (Connect: 0, Receive: 0, Length: 1882, Exceptions: 0)
Write errors:           0
Total transferred:      2248118 bytes
HTML transferred:       348118 bytes
Requests per second:    64.71 [#/sec] (mean)
Time per request:       15.452 [ms] (mean)

Итого: С поддержкой открытого соединения больше выигрыш составил 12,5% (при повышенном Failed requests)

Резюме: Где есть возможность держать открытое соединение - там нужно это использовать, где нет такой возможности то лучше использовать открытие соединения на каждом подключении вместо "костыля" для поддержки открытого соединения из вне т.к. затраты на подключение не слишком большие и будут уменьшаться в процентном соотношении по мере роста БД, или лучше стоит подумать о смене "платформы"