mirror of
https://github.com/mangosfour/server.git
synced 2025-12-18 01:37:01 +00:00
[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:
parent
9bc37afa28
commit
631ce36680
19 changed files with 655 additions and 547 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue