应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

实现自己的数据库一

  一前言
  从上篇原创文章到现在又是新的一年,今天是2023年的大年初三,先在这里祝各位亲爱的老铁们新年快乐,身体健康,在新的一年里更帅气、更漂亮,都能完成自己的小目标。
  一直以来,我对数据存储还是比较感兴趣的,计算机从大的抽象层次来说,只干了三件事情,存入数据,计算,输出数据。所以数据的存储和搜索可以说是占据计算机的大半功能了。在工作中,也自己设计过一些存储和索引,不过是简单的哈希表的结构存储,对以B树为核心的数据库一直挺有兴趣,所以翻找了一些原理文章和源码研究了下,虽然原理看的不少,但是总觉得没有动手亲自写过还是学习的不够深刻,同时在github上找到了一步步写简单db的项目,于是开始借鉴学习后(代码参考后有所改进)有了这一系列文章。
  这次也不定啥目标了,怕坑挖大了,填不完,能写多少就写多少吧,同时希望自己写的这个开源,并将自己以后所学所知的一些优化的技巧应用在这上面,当一个练手的小项目吧,不要相信题目,题目只是吸引大家进来的,请原谅我厚着脸皮起的这么大的题目。二开始2。1名字和设计原则名字这是个练手的小的不能再小的数据库,最终很可能会基于B树来构建,至于后续的谁知道那,所以起了个名叫microdb,会用纯c开发,希望用标准的C开发,仿照sqlite,不会考虑多系统的兼容性,这是小玩具,仅此而已。设计原则简单!简单!简单!2。2关于数据库存储的疑问
  在设计需要持久化到硬盘索引的时候,觉得写的不够好,数据结构还有存储格式有很大的优化空间。总在想在Mysql等数据库上,上亿条记录最多通过四次的磁盘读就搜寻到了需要的数据,这个是怎么实现的?内存对应的数据是何时持久化到磁盘上的?B树上每个分支的指针怎么保存,内存里面就是地址,B树持久化到磁盘上,这个指针是磁盘块的地址吗?2。3sqlite
  只所以仿照sqlite,原因无非是它够小,只有一个c文件,功能少模仿简单。
  说明:整体来说sqlite将sql命令通过分词、解析和代码生成器最后生成字节码,在虚拟机上运行,直到它完成或返回结果、或遇到致命错误、或中断。2。3第一个版本
  主要开发工具vscode,采用xmake做c的构建工具,主要是比较简单和方便,可以跨平台使用,不了解的可以参考我的xmake构建工具的文章。新建个项目,最终展示如下:
  先来仿照sqlite的命令行实现个简单的解释器,它会循环执行:读取用户输入执行命令输出结果继续等待用户输出。关键代码如下:intmain(intargc,charargv){InputBufferinputbuffernewinputbuffer();while(true){printprompt();readinput(inputbuffer);if(strcmp(inputbufferbuffer,。exit)0){closeinputbuffer(inputbuffer);exit(EXITSUCCESS);}else{printf(unkowncommand:s。,inputbufferbuffer);}}return0;}
  其中InputBuffer为自定义的保存用户输入的缓冲区,读取一行数据后判断命令是否为。exit命令,不是退出命令则打印不识别命令,是退出命令则退出,非常简单:typedefstruct{charbuffer;sizetbufferlength;ssizetinputlength;}InputBuffer;intgetlinemy(charbuffer,intlength,FILEfd){inti0;charch;charbuf〔MAXLEN〕{0};while((chfgetc(fd))!EOFch!){if(MAXLEN1i){break;}buf〔i〕ch;}lengthi;buf〔i〕;buffer(char)malloc(sizeof(char)(i1));assert(buffer!NULL);strncpy(buffer,buf,i1);returni;}voidreadinput(InputBufferinputbuffer){ssizetbytesreadgetlinemy((inputbufferbuffer),(inputbufferbufferlength),stdin);if(bytesread0){printf(Errorreadinginput);exit(EXITFAILURE);}inputbufferinputlengthbytesread;inputbufferbuffer〔bytesread〕0;}
  运行提示行:F:ccodemicrodbmybuildwindowsdebugmicrodbmy。exemicrodb。testunkowncommand:。test。microdb。helpunkowncommand:。help。microdb。exit三小小的改进版本3。1支持语句识别
  上个版本的功能除了识别。exit退出外,没啥其他有用的功能,这个版本先添加对语句的支持,另外我们区分下点号开头的表示是元命令,否则就是正常的sql语句,需要我们解析执行的先不管怎么解析和执行。第2版的main函数,内容如下:intmain(intargc,charargv){InputBufferinputbuffernewinputbuffer();while(true){printprompt();readinput(inputbuffer);if(inputbufferbuffer〔0〕。){switch(dometacommand(inputbuffer)){case(METACOMMANDSUCCESS):continue;case(METACOMMANDUNRECOGNIZEDCOMMAND):printf(Unrecognizedcommands,inputbufferbuffer);continue;}}else{Statementstatement;switch(preparestatement(inputbuffer,statement)){case(PREPARESUCCESS):break;case(PREPAREUNRECOGNIZEDSTATEMENT):printf(Unrecognizedkeywordatstartofs。,inputbufferbuffer);continue;}executestatement(statement);printf(Executed。);}}return0;}
  将整个输入分成两个部分,以点号开头的表示元命令,不然就当成sql语句进行处理,解析好语句后,就执行,执行只是打印个输出内容:F:ccodemicrodbmybuildwindowsdebugmicrodbmy。exemicrodb。testUnrecognizedcommand。testmicrodb。helpUnrecognizedcommand。helpmicrodbinsertaThisiswherewewoulddoaninsert。Executed。microdbselectaThisiswherewewoulddoaselect。Executed。microdb。exit
  关于语句和解析语句的定义如下:typedefenum{METACOMMANDSUCCESS,METACOMMANDUNRECOGNIZEDCOMMAND}MetaCommandResult;typedefenum{STATEMENTINSERT,STATEMENTSELECT}StatementType;typedefenum{PREPARESUCCESS,PREPAREUNRECOGNIZEDSTATEMENT}PrepareResult;typedefstruct{StatementTypetype;}Statement;MetaCommandResultdometacommand(InputBufferinputbuffer){if(strcmp(inputbufferbuffer,。exit)0){closeinputbuffer(inputbuffer);exit(EXITSUCCESS);}else{returnMETACOMMANDUNRECOGNIZEDCOMMAND;}}PrepareResultpreparestatement(InputBufferinputbuffer,Statementstatement){if(strncmp(inputbufferbuffer,insert,6)0){statementtypeSTATEMENTINSERT;returnPREPARESUCCESS;}elseif(strncmp(inputbufferbuffer,select,6)0){statementtypeSTATEMENTSELECT;returnPREPARESUCCESS;}returnPREPAREUNRECOGNIZEDSTATEMENT;}voidexecutestatement(Statementstatement){switch(statementtype){case(STATEMENTINSERT):printf(Thisiswherewewoulddoaninsert。);break;case(STATEMENTSELECT):printf(Thisiswherewewoulddoaselect。);break;}}3。2支持在内存中插入和查询语句
  首先数据保存在内存中,采用硬编码的方式来保存用户信息,用户信息如下表:
  插入语句和查询语句的语法也进行了简化,插入语句如下:insert1cstackfoobar。comselect
  整体实现思路如下:扩展解析功能,将插入的数据以行为基本单元保存在称为页的一块内存中。每个页面有很多行组成,行紧凑排列。整个系统会根据需要申请多个页面,所有的页面都保存在一个大数组里面。
  对row的定义如下:typedefstruct{uint32tid;charusername〔COLUMNUSERNAMESIZE〕;charemail〔COLUMNEMAILSIZE〕;}Row;判断数据类型的长度,(Struct)0是把null强转为Struct指针,后获取属性,这里面只获取类型所以不会报错definesizeofattribute(Struct,Attribute)sizeof(((Struct)0)Attribute)获取ID的属性的长度constuint32tIDSIZEsizeofattribute(Row,id);获取用户名的长度constuint32tUSERNAMESIZEsizeofattribute(Row,username);获取邮箱的长度constuint32tEMAILSIZEsizeofattribute(Row,email);ID偏移量constuint32tIDOFFSET0;用户名偏移量constuint32tUSERNAMEOFFSETIDOFFSETIDSIZE;邮箱的偏移量constuint32tEMAILOFFSETUSERNAMEOFFSETUSERNAMESIZE;整行长度constuint32tROWSIZEIDSIZEUSERNAMESIZEEMAILSIZE;
  列的长度和偏移量
  下面两个函数比较关键,即序列化列和反序列化列,这里面序列化并没有把数据写入到磁盘,只是放入一块连续的平铺的内存中而已,不过这个却是可以方便持久化到磁盘上。序列化行,将row信息放入连续内存行中voidserializerow(Rowsource,voiddestination){memcpy(destinationIDOFFSET,(sourceid),IDSIZE);memcpy(destinationUSERNAMEOFFSET,(sourceusername),USERNAMESIZE);memcpy(destinationEMAILOFFSET,(sourceemail),EMAILSIZE);}反序列化,将内存块行转成rowvoiddeserializerow(voidsource,Rowdestination){memcpy((destinationid),sourceIDOFFSET,IDSIZE);memcpy((destinationusername),sourceUSERNAMEOFFSET,USERNAMESIZE);memcpy((destinationemail),sourceEMAILOFFSET,EMAILSIZE);}
  定义一个页面为4KB大小,然后定义最大只能保存100个页面,每个页面再按照每个用户总的字节数大小进行划分,具体如下:总页数defineTABLEMAXPAGES100每页的大小constuint32tPAGESIZE4096;每页可以保存row的总量constuint32tROWSPERPAGEPAGESIZEROWSIZE;整个表可以保存的总的数据量constuint32tTABLEMAXROWSROWSPERPAGETABLEMAXPAGES;typedefstruct{uint32tnumrows;voidpages〔TABLEMAXPAGES〕;}Table;
  行不跨页,每页会有些浪费空间。定位行的算法也是常用在布隆过滤器的算法,如下:voidrowslot(Tabletable,uint32trownum){uint32tpagenumrownumROWSPERPAGE;voidpagetablepages〔pagenum〕;if(pageNULL){pagetablepages〔pagenum〕malloc(PAGESIZE);}uint32trowoffsetrownumROWSPERPAGE;uint32tbyteoffsetrowoffsetROWSIZE;return(char)pagebyteoffset;}
  通过:uint32tpagenumrownumROWSPERPAGE;来定位具体的页,通过uint32trowoffsetrownumROWSPERPAGE;来定位具体的row,然后通过offset来定义在页面的具体偏移量。uint32tbyteoffsetrowoffsetROWSIZE;return(char)pagebyteoffset;
  插入和查询函数定义如下:ExecuteResultexecuteinsert(Statementstatement,Tabletable){if(tablenumrowsTABLEMAXROWS){returnEXECUTETABLEFULL;}Rowrowtoinsert(statementrowtoinsert);serializerow(rowtoinsert,rowslot(table,tablenumrows));tablenumrows1;returnEXECUTESUCCESS;}ExecuteResultexecuteselect(Statementstatement,Tabletable){Rowrow;for(uint32ti0;itablenumrows;i){deserializerow(rowslot(table,i),row);printrow(row);}returnEXECUTESUCCESS;}
  插入比较简单,关键是定位到具体的页面的偏移量,将row的内容持久化到内存块中。查询是每次都是全部查询没有查询全部的功能。四第一个正式版本
  代码一共320行,要定义为main。cpp,用c编译器编译才可以,虽然大部分是c的语法,但是定义const外部产量原因,c编译器无法通过。代码实现了内存一个固定表格的插入和查询功能,和一个交互界面,下一阶段是实现文件的持久化和B树的存储功能。includestdio。hifdefined(MSCVER)includeBaseTsd。htypedefSSIZETssizet;endifincludestdint。hincludestring。hincludemalloc。hincludecstdlibifndeftruedefinetrue1definefalse0endifdefineEXITSUCCESS0defineMAXLEN1024pragmawarning(disable:4819)defineCOLUMNUSERNAMESIZE32defineCOLUMNEMAILSIZE255defineTABLEMAXPAGES100元命令的解析结果typedefenum{METACOMMANDSUCCESS,METACOMMANDUNRECOGNIZEDCOMMAND}MetaCommandResult;插入和查询语句typedefenum{STATEMENTINSERT,STATEMENTSELECT}StatementType;解析命令结果typedefenum{PREPARESUCCESS,PREPAREUNRECOGNIZEDSTATEMENT,PREPARENEGATIVEID,PREPARESTRINGTOOLONG,PREPARENEGATIVEID,PREPARESYNTAXERROR}PrepareResult;执行结果typedefenum{EXECUTESUCCESS,EXECUTETABLEFULL}ExecuteResult;行定义typedefstruct{uint32tid;charusername〔COLUMNUSERNAMESIZE1〕;charemail〔COLUMNEMAILSIZE1〕;}Row;表的定义typedefstruct{uint32tnumrows;voidpages〔TABLEMAXPAGES〕;}Table;语句定义typedefstruct{StatementTypetype;Rowrowtoinsert;}Statement;缓冲区定义typedefstruct{charbuffer;sizetbufferlength;ssizetinputlength;}InputBuffer;definesizeofattribute(Struct,Attribute)sizeof(((Struct)0)Attribute)constuint32tIDSIZEsizeofattribute(Row,id);constuint32tUSERNAMESIZEsizeofattribute(Row,username);constuint32tEMAILSIZEsizeofattribute(Row,email);偏移量constuint32tIDOFFSET0;constuint32tUSERNAMEOFFSETIDOFFSETIDSIZE;constuint32tEMAILOFFSETUSERNAMEOFFSETUSERNAMESIZE;constuint32tROWSIZEIDSIZEUSERNAMESIZEEMAILSIZE;constuint32tPAGESIZE4096;constuint32tROWSPERPAGEPAGESIZEROWSIZE;constuint32tTABLEMAXROWSROWSPERPAGETABLEMAXPAGES;InputBuffernewinputbuffer(){InputBufferinputbuffer(InputBuffer)malloc(sizeof(InputBuffer));inputbufferbufferNULL;inputbufferbufferlength0;inputbufferinputlength0;returninputbuffer;}intgetlinemy(charbuffer,sizetlength,FILEfd){inti0;charch;charbuf〔MAXLEN〕{0};while((chfgetc(fd))!EOFch!){if(MAXLEN1i){break;}buf〔i〕ch;}lengthi;buf〔i〕;buffer(char)malloc(sizeof(char)(i1));strncpy(buffer,buf,i1);returni;}voidprintrow(Rowrow){printf((d,s,s),rowid,rowusername,rowemail);}从命令行读取一行命令voidreadinput(InputBufferinputbuffer){ssizetbytesreadgetlinemy((inputbufferbuffer),(inputbufferbufferlength),stdin);if(bytesread0){printf(Errorreadinginput);exit(EXITFAILURE);}inputbufferinputlengthbytesread;inputbufferbuffer〔bytesread〕0;}voidcloseinputbuffer(InputBufferinputbuffer){free(inputbufferbuffer);free(inputbuffer);inputbufferNULL;}解析元命令MetaCommandResultdometacommand(InputBufferinputbuffer){if(strcmp(inputbufferbuffer,。exit)0){closeinputbuffer(inputbuffer);exit(EXITSUCCESS);}else{returnMETACOMMANDUNRECOGNIZEDCOMMAND;}}解析插入语句PrepareResultprepareinsert(InputBufferinputbuffer,Statementstatement){statementtypeSTATEMENTINSERT;charkeywordstrtok(inputbufferbuffer,);charidstringstrtok(NULL,);charusernamestrtok(NULL,);charemailstrtok(NULL,);if(idstringNULLusernameNULLemailNULL){returnPREPARESYNTAXERROR;}intidatoi(idstring);if(id0){returnPREPARENEGATIVEID;}if(strlen(username)COLUMNUSERNAMESIZE){returnPREPARESTRINGTOOLONG;}if(strlen(email)COLUMNEMAILSIZE){returnPREPARESTRINGTOOLONG;}statementrowtoinsert。idid;strcpy(statementrowtoinsert。username,username);strcpy(statementrowtoinsert。email,email);returnPREPARESUCCESS;}语句的解析PrepareResultpreparestatement(InputBufferinputbuffer,Statementstatement){if(strncmp(inputbufferbuffer,insert,6)0){returnprepareinsert(inputbuffer,statement);}elseif(strncmp(inputbufferbuffer,select,6)0){statementtypeSTATEMENTSELECT;returnPREPARESUCCESS;}returnPREPAREUNRECOGNIZEDSTATEMENT;}持久化rowvoidserializerow(Rowsource,voiddestination){memcpy((char)destinationIDOFFSET,(sourceid),IDSIZE);memcpy((char)destinationUSERNAMEOFFSET,(sourceusername),USERNAMESIZE);memcpy((char)destinationEMAILOFFSET,(sourceemail),EMAILSIZE);}反持久化rowvoiddeserializerow(voidsource,Rowdestination){memcpy((destinationid),(char)sourceIDOFFSET,IDSIZE);memcpy((destinationusername),(char)sourceUSERNAMEOFFSET,USERNAMESIZE);memcpy((destinationemail),(char)sourceEMAILOFFSET,EMAILSIZE);}核心row的定位voidrowslot(Tabletable,uint32trownum){uint32tpagenumrownumROWSPERPAGE;voidpagetablepages〔pagenum〕;if(pageNULL){pagetablepages〔pagenum〕malloc(PAGESIZE);}uint32trowoffsetrownumROWSPERPAGE;uint32tbyteoffsetrowoffsetROWSIZE;return(char)pagebyteoffset;}核心定位row之后持久化ExecuteResultexecuteinsert(Statementstatement,Tabletable){if(tablenumrowsTABLEMAXROWS){returnEXECUTETABLEFULL;}Rowrowtoinsert(statementrowtoinsert);serializerow(rowtoinsert,rowslot(table,tablenumrows));tablenumrows1;returnEXECUTESUCCESS;}查询语句执行ExecuteResultexecuteselect(Statementstatement,Tabletable){Rowrow;for(uint32ti0;itablenumrows;i){deserializerow(rowslot(table,i),row);printrow(row);}returnEXECUTESUCCESS;}ExecuteResultexecutestatement(Statementstatement,Tabletable){switch(statementtype){case(STATEMENTINSERT):returnexecuteinsert(statement,table);case(STATEMENTSELECT):returnexecuteselect(statement,table);}}Tablenewtable(){Tabletable(Table)malloc(sizeof(Table));tablenumrows0;for(uint32ti0;iTABLEMAXPAGES;i){tablepages〔i〕NULL;}returntable;}voidfreetable(Tabletable){for(inti0;tablepages〔i〕;i){free(tablepages〔i〕);}free(table);}voidprintprompt(){printf(microdb);}intmain(intargc,charargv){InputBufferinputbuffernewinputbuffer();Tabletablenewtable();while(true){printprompt();readinput(inputbuffer);if(inputbufferbuffer〔0〕。){switch(dometacommand(inputbuffer)){case(METACOMMANDSUCCESS):continue;case(METACOMMANDUNRECOGNIZEDCOMMAND):printf(Unrecognizedcommands,inputbufferbuffer);continue;}}else{Statementstatement;switch(preparestatement(inputbuffer,statement)){case(PREPARESUCCESS):break;case(PREPARESTRINGTOOLONG):printf(Stringistoolong。);continue;case(PREPARENEGATIVEID):printf(IDmustbepositive。);continue;case(PREPARESYNTAXERROR):printf(Syntaxerror。Couldnotparsestatement。);continue;case(PREPAREUNRECOGNIZEDSTATEMENT):printf(Unrecognizedkeywordatstartofs。,inputbufferbuffer);continue;}switch(executestatement(statement,table)){caseEXECUTESUCCESS:printf(Executed。);break;caseEXECUTETABLEFULL:printf(Error:Tablefull。);break;}}}return0;}

