社交应用的数据存储与实现
目录
一、 概述
二、 整体架构
三、 前台交互
四、 后台存储
(一) 、数据文件
(二) 、数据结构
五、 支持的操作及其算法
(一) 、注册用户
(二) 、登录用户
(三) 、修改个人信息及修改密码
(四) 、删除用户
(五) 、关注/取消关注/移除粉丝
(六) 、用户查找
(七) 、发布消息
(八) 、查看用户消息
(九) 、查看新鲜事推送(即《课程设计》所指的消息列表)
(十) 、转发消息
(十一) 、删除消息
六、 测试
(一) 、正确性测试
(二) 、性能测试
七、 缺陷、展望及感悟
一、概述
本文介绍根据《社交应用的数据存储与实现》一文中所提要求设计的程序,该程序为32位Windows控制台程序,用C++写成,用VS编译,因调用了WinAPI不具备可移植性。截至目前该程序实现了《课程设计》文中的所有基本要求以及除关注推荐和折叠消息以外的所有扩展要求,此外还实现了一个简易的输入框(详见下文)。
(一)数据文件
一共使用四个文件,分别为user.idx(用户索引),user.dat(用户信息),user.rel(用户间关注关系),user.msg(用户所发消息)。使用C++标准库的文件流操作文件,在类初始化时打开文件,程序退出时关闭文件,程序运行途中由前台调用后台执行某事务时,在事务执行的最后调用fstream的flush方法将数据写入硬盘,既保证了内存与文件数据的一致性,又不至于每次调用write方法都flush而造成不必要的大量硬盘读写操作。
(二)、数据结构
1.用户信息及其索引
用户的各项信息在设置时都有长度限制,存储在user.dat文件中,某用户的个人信息体现为一条定长的记录,用户记录一旦插入文件永不删除,删除用户时只对该记录做一标记以表示其已被删除,不可用于注册新用户,整个文件的结构为一系列用户记录按照用户注册的先后顺序依次排列,构成文件。用户记录如下所示:
User:
UserName 16 Bytes
Password 32 Bytes
Name 24 Bytes
{Gender [1] 8 Bytes
{Deleted[2]
{Phone[3-8]
Birthday 4 Bytes
Hometown 24 Bytes
InEdges 8 Bytes
OutEdges 8 Bytes
Message 8 Bytes
InCount 4 Bytes
OutCount 4 Bytes
MessageCount 4 Bytes
总计144字节
其中,电话号码采用64位整数存放,由于电话号码即使算上区号也无法占满64位的整数,因此在前台限制了用户最多输入14位的电话号码,从而利用其最高及次高字节来存储性别以及是否已被删除;InEdges,OutEdges指向关注链表,Message指向消息链表,Incount,OutCount,MessageCount分别为粉丝计数、关注计数与消息计数,详见关注与消息的介绍部分。
由于用户信息在user.dat文件中并没有任何有效的组织形式,为了快速找到某用户在user.dat中的位置,还建立了user.idx文件作为索引。在该索引文件内存储了一棵171阶B+树,Block大小为4096字节,其根节点总是位于该文件内的第一个Block。通过测试可以发现,VS实现的C++标准库在进行文件读写时,是以4096字节为单位进行读写的,此外Windows下NTFS文件系统默认的簇大小也是4096字节,因此以此作为参考,我将B+树的Block大小选定为了4096字节,以期与操作系统及标准库的buffer形成最好的配合。
本文转载自:http://www.biyezuopin.vip/onews.asp?id=16549
#include "stdafx.h"
#include "user.h"
#include <string>
void User::initialize(string un, string pw, string na, bool ism, date bir, long long pn, string ht) {
username = un;
password = pw;
name = na;
ismale = ism;
birthday = bir;
phonenum = pn;
hometown = ht;
isdeleted = false;
in_edges = NULL;
out_edges = NULL;
message = NULL;
in_count = 0;
out_count = 0;
message_count = 0;
}
void User::initialize(char (&record)[RECORD_LENGTH]) {
username.clear();
for (int i = 0; i < 16; i++) {
if (record[i] == '\0')
break;
else
username.push_back(record[i]);
}
password.clear();
for (int i = PASSWORD; i < PASSWORD + 32; i++) {
if (record[i] == '\0')
break;
else
password.push_back(record[i]);
}
name.clear();
for (int i = NAME; i < NAME + 24; i++){
if (record[i] == '\0')
break;
else
name.push_back(record[i]);
}
memcpy(&phonenum, record + PHONE_GENDOR, 8);
if (phonenum >> 56)
ismale = true;
else
ismale = false;
phonenum &= 0x00FFFFFFFFFFFFFF;
if (phonenum >> 48)
isdeleted = true;
else
isdeleted = false;
phonenum &= 0x0000FFFFFFFFFFFF;
memcpy(&birthday.year, record + BIRTHDAY, 2);
birthday.month = record[BIRTHDAY + 2];
birthday.day = record[BIRTHDAY + 3];
hometown.clear();
for (int i = HOMETOWN; i < HOMETOWN + 24; i++){
if (record[i] == '\0')
break;
else
hometown.push_back(record[i]);
}
memcpy(&in_edges, record + IN_EDGES, 8);
memcpy(&out_edges, record + OUT_EDGES, 8);
memcpy(&message, record + MESSAGE, 8);
memcpy(&in_count, record + IN_COUNT, 4);
memcpy(&out_count, record + OUT_COUNT, 4);
memcpy(&message_count, record + MESSAGE_COUNT, 4);
}
string User::get_record() {
char record[RECORD_LENGTH] = {};
for (size_t i = 0; i < username.size(); i++)
record[i] = username[i];
for (size_t i = 0; i < password.size(); i++)
record[i + PASSWORD] = password[i];
for (size_t i = 0; i < name.size(); i++)
record[i + NAME] = name[i];
long long phone_gendor = phonenum;
if (ismale)
phone_gendor |= 0x0100000000000000; //最高字节标明性别
if (isdeleted)
phone_gendor |= 0x0001000000000000; //次高字节标明是否删除
memcpy(record + PHONE_GENDOR, &phone_gendor, 8);
memcpy(record + BIRTHDAY, &birthday.year, 2);
record[BIRTHDAY + 2] = birthday.month;
record[BIRTHDAY + 3] = birthday.day;
for (size_t i = 0; i < hometown.size(); i++)
record[i + HOMETOWN] = hometown[i];
memcpy(record + IN_EDGES, &in_edges, 8);
memcpy(record + OUT_EDGES, &out_edges, 8);
memcpy(record + MESSAGE, &message, 8);
memcpy(record + IN_COUNT, &in_count, 4);
memcpy(record + OUT_COUNT, &out_count, 4);
memcpy(record + MESSAGE_COUNT, &message_count, 4);
return string(record, RECORD_LENGTH);
}
void User::print_info(bool followed, bool fans) {
cout << username << " 关注:" << out_count << " 粉丝:" << in_count << " 微博:" << message_count << endl;
if (followed)
if (fans)
cout << "互相关注 ";
else
cout << "已关注 ";
else
if (fans)
cout << "我的粉丝 ";
if (ismale)
cout << "男 ";
else
cout << "女 ";
cout << birthday.year << '-' << int(birthday.month) << '-' << int(birthday.day) << " ";
cout << "姓名: " << name << endl;
cout << "联系电话: ";
if (phonenum)
cout << phonenum;
else
cout << "无";
cout << " 来自: ";
if (!hometown.empty())
cout << hometown << endl;
else
cout << "无" << endl;
}
bool User::fits(Query &query, string ¤t_username) {
if (isdeleted || username == current_username)
return false;
if (query.name.second && name != query.name.first)
return false;
if (query.ismale.second && ismale != query.ismale.first)
return false;
if (query.birthday_start.second) {
if (query.birthday_end.second) {
if (birthday < query.birthday_start.first ||
birthday > query.birthday_end.first) //生日区间理解为闭区间
return false;
}
else if (birthday != query.birthday_start.first)
return false;
}
if (query.phonenum.second && phonenum != query.phonenum.first)
return false;
if (query.hometown.second && hometown != query.hometown.first)
return false;
return true;
}
bool date::operator==(const date &other) {
if (year == other.year && month == other.month &&
day == other.day)
return true;
else
return false;
}
bool date::operator!=(const date &other) {
if (year != other.year || month != other.month ||
day != other.day)
return true;
else
return false;
}
bool date::operator<(const date &other) {
if (year < other.year)
return true;
else if (year == other.year) {
if (month < other.month)
return true;
else if (month == other.month && day < other.day)
return true;
}
return false;
}
bool date::operator>(const date &other) {
if (year > other.year)
return true;
else if (year == other.year) {
if (month > other.month)
return true;
else if (month == other.month && day > other.day)
return true;
}
return false;
}
bool date::operator<=(const date &other) {
return !(*this > other);
}
bool date::operator>=(const date &other) {
return !(*this < other);
}
bool operator <(const User &a, const User &b) {
return a.username < b.username;
}
bool operator !=(const date &a, const date &b) {
if (a.year != b.year || a.month != b.month ||
a.day != b.day)
return true;
else
return false;
}
bool operator !=(const User &a, const User &b) {
if (a.birthday != b.birthday)
return true;
if (a.hometown != b.hometown)
return true;
if (a.in_count != b.in_count)
return true;
if (a.in_edges != b.in_edges)
return true;
if (a.isdeleted != b.isdeleted)
return true;
if (a.ismale != b.ismale)
return true;
if (a.message != b.message)
return true;
if (a.message_count != b.message_count)
return true;
if (a.name != b.name)
return true;
if (a.out_count != b.out_count)
return true;
if (a.out_edges != b.out_edges)
return true;
if (a.password != b.password)
return true;
if (a.phonenum != b.phonenum)
return true;
if (a.username != b.username)
return true;
return false;
}