引言:
我们的项目是做即时语音聊天,服务器需要保持用户在线信息。在语音包来了之后需要立刻查询目标用户地址,然后发送出去。假设用户说话的时候一秒钟发送6个语音包。那么针对这一个用户就需要在一秒内查询6次数据库。想象当服务器内有1000个用户同时说话的时候,这个查询量就会很大。
针对这种类似的程序启动才会产生数据,程序退出清空数据,需要快速查询的应用,把这个需要查询的数据放入数据库中就有些不太合适。数据库中的数据查询分3个步骤实现:首先,通过tcp把查询请求发到数据库服务器;接下来,数据库服务器查询相应数据,之后,数据库服务器把查询结果通过tcp发还给应用程序。通过数据库查询数据,即使查询数据过程再短,也还是需要经过两次网络传输。为了提高查询速度,相应的解决方法是把数据放入应用程序的进程空间的内存中。由应用程序自己实现一个类似 数据库表管理的一个功能,相应的针对具体的查询条件实现简单的索引。暂且把这种放在自己进程空间内存中的数据库表叫 内存表----或许这个称谓不官方,也不科学。
下面结合具体的代码实现一个简单的内存表。
首先定义一个表结构,
struct T_Table
{
int nId; //主键
int nItem1;
int nItem2;
string sName;
};
接下来确定查询条件。假设需要针对 nId,sName,做两个索引。其中nId是唯一索引,sName不是唯一索引。
根据上面的需求,我们需要实现下面这样一个类:
class CTable
{
public:
//构造函数
CTable();
//析构函数
~CTable();
//以id为索引的查询
T_Table* m_GetById(int nId);
//以name为索引的查询
vector m_GetByName(string sName);
//插入数据
void m_Insert(int nId,int nItem1,int nItem2,string sName);
//删除数据
void m_Delete(int nId);
//根据id进行数据更新呢
void m_UpdateById(int nId,int nItem1);
//根据name进行数据更新
void m_UpdateByName(string nName,int nItem1);
protected:
private:
//以id为索引的数据存储
map m_IndexDataById;
//以name为索引的数据存储
multimap m_IndexDataByName;
//读写锁
pthread_rwlock_t m_Lock;
};
下面给出一个简单的实现代码
CTable::CTable()
{
//初始化锁
pthread_rwlock_init(&m_Lock,NULL);
}
CTable::~CTable()
{
pthread_rwlock_wrlock(&m_Lock);
//释放数据内存
for (map::iterator it = m_IndexDataById.begin();it!= m_IndexDataById.end();it++)
{
delete it->second;
it->second = NULL;
}
//清空索引数据
m_IndexDataById.clear();
m_IndexDataByName.clear();
pthread_rwlock_unlock(&m_Lock);
//释放锁资源
pthread_rwlock_destroy(&m_Lock);
}
T_Table* CTable::m_GetById(int nId)
{
pthread_rwlock_rdlock(&m_Lock);
map::iterator it = m_IndexDataById.find(nId);
if (it != m_IndexDataById.end())
{
pthread_rwlock_unlock(&m_Lock);
return it->second;
}
else
{
pthread_rwlock_unlock(&m_Lock);
return NULL;
}
}
vector CTable::m_GetByName(string sName)
{
vector vData;
pthread_rwlock_rdlock(&m_Lock);
for (multimap::iterator it = m_IndexDataByName.lower_bound(nName);
it != m_IndexDataByName.upper_bound(nName);it++)
{
vData.push_back(it->second);
}
pthread_rwlock_unlock(&m_Lock);
return vData;
}
void CTable::m_Insert(int nId,int nItem1,int nItem2,string sName)
{
T_Table* pData = new T_Table;
memset(pData,0,sizeof(T_Table));
pData->nId = nId;
pData->nItem1 = nItem1;
pData->nItem2 = nItem2;
pData->sName = sName;
pthread_rwlock_wrlock(&m_Lock);
m_IndexDataById[nId] = pData;
m_IndexDataByName[sName] = pData;
pthread_rwlock_unlock(&m_Lock);
}
void CTable::m_Delete(int nId)
{
pthread_rwlock_wrlock(&m_Lock);
map::iterator it = m_IndexDataById.find(nId);
m_IndexDataByName.erase(m_IndexDataByName.find(it->second->sName));
m_IndexDataById.erase(it);
pthread_rwlock_unlock(&m_Lock);
}
void CTable::m_UpdateById(int nId,int nItem1)
{
pthread_rwlock_wrlock(&m_Lock);
(m_IndexDataById.find(nId)->second)->nItem1 = nItem1;
pthread_rwlock_unlock(&m_Lock);
}
void CTable::m_UpdateByName(string nName,int nItem1)
{
pthread_rwlock_wrlock(&m_Lock);
for (multimap::iterator it = m_IndexDataByName.lower_bound(nName);
it != m_IndexDataByName.upper_bound(nName);it++)
{
it->second->nItem1 = nItem1;
}
pthread_rwlock_unlock(&m_Lock);
}
如果需要增加新的唯一索引,就增加一个map数据存储,map的key就是索引列,value就是对应的数据行。相应的如果需要增加一个非唯一索引,就增加一个multimap的数据结构。在增加了索引之后相应的要增加索引相对的查询,更新函数。这里采用map去存储数据应该能够满足表大小5000行以下的数据查询需求。如果数据行数超过5000行,可以考虑采用hashmap存储数据。同时考虑采用多个读写锁,一个读写锁管理一部分数据。
这只是我在应用中想到的一个解决方法,如果有更好的解决方法,希望多多指正。
阅读(2902) | 评论(0) | 转发(0) |