2D动作roguelite游戏DeathorTreat5月6发行商PerpGames和开发商SaonaStudios宣布,2D动作roguelite砍杀游戏《DeathorTreat》将于5月6日登陆Steam、PlayStation5……张常宁的粉丝备受打击,吴冠希亲承小宝宝正在安排中虽然已经远离中国女排将近两年时间了,但张常宁的热度却丝毫不减,最近一段时间,有关她将回归中国女排的报道屡见不鲜,甚至有一位宝粉,曾在两个月之前就直接进行了这样预言:朱婷5……20230324最新iPhone14Mac壁纸,适配iOS1头条创作挑战赛iOS16。4RC版正式推出更新了,(内部版本号:20E246)。这是正式版发布之前的最后一个内部测试版本。有兴趣的小伙伴可以更新体验了。这个版本主要……短道速滑冠军赛孙龙力压刘氏兄弟斩获双冠4月1日,20222023赛季全国短道速滑冠军赛在首都体育馆展开第二比赛日的争夺。男子1500米A组决赛中,长春队的孙龙以2分23秒488率先冲线夺冠,吉林队选手任浩博位列第二……卡普空官宣街霸新真人影视剧将与传奇影业合作前几天流传传奇影业获得了《街霸》的影视改编权,日前卡普空正式官宣《街霸》新真人影视剧计划,将与传奇影业合作拍摄《街霸》全新真人电影与电视剧,敬请期待后续消息。作为目前世界……跨境周报丨瑞幸新加坡2家门店开始营业泡泡玛特2022年海外业21世纪经济报道记者董静怡上海报道过去一周(3月27日至4月2日),跨境电商行业发生哪些动态?行业一览《2022年度中国跨境电商市场数据报告》发布3月3……哈尼的万种风情云南元阳哈尼梯田,以其历史悠久、规模宏大、景色艳美、仪态万千闻名遐迩,是世界文化遗产。我心向往之,初春三月,我们踏上了云南之旅。我们从云南罗平出发,经过5个多小时的车程,……大事不妙!杜兰特伤情出炉!勇士也传来坏消息(点击阅读可看更多857体育赛事资讯)官宣缺席3周时间,杜兰特恐常规赛报销没想到意外来得这么快,昨儿个杜兰特在赛前扭伤脚踝,而后并没有第一时间下场,依旧在场上进行简……30余年来首次美国去年黑胶唱片销量超过CD新华社北京3月10日电美国唱片业协会9日发布报告说,美国去年黑胶唱片销量自1987年以来首次超过CD。报告数据显示,美国去年实物唱片销售收入同比增长4至17亿美元,其中黑……伊藤美诚被钱天一拿下,含泪止步16强,大满贯冠军梦再次破灭今天的世乒联大满贯赛(新加坡站)继续比赛,各个赛场可谓波澜起伏,一些不太出名的运动员发挥出色,把一些非常出色的运动员拉下了马。先是世界排名第四的我国运动员王艺迪23输给了……Oracle安装教程将2个压缩包解压到一个目录下,运行setup。exe2)出现下面提示,跳过3)email和更新都不用填4)选择创建和配置数据库(这样数据库安装完成后,会默认创……知名女演员!胜诉!致歉声明浙江绍兴籍知名演员江一燕名誉维权案件近日公布,经法院审理判决,原告江一燕女士一审胜诉。甘肃西行影视有限责任公司于2019年6月至9月期间,通过其实际运营的新……
一生必去的地方,香格里拉,最值得去的10个景点如果说人有一生必去的地方,那一定是香格里拉。《消失的地平线》里描绘的香格里拉,是离天堂最近的地方,是被奉为人间净土的乌托邦。神圣的雪,成群的牛羊,明镜般的天空,辉煌的庙宇,组成……跨国甚至跨洲举行!那些奇妙的马拉松赛事们绝大多数马拉松都在一个城市里举行,也有少数从A城跑到B城(比如从郑州跑到开封的郑开马拉松)。不过世界上还存在更大噱头的,从A国跑到B国的跨国、甚至是跨越两个大洲的马拉松。……谁说开放世界重探索轻战斗?幻塔带来炫酷连招体验提到开放世界类型的游戏,相信很多玩家首先想到的就是可以自由探索的巨大地图,很少有人能够将其与动作战斗联想到一起。事实上也是如此,一些开放世界游戏过于注重探索,从而弱化了动作战斗……8512G恢复现货,6。92英寸大屏55W快充,3498入手中兴这个手机品牌的历史很久了,从源头上来看,中兴做手机的历史比华为小米还要久,但发展到现在,中兴的状态确实不怎么好,甚至可以说,在目前的国产手机市场上,中兴是被边缘化了。……哈登真的会离开篮网吗?短期内不可能的事似乎有了悬念詹姆斯哈登真的会离开篮网吗?这个原本短期内似乎不太可能发生的事情似乎随着人们的讨论越来越有悬念了。NBA专家马克斯通报道提到,对于费城76人要注意的是哈登离队的可能性不能……这6个省的名字在中国地图上消失了,留下来的风景却美上了天每个城市都在翻新进步,为了好管理,曾经也有一些省份被重新安置。这6个省的名字虽然在中国地图上消失了,但风景到今天依然是美上了天!NO。1西康省跑马溜溜的山上几乎每一……中国科大又双叒进十大科技进展!2021年度中国十大科技进展新闻2022年1月18日,由中国科学院、中国工程院主办,中国科学院院士和中国工程院院士投票评选的2021年中国、世界十大科技进展新闻在京……LOL德杯RNG止步八强,新阵容遭TES零封!TES你比主播在2022年LPL春季赛开打之前,如今电竞圈观众的目光大多聚焦在进行到白热化阶段的德玛西亚杯这项赛事。虽然过去德杯并不受重视,但是由于今年转会期大洗牌,多支队伍重建阵容,因此很……Python程序设计Python语法基础2。1数据类型和其他编程语言相比,Python语言中的变量不需要显示数据类型的声明,Python的变量类型是根据变量的赋值内容而自动生成的。Python六大数据类型……2023如何挑选一款适合的HDMI线?版本场景2大方面解析选一条HDMI线有多难?或许很多人从来没去考虑过这个问题,HDMI线对于大多数人都是一件比较冷门小众的产品,但事实上不论是显示屏还是电视都有它出现的身影。简单来说,选对一……梅峰寺梅花踏春而来枝头暗香浮动眼下正是梅花盛开的季节,梅峰寺近百株梅花悄然绽放,枝头暗香浮动,吸引不少市民和摄影爱好者前来,领略诗情画意,共度梅好时光。走进古刹,远远望去,一树树梅花,繁花似锦,灿若云……骨头够硬才长寿!3个靠谱补钙妙招学起来,别只知吃钙片这几年,网上流传着这么一句话:人这一生,至少要有一次说走就走的旅行。可说起来容易做起来难啊!先不说有没有足够的时间和资金,强健的身体和坚硬的骨骼也是最基本的吧!但有些人可能摔一……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网