[11045] Rewrite internals of DB layer. Simplify code and use less locking. Spawn and use separate connections for sync and async DB requests. Implement database connection pool for SELECT queries. Up to maximum 16 connections supported. Disable 'autocommit' mode for MySQL.

UPDATE YOUR CONFIGS!

Defaults:
LoginDatabaseConnections = 1
WorldDatabaseConnections = 1
CharacterDatabaseConnections = 1

If you are not using <mtmaps> patch do not change the default settings - this is useless. You can try following option in your MySQL config to squeeze even more performance from your DB:

[mysqld]
transaction-isolation = READ-COMMITTED

Great thanks to Undergarun, kero99 and selector for making tests and providing very useful feedback and DB statistics! Have fun :)

Signed-off-by: Ambal <pogrebniak@gala.net>
This commit is contained in:
Ambal 2011-01-19 22:04:54 +02:00
parent 9bc37afa28
commit 631ce36680
19 changed files with 655 additions and 547 deletions

View file

@ -18,19 +18,29 @@
#include "DatabaseEnv.h"
#include "Config/Config.h"
#include "Database/SqlOperations.h"
#include <ctime>
#include <iostream>
#include <fstream>
#define MIN_CONNECTION_POOL_SIZE 1
#define MAX_CONNECTION_POOL_SIZE 16
Database::~Database()
{
HaltDelayThread();
/*Delete objects*/
delete m_pResultQueue;
delete m_pAsyncConn;
for (int i = 0; i < m_pQueryConnections.size(); ++i)
delete m_pQueryConnections[i];
}
bool Database::Initialize(const char *)
bool Database::Initialize(const char * infoString, int nConns /*= 1*/)
{
// Enable logging of SQL commands (usally only GM commands)
// Enable logging of SQL commands (usually only GM commands)
// (See method: PExecuteLog)
m_logSQL = sConfig.GetBoolDefault("LogSQL", false);
m_logsDir = sConfig.GetStringDefault("LogsDir","");
@ -41,9 +51,74 @@ bool Database::Initialize(const char *)
}
m_pingIntervallms = sConfig.GetIntDefault ("MaxPingTime", 30) * (MINUTE * 1000);
//create DB connections
//setup connection pool size
if(nConns < MIN_CONNECTION_POOL_SIZE)
m_nQueryConnPoolSize = MIN_CONNECTION_POOL_SIZE;
else if(nConns > MAX_CONNECTION_POOL_SIZE)
m_nQueryConnPoolSize = MAX_CONNECTION_POOL_SIZE;
else
m_nQueryConnPoolSize = nConns;
//create connection pool for sync requests
for (int i = 0; i < m_nQueryConnPoolSize; ++i)
{
SqlConnection * pConn = CreateConnection();
if(!pConn->Initialize(infoString))
{
delete pConn;
return false;
}
m_pQueryConnections.push_back(pConn);
}
//create and initialize connection for async requests
m_pAsyncConn = CreateConnection();
if(!m_pAsyncConn->Initialize(infoString))
return false;
InitDelayThread();
return true;
}
SqlDelayThread * Database::CreateDelayThread()
{
assert(m_pAsyncConn);
return new SqlDelayThread(this, m_pAsyncConn);
}
void Database::InitDelayThread()
{
assert(!m_delayThread);
m_pResultQueue = new SqlResultQueue;
//New delay thread for delay execute
m_threadBody = CreateDelayThread(); // will deleted at m_delayThread delete
m_delayThread = new ACE_Based::Thread(m_threadBody);
}
void Database::HaltDelayThread()
{
if (!m_threadBody || !m_delayThread) return;
m_threadBody->Stop(); //Stop event
m_delayThread->wait(); //Wait for flush to DB
delete m_delayThread; //This also deletes m_threadBody
m_delayThread = NULL;
m_threadBody = NULL;
//stop async result queue
if(m_pResultQueue)
{
m_pResultQueue->Update();
delete m_pResultQueue;
m_pResultQueue = NULL;
}
}
void Database::ThreadStart()
{
}
@ -52,17 +127,52 @@ void Database::ThreadEnd()
{
}
void Database::ProcessResultQueue()
{
if(m_pResultQueue)
m_pResultQueue->Update();
}
void Database::escape_string(std::string& str)
{
if(str.empty())
return;
char* buf = new char[str.size()*2+1];
escape_string(buf,str.c_str(),str.size());
//we don't care what connection to use - escape string will be the same
m_pQueryConnections[0]->escape_string(buf,str.c_str(),str.size());
str = buf;
delete[] buf;
}
SqlConnection * Database::getQueryConnection()
{
int nCount = 0;
if(m_nQueryCounter == long(1 << 31))
m_nQueryCounter = 0;
else
nCount = ++m_nQueryCounter;
return m_pQueryConnections[nCount % m_nQueryConnPoolSize];
}
void Database::Ping()
{
const char * sql = "SELECT 1";
{
SqlConnection::Lock guard(m_pAsyncConn);
delete guard->Query(sql);
}
for (int i = 0; i < m_nQueryConnPoolSize; ++i)
{
SqlConnection::Lock guard(m_pQueryConnections[i]);
delete guard->Query(sql);
}
}
bool Database::PExecuteLog(const char * format,...)
{
if (!format)
@ -107,12 +217,6 @@ bool Database::PExecuteLog(const char * format,...)
return Execute(szQuery);
}
void Database::SetResultQueue(SqlResultQueue * queue)
{
m_queryQueues[ACE_Based::Thread::current()] = queue;
}
QueryResult* Database::PQuery(const char *format,...)
{
if(!format) return NULL;
@ -151,6 +255,29 @@ QueryNamedResult* Database::PQueryNamed(const char *format,...)
return QueryNamed(szQuery);
}
bool Database::Execute(const char *sql)
{
if (!m_pAsyncConn)
return false;
SqlTransaction * pTrans = m_TransStorage->get();
if(pTrans)
{
//add SQL request to trans queue
pTrans->DelayExecute(sql);
}
else
{
// Simple sql statement
pTrans = new SqlTransaction;
pTrans->DelayExecute(sql);
m_threadBody->Delay(pTrans);
}
return true;
}
bool Database::PExecute(const char * format,...)
{
if (!format)
@ -171,6 +298,18 @@ bool Database::PExecute(const char * format,...)
return Execute(szQuery);
}
bool Database::DirectExecute(const char* sql)
{
if(!m_pAsyncConn)
return false;
SqlTransaction trans;
trans.DelayExecute(sql);
trans.Execute(m_pAsyncConn);
return true;
}
bool Database::DirectPExecute(const char * format,...)
{
if (!format)
@ -191,6 +330,62 @@ bool Database::DirectPExecute(const char * format,...)
return DirectExecute(szQuery);
}
bool Database::BeginTransaction()
{
if (!m_pAsyncConn)
return false;
//initiate transaction on current thread
//currently we do not support queued transactions
m_TransStorage->init();
return true;
}
bool Database::CommitTransaction()
{
if (!m_pAsyncConn)
return false;
//check if we have pending transaction
if(!m_TransStorage->get())
return false;
//add SqlTransaction to the async queue
m_threadBody->Delay(m_TransStorage->detach());
return true;
}
bool Database::CommitTransactionDirect()
{
if (!m_pAsyncConn)
return false;
//check if we have pending transaction
if(!m_TransStorage->get())
return false;
//directly execute SqlTransaction
SqlTransaction * pTrans = m_TransStorage->detach();
pTrans->Execute(m_pAsyncConn);
delete pTrans;
return true;
}
bool Database::RollbackTransaction()
{
if (!m_pAsyncConn)
return false;
if(!m_TransStorage->get())
return false;
//remove scheduled transaction
m_TransStorage->reset();
return true;
}
bool Database::CheckRequiredField( char const* table_name, char const* required_name )
{
// check required field
@ -278,3 +473,31 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
return false;
}
Database::TransHelper::~TransHelper()
{
reset();
}
SqlTransaction * Database::TransHelper::init()
{
MANGOS_ASSERT(!m_pTrans); //if we will get a nested transaction request - we MUST fix code!!!
m_pTrans = new SqlTransaction;
return m_pTrans;
}
SqlTransaction * Database::TransHelper::detach()
{
SqlTransaction * pRes = m_pTrans;
m_pTrans = NULL;
return pRes;
}
void Database::TransHelper::reset()
{
if(m_pTrans)
{
delete m_pTrans;
m_pTrans = NULL;
}
}