• [问题求助] 数据库的集群架构是哪些
    文档数据库的集群架构是深恶
  • [用户实践] 利用OpenGauss设计和实现成绩管理系统
    一、需求分析先介绍系统开发意义、可行性和应用环境等。某校管理学生成绩的工作人员,根据实际工作需要,提出了以下基本数据和业务处理需求:(1)学校设置了各专业,在专业下开设班级,每个班级包含若干学生,学生信息至少需要包含学号、姓名、性别、年龄、生源所在地、已修学分总数等数据项;另外,需要有地区信息,用于统计某一地区的学生数;(2)课程信息表至少需包含课程编号、课程名称、任课教师、开课学期、学时、考试或考查、学分等数据项,课程根据班级开设。(3)教师信息至少需要包含教师编号、姓名、性别、年龄、职称、联系电话等数据项;(4)学生成绩至少需要学号,学期,课程名称,成绩,任课老师等数据项;(5)需要实现业务处理和查询功能:a) 学生成绩按每学年成绩统计;学生成绩b) 名次排定;c) 每门课程平均成绩统计;d) 学生所学课程及学分统计;e) 对每个学生输入成绩的时候,自动生成学生的已修学分总数;f) 学生成绩查询;g) 教师任课查询;h) 班级课程开设查询。(6) 补充说明a) 假设学籍管理制度是采用学年制,且暂不考虑选修课情况。b) 同一专业可能有多个班,所以同一门课可能需要多个教师上课,一个教师在一学期可能上多门课程。1.1 数据需求描述分析系统的数据需求,用数据字典和数据流图描述系统的数据需求,一般要求有2级(初级和详细级)数据流图,并确定系统开发边界。1.1.1数据字典➢ 数据项名 数据项名:学号含义说明:唯一标识每个学生数据类型:CHAR长度:12数据项名:学生姓名含义说明:标识每个学生的姓名数据类型:CHAR长度:20数据项名:学生性别含义说明:学生性别数据类型:CHAR长度:3取值范围:“男”或“女”取值含义:学生分为男生和女生数据项名:学生出生日期含义说明:表明每个学生的出生日期数据类型:DATESTAMP数据项名:籍贯(省)含义说明:表明每个学生入学前的来源地数据类型:CHAR长度:20数据项名:籍贯(市)含义说明:表明每个学生入学前的来源地数据类型:CHAR长度:20数据项名:已修学分总数含义说明:学生已修课程的学分总数数据类型:FLOAT长度:4取值范围:>=0取值含义:已修学分不可能小于0与其他数据项的逻辑关系: 等于该学生所有课程的学分数据项之和数据项名:学生登录密码含义说明:学生账户用于登录系统时的密码数据类型:CHAR长度:20数据项名:成绩含义说明:学生某一课程的成绩数据类型:INT长度:3取值范围:0 ~ 100取值含义:课程成绩60以上为合格,100为满分,不可能低于0分数据项名:上课时间含义说明: 表示教师授课和学生上课的时间数据类型: CHAR长 度: 20数据项名:上课地点含义说明: 表示教师授课和学生上课的地点数据类型: CHAR长 度: 20数据项名:专业编号含义说明: 唯一标识每一个专业数据类型: CHAR长 度: 20数据项名:专业名称含义说明:表示咋混也的名称数据类型: CHAR长 度: 20数据项名:班级编号含义说明: 唯一标识每一个班级数据类型: CHAR长 度: 20数据项名:班级名称含义说明:表示班级的名称数据类型: CHAR长 度: 20数据项名:课程编号含义说明:唯一标识每一门课程数据类型: CHAR长 度: 20数据项名:课程名称含义说明:表示课程的名称数据类型: CHAR长 度: 20数据项名:开课学期含义说明:某课程开设的时间节点数据类型: CHAR长 度: 20取值范围:类似于2021-2022(1),2022-2023(2)这样代表某一学年第几学期数据项名:学时含义说明:某课程总共需要的授课时间数据类型: INT长 度: 3取值范围:(0,999)取值含义:课程授课时间不小于0小时,不大于999小时数据项名:学分含义说明:某课程的学分数据类型:FLOAT数据项名:考核方式含义说明:某课程的评价方式数据类型: CHAR长 度: 2取值范围:考查/考试取值含义:课程只有考查和考试两种考核方式数据项名:教师编号含义说明:唯一标识每一位教师数据类型:CHAR长度: 20数据项名:教师姓名含义说明:表示每一位教师的姓名数据类型:CHAR长度: 20数据项名:教师性别含义说明:表示每一位教师的姓名数据类型:CHAR长度: 2取值范围:男/女取值含义:教师性别只有男和女数据项名:教师出生日期含义说明:表示每一位教师的出生日期,可计算教师的年龄数据类型:DATE数据项名:职称含义说明:表示每一位教师的职称数据类型:CHAR长度:20数据项名:联系电话含义说明:教师的联系电话数据类型:CHAR长度:20数据项名:教师登录密码含义说明:教师账户用于登录系统时的密码数据类型:CHAR长度:20数据项名:管理员登录账号含义说明:管理员账户用于登录系统时的密码数据类型:CHAR长度:20数据项名:管理员登录密码含义说明:管理员账户用于登录系统时的密码数据类型:CHAR长度:20➢ 数据结构 数据结构名:学生别名:学生基本信息含义说明:定义了每一个学生的基本信息组成:学号、学生姓名、学生性别、学生出生日期、籍贯(省)、籍贯(市)数据结构名:班级别名:学生基本信息含义说明:定义了每一个班级的基本信息组成:班级编号、班级名称数据结构名:专业别名:专业基本信息含义说明:定义了每一个专业的基本信息组成:专业编号、专业名称数据结构名:课程别名:课程基本信息含义说明:定义了每一个学生的基本信息组成:学号、学生姓名、学生性别、学生出生日期、籍贯(省)、籍贯(市)数据结构名:教师别名:教师基本信息含义说明:定义了每个教师的基本信息组 成: 教师编号、教师姓名、教师性别、教师出生日期、职称、联系电话、教师登录密码数据结构名:管理员别名:管理员基本信息含义说明:定义了每个管理员的基本信息组 成: 管理员账号、管理员登录密码➢ 数据流数据流名:输入登录信息含义说明: 登录系统对用户身份进行验证数据来源: 用户数据去向: 身份信息表组 成: 用户账户、用户密码、用户身份数据流名:查询、添加、修改、删除学生本人基本信息、成绩、选课含义说明: 学生进行个人信息的查询,教师、管理员对学生信息进行管理数据来源: 用户数据去向: 学生信息表组 成: 学号、学生姓名、学生性别、出生日期、生源所在省市、班级、专业、选课、课程成绩、已修总学分数据流名:查询、添加、修改、删除教师基本信息、任教情况、任教班级成绩含义说明: 教师对学生及个人信息进行管理、管理员对教师信息进行管理数据来源: 用户数据去向: 教师信息表、学生信息表、选课信息表组 成: 教师编号、教师姓名、性别、出生日期、职称、联系电话、任教信息、任教班级成绩信息➢ 数据存储 数据存储名: 用户账号信息说明: 记录用户账号信息输入数据流: 管理员输入、修改、删除账号信息操作输出数据流: 用户账号表组 成: 用户账号,用户密码,用户身份存取方式: 随机存取数据存储名:学生信息说明: 记录学生基本信息输入数据流: 管理员输入、更新、删除学生信息操作输出数据流: 学生信息表组 成: 学号、学生姓名、学生性别、出生日期、生源所在省市、班级、专业、已修总学分存取方式: 随机存取数据存储名: 教师信息说明: 记录教师基本信息输入数据流: 管理员输入、更新、删除教师信息操作输出数据流: 教师信息表组 成: 教师编号、教师姓名、性别、出生日期、职称、联系电话存取方式: 随机存取数据存储名: 课程信息说明: 记录课程基本信息输入数据流: 管理员输入、更新、删除课程信息操作输出数据流: 课程信息表组 成: 课程编号、课程名称、课程学时、课程学分、开设学期、考核方式存取方式: 随机存取数据存储名: 教师信息说明: 记录专业班级基本信息输入数据流: 管理员输入、更新、删除专业班级信息操作输出数据流: 专业班级信息表组 成: 班级编号、专业名称、专业编号存取方式: 随机存取数据存储名: 教师信息说明: 记录教师任教信息及成绩信息输入数据流: 管理员输入、更新、删除任教信息操作,教师录入成绩操作输出数据流: 任教信息表组 成: 课程编号、教师编号、学生学号、课程成绩存取方式: 随机存取1.1.2 数据流图数据流图主要分为两级,分别为顶级数据流图和次级数据流图。其中顶级数据流图包括学生模块、教师模块、管理员模块,描述了这三个模块与高校管理系统间的数据传递关系。具体如下图1-1所示。图1-1 顶级数据流图次级数据流图是对顶级数据流图局部模块的细化描述与拓展描述,这里根据三个模块将顶级数据流图细化出了三个次级数据流图,分别为学生模块次级数据流图、教师模块次级数据流图、管理员模块次级数据流图。学生模块的主要功能为课程信息、个人信息和成绩、已修总学分的查询,具体的数据流图如下图 1-2 所示:图 1-2 学生模块次级数据流图教师模块的主要功能为授课信息、教师个人信息、授课班级成绩信息的查询以及学生课程成绩的录入,具体的数据流图如下图 1-3 所示:图 1-3 教师模块次级数据流图管理模块的主要功能为学生成绩的查询以及对于学生、课程、教师信息的管理,具体的数据流图如下图 1-4 所示图 1-4 管理模块次级数据流图1.2 系统功能需求本高校成绩管理数据库系统分为4个界面,分别为登录界面、学生界面、教师界面和管理员界面,并由登录界面中的身份选择来决定界面的选择。学生界面有3个功能,分别为学生个人信息查询、个人成绩查询和班级课程查询功能;教师界面有5个功能,分别为教师个人信息查询、授课信息查询、学生成绩统计、学生成绩查询和学生成绩录入功能;管理员界面有5个功能,分别为对学生信息、成绩信息、教师信息、课程信息的管理(查询、添加、删除、更新)以及地区人数的统计功能。其中,由于管理员不具备更改成绩的权限,因此管理员界面不设置对成绩信息的更新功能。除此之外,本系统对所有的信息都具有模糊查询和按各数据项排序的功能, 而且实现了灵活排版调整内容的自适应界面。本系统的功能结构如下图 1-5 所示图 1-5 高校成绩管理数据库系统功能结构1.3 其他性能需求并发用户数:考虑到实际应用,数据库系统应面向在校学生和教师以及教务 处管理员使用,理论上并发用户数应到达上千数量级;响应时间:考虑到实际应用,数据库系统应响应时间足够短,理论上不大于 3s;存储需求:考虑到实际应该,数据库系统应在满足查询效率的前提下,尽量 精简存储空间,减少数据冗余。1.4 其他需求1. 保证用户数据操作的简单便捷;2. 用户更新信息时数据库应实时更新;3. 保证系统良好的交互性、功能性、稳定性;4. 建立存储过程,集成操作功能,增加数据更新的便利性;5. 建立触发器,保证数据项格式的规范化以及数据的一致性;6. 保证前端界面的简洁美观、人性化,能对错误信息进行提示;7. 满足高并发下的大数据量交互需求,满足数据备份的数据量迁移;8. 用户分身份以账号密码登录数据库应用系统,涉及不同的界面和功能。二、概念结构设计2.1 局部E-R图局部 E-R 模式的设计一般可分解为以下三步骤:1. 确定局部 E-R 模式的范围设计局部 E-R 模式的第一步就是确定局部结构的范围,即将用户需求划分 成若干个部分,其划分方式一般有以下两种:(1) 根据企业的组织机构对其进行自然划分;(2) 根据数据库提供的服务种类进行划分,使得每一种服务所使用的数据明显地不同于其他种类,这样就可为每一类服务设计一个局部 E-R 模式。本系统采用第二种划分方式,根据数据库提供的服务种类进行划分。高校成 绩管理数据库可以按提供的服务分为以下三种类型:(1) 学生专业班级信息查询; (2) 学生成绩查询; (3) 教师任教班级课程查询。2. 查询定义实体型每一个局部 E-R 模式都包括一些实体型,定义实体型就是从选定的局部范 围中的用户需求出发,确定每一个实体型的属性和主键。 实体型的设计可参考以下三个原则:(1) 信息描述原则;(2) 依赖性原则;(3) 一致性原则。在确定了实体型和属性后,需对下述几个方面作详细描述:(1) 给实体集与属性命名;(2) 确定实体标识;(3) 非空值原则。3. 定义联系在 E-R 模型中,“联系”用于刻画实体集之间的关联。在定义了实体型和 属性并进行描述后,还要确定实体集之间的联系及其属性。实体集之间的联系非 常广泛,大致可分为以下三种:a) 存在性联系b) 功能性联系c) 事件联系由此,可最终根据学生专业班级信息查询、学生成绩查询、教师任教班级课 程查询三种服务确定三个局部 E-R 模式如下:(1) 学生专业班级信息查询的局部 E-R 图根据高校成绩管理的实际需求,专业信息包括专业编号、专业名称等数据项;班级信息包括班级编号、班级名称等数据项。但为了查询方便,将二者置于一张表中,并且设置班级编号为主键。学生个人信息包含学号、姓名、性别、出生日期、生源所在省、生源所在市、已修学分总数,学生登录密码等数据项,学号为主键。在设计学生实体型的属性时,为满足对学生的年龄信息查询需求,同时又省 去随时间定期更新数据库带来的维护成本,将年龄替换为出生日期。即年龄可由 当前年份减去出生年份获得,使得数据库中信息变得准确又易维护。同时,考虑到之后以学生身份登录系统的需求,将学生登录密码也作为学生 实体型的属性,以方便数据存储和查询。由此定义实体型(下划线处为主键)如下:a) 专业班级(班级编号,班级名称,专业编号,专业名称);b) 学生(学号、学生姓名、学生性别、教师出生日期、生源所在省、生源 所在市、已修学分总数、学生登录密码)。学校设置了各专业,在专业下开设班级,每个班级包含若干学生,并且考虑 到课程根据班级开设,因此将专业班级表也纳入学生专业班级信息查询的局部 E-R 图中。由此定义联系如下:c) 包含:班级和学生之间具有一对多联系,“包含”为联系名。由此可得学生专业班级信息查询的局部 E-R 图如图 2-1 所示:图 2-1 学生专业班级信息查询的局部 E-R 图(2) 学生成绩查询的局部 E-R 图根据高校成绩管理的实际需求,课程信息包括课程编号、课程名称、开课学期、学时、学分、考核方式等数据项,课程编号为主键。由此定义实体型(下划 线处为主键)如下:a) 课程(课程编号、课程名称、开课学期、学时、学分、考核方式);学生选修课程,一名学生可选择多门课程,一门课程可由多名学生选修,每 名学生的每门课程都有一个成绩。由此定义联系如下:b) 选课:学生和课程之间具有多对多联系,“选课”为联系名,包含成绩 属性;由此可得学生成绩查询的局部 E-R 图如图 2-2 所示:图 2-2 学生成绩查询的局部 E-R 图(3) 教师任教班级课程查询的局部 E-R 图在定义实体集之间的联系时,要尽量消去冗余的联系,以免将这些问题留给全局E-R模式的集成阶段,从而造成困难和麻烦。一个班级开设的一门课程只有一个教师,一个教师在一个班可能教授多门课程,一个教师可能教授多个班级同一门课程,因此教师与班级、课程之间的联系是一对多的,可将三者联系设定为1:m:n的联系。图 2-3 教师任教班级课程查询的局部 E-R 图然而,考虑到实际情况中可能存在三者不一定同时知道的情况,即可能仅知道教师所教授的班级,但不知道教师所教授的课程。因此,仍然采取建立三张关系表说明三者之间的两两关系。根据高校成绩管理的实际需求,教师信息包括教师编号、教师姓名、教师性别、教师出生日期、职称、联系电话,教师登录密码等数据项。教师实体型中教 师出生日期和教师登录密码属性的设计思路同学生实体型,此处不再赘述。由此 定义实体型(下划线处为主键)如下:a) 教师(教师编号、教师姓名、教师性别、教师出生日期、职称、联系电 话、教师登录密码)。 一名教师可教授多门课程,一门课程可以有多名教师;一个班级可以开设多 门课程,一门课程可以为多个班级设置;一名教师可以给多个班级授课,一个班 级可以有多名教师任课。由此定义实体型如下:a) 教授:教师和课程之间具有多对多联系,“教授”为联系名;b) 设置:班级和课程之间具有多对多联系,“设置”为联系名;c) 任课:教师和班级之间具有多对多联系,“任课”为联系名。由此可得教师任教班级课程查询的局部 E-R 图如图 2-4 所示:图2-4 教师任教班级课程查询的局部 E-R 图2.2 全局E-R图当各个局部 E-R 模式设计完成后,就需要对它们进行合并,将其集成为一 个全局的 E-R 模式,即数据库的全局概念结构。全局 E-R 模式的集成过程,一般可以分成两步进行:1. 确定公共实体型公共实体型是多个局部 E-R 模式综合集成的基础,因此必须首先确定各局 部 E-R 模式之间的公共实体型。在这一步中,一般仅根据实体型名称和主键来 认定公共实体型,即把同名实体型作为一个候选的公共实体型,把具有相同主键 的实体型作为另一个候选的公共实体型。本系统公共实体型为:(1) 专业班级(班级编号,班级名称,专业编号,专业名称)(2) 课程(课程编号、课程名称、开课学期、学时、学分、考核方式)(3) 学生(学号、学生姓名、学生性别、学生出生日期、生源所在省、生源 所在市、已修学分总数,学生登录密码)(4) 教师(教师编号、教师姓名、教师性别、教师出生日期、职称、联系电 话,教师登录密码)2. 合并局部 E-R 模式局部 E-R 模式的合并顺序有时会影响处理效率和结果。一般都采用逐步合 并的方式,即首先将两个具有公共实体型的局部 E-R 模式进行合并,然后每次 将一个新的与前面已合并模式具有公共实体型的局部 E-R 模式合并起来,最后 再加入独立的局部 E-R 模式,这样即可终获得全局 E-R 模式。 合并局部 E-R 模式可能会产生的冲突通常可分为三种类型:(1) 属性冲突;(2) 命名冲突;(3) 结构冲突。消除冲突后,合并后的全局 E-R 模式如图 2-5 所示:图2-5 全局 E-R 图2.3 优化E-R图按照前面的方法将各个局部 E-R 模式合并后就得到一个初步的全局 E-R 模式,之所以这样称呼是因为其中可能存在冗余的数据和冗余的联系等。所谓冗余的数据是指可由基本数据导出的数据,冗余的联系是指可由其他联系导出的联系。冗余的数据和冗余的联系容易破坏数据库的完整性,给数据库维护带来困难,因此再得到初步的全局 E-R 模式后,还应当进一步检查 E-R 图中是否存在冗余,如果存在冗余则一般应设法将其消除。 一个好的全局 E-R 模式,不仅能全面、准确地反映用户需求,而且应该满足如下的一些条件:实体型的个数尽可能少;实体型所含属性个数尽可能少;实体型之间联系无冗余。下面从优化全局 E-R 模式时需要重点考虑的几个问题入手优化全局 E-R 图1. 实体型是否合并的问题初步全局 E-R 模式不存在冗余的实体型,且没有一对一联系,因此不需要 合并实体型。2. 冗余属性是否消除的问题初步全局 E-R 模式没有冗余属性,因此不需要消除冗余属性。3. 冗余联系是否消除的问题在设计局部 E-R 模式的定义联系时,考虑到已将教师与课程之间的“讲授” 联系、班级与课程之间的“设置”联系、教师与班级之间的“授课”联系转化成 教师、班级与课程之间的一对多三元联系,大大减小了存储空间,降低了维护代 价。三、逻辑结构设计3.1 关系模式设计将 E-R 图转换为关系模式,定义实体型、属性及其联系,关系模式定义如下,其中下划直线的属性为主键,下划波浪线的属性为外键。3.2 数据类型定义对关系模式中的属性定义类型、长度和约束如下表所示:表 3-1 学生信息基本表 linc_Students08字段名数据类型长度约束学生学号sja_Sno08NVARCHAR50主键学生姓名sja_Sname08NVARCHAR50-学生性别sja_Ssex08CHAR3男/女学生生日sja_Sbirthday08DATE--生源省sja_Sprovince08NVARCHAR50-生源市sja_Scity08NVARCHAR50-已修总学分sja_Countcredit08FLOAT--学生登录密码sja_Spassword08NVARCHAR50-班级编号sja_CLno08NVARCHAR50外键表 3-2 专业班级基本表 linc_CLasses08字段名数据类型长度约束班级编号sja_Clno08NVARCHAR50主键专业编号sja_Mno08NVARCHAR50-专业名称sja_Mname08NVARCHAR50-班级名称sja_CLnameNVARCHAR50-表 3-3 课程信息基本表 linc_Courses08字段名数据类型长度约束课程编号sja_Cno08NVARCHAR50主键课程名称sja_Cname08NVARCHAR50-开设学期sja_Cterm08NVARCHAR50-学时sja_Cclasshour08INT4-学分sja_Credit08FLOAT--考核方式sja_Cexamine08NVARCHAR50考察/考试表 3-4 教师信息基本表 linc_Teacher08字段名数据类型长度约束教师编号sja_Tno08NVARCHAR50主键教师名称sja_Tname08NVARCHAR50-教师性别sja_Tsex08CHAR3男/女教师生日sja_Tbirthday08DATE--职称sja_Ttitle08NVARCHAR50助教/讲师/副教授/教授联系电话sja_Tphone08NVARCHAR50-教师登录密码sja_Tpassword08NVARCHAR50考察/考试表 3-5 选课基本表 linc_ChooseClass08字段名数据类型长度约束学生学号sja_Sno08NVARCHAR50主键,外键课程编号sja_Cno08NVARCHAR50主键,外键成绩sja_Score08INT30~100表 3-6 上课基本表 linc_Teaching08字段名数据类型长度约束班级编号sja_CLno08NVARCHAR50主键,外键课程编号sja_Cno08NVARCHAR50主键,外键上课时间sja_Time08NVARCHAR50-上课地点sja_Place08NVARCHAR50-表 3-7 老师授课班级基本表 linc_Teaching08字段名数据类型长度约束班级编号sja_CLno08NVARCHAR50主键,外键教师编号sja_Tno08NVARCHAR50主键,外键表 3-8 老师授课课程基本表 linc_Teaching08字段名数据类型长度约束课程编号sja_Cno08NVARCHAR50主键,外键教师编号sja_Tno08NVARCHAR50主键,外键表 3-9 管理员基本表 linc_ctrlogin08字段名数据类型长度约束管理员登录用户名sja_Ctrno08NVARCHAR50主键,外键管理员登录密码sja_Ctrpassword08NVARCHAR50主键,外键3.3 关系模式的优化3.3.1 规范化处理考察关系模式的数据依赖集合,仅为函数依赖,上述关系模式都属于 BC 范式,无需规范化分解。3.3.2 模式的评价与修正1. 模式评价(1) 功能评价根据需求分析的结果,规范化后的关系模式可以支持用户的所有应用要求,且满足无损连接性。(2) 性能评价经过逻辑记录访问估算,观察到专业关系模式和班级关系模式经常需要执行连接操作,因此考虑在模式修正时合并两者。2. 模式修正为减少连接操作,提高查询效率,故将专业关系模式和班级关系模式合并。同时,考虑到专业名称和班级名称不会出现重名现象,故将班级编号作为主键,修改后得到专业班级关系模式如下:专业班级(班级编号,班级名称,专业编号,专业名称)在上述关系模式中班级编号决定专业编号,专业编号决定专业名称,班级编号决定专业名称,因此存在非主属性对主键的传递函数依赖,属于第二范式。但考虑到查询效率和专业名称使用的频率,不对该关系模式进行规范化。因此,关模式优化后最终的关系模式如下:四、物理结构设计4.1 聚簇设计聚簇是将有关的数据元组集中存放于一个物理块内或若干相邻物理块内或同一柱面内,以提高查询效率的数据存储结构。所谓聚簇设计,就是根据用户需求确定每个关系是否需要建立聚簇,如果需要,则应确定在该关系的哪些属性列上建立聚簇。当一个关系按照某些属性列建立聚簇后,关系中的元组都按照聚簇属性列的顺序存放在磁盘的一个物理块或若干相邻物理块内,因此对这些属性列的查询特别有效,它可以明显提高查询效率,但是对于非聚簇属性列的查询效果不佳。此外,数据库系统建立和维护聚簇的开销很大,每次修改聚簇属性列值或增加、删除元组都将导致关系中的元组移动其物理存储位置,并且重建该关系的聚簇。因此,只有在遇到以下一些特定情况时才考虑对一个关系建立聚簇:(1) 当对一个关系的某些属性列的访问时该关系的主要应用,而对其他属性的访问很少或是次要应用时,可考虑对该关系在这些属性列上建立聚簇;(2) 若一关系在某些属性列上的值重复率很高,则可考虑对该关系在这些组属性列上建立聚簇;(1) 若一关系一旦装入数据,某些属性列的值很少修改,也很少增加或删除元组,则可考虑对该关系在这些组属性列上建立聚簇。考虑到上述信息,对在每个关系模式的主键上建立聚簇。4.2 索引设计索引设计时数据库物理设计的基本问题,对关系选择有效的索引对提高数据 库访问效率有很大的帮助。索引也是按照关系的某些属性列建立的,它与聚簇的 不同之处在于,当索引属性列发生变化,或增加、删除元组时,只有索引发生变 化,而关系中原先元组的存放位置不受影响。此外,每个关系只能建立一个聚簇, 但却可以同时建立多个索引。 对于一个确定的关系,通常在下列情况下可以考虑建立索引:(1) 在主键属性列和外键属性列上通常都可分别建立索引,不仅有助于唯一 性检查和完整性检查,而且可以加快连接查询的速度;(2) 以查询为主的关系可建立尽可能多的索引;(3) 对等值连接,但满足条件的元组较少的查询可考虑建立索引;(4) 如果查询可以从索引直接得到结果而不必访问关系,则对此种查询可建 立索引。例如,为查询某个属性的 MIN, MAX, AVG, SUM, COUNT 等函数值,可在该属性列上建立索引。考虑到上述信息,对每个关系模式的外键都建立索引;同时,为满足最统计 同一地区学生数的需求,在学生关系模式的生源所在省和生源所在市的属性列上 分别建立索引;为满足经常查询的需求,在课程关系模式的开课学期和教师关系 模式的职称属性列上建立索引。4.3 分区设计数据库中的数据,包括关系、索引、聚簇、日志等,一般都存放在磁盘内, 由于数据量的增大,往往需要用到多个磁盘驱动器或磁盘阵列,这就产生了数据 在多个磁盘如何分配的问题,即磁盘分区设计问题。磁盘分区设计的本质市确定 数据库数据的存放位置,其目的市提高系统性能,是数据库物理设计的内容之一。 磁盘分区设计的一般原则是:(1) 减少访问冲突,提高 I/O 并行性。多个事务并发访问同一磁盘时,会产 生磁盘访问冲突而导致效率低下,如果事务访问数据能均匀分布于不同磁盘上, 则 I/O 可并发执行,从而提高数据库访问速度。(2) 分散热点数据,均衡 I/O 负担。在数据库中数据访问的频率是不均匀的, 那些经常被访问的数据称为热点数据,此类数据宜分散存放于不同的磁盘上,以 均衡各个磁盘的负荷,充分发挥多磁盘并行操作的优势。(3) 保证关键数据快速访问,缓解系统“瓶颈”。在数据库中有些数据如数 据字典等的访问频率很高,为保证对它的访问不直接影响整个系统的效率,可以 将其存放在某一固定磁盘上,以保证其快速访问。 由于高校成绩管理数据库系统的数据所需存储规模较小,因此不进行分区设 计。五、数据库实施创建数据库,如图 5-1 所示:图5-1 数据库建立5.1 基本表建立(1)教师表建立该表格包含两个列:`sja_Aacc08`和`sja_Apwd08`。`sjA_Aacc08`列是一个字符类型(CHAR),长度为12个字符,且不允许为空(NOT NULL)。它将用作表格的主键(PRIMARY KEY),这意味着每个记录在该列上必须具有唯一的值。`sja_Apwd08`列也是一个字符类型(CHAR),长度为10个字符。它使用了DEFAULT关键字,表示如果没有为该列提供值,则该列的默认值为NULL。因此,这个表格的主要目的是存储管理员账户的信息。每个管理员账户由一个唯一的12位字符编码(sja_Aacc08)和一个最长为10个字符的密码(sja_Apwd08)组成。图5-2 建立教师表(2) 专业表建立该表格包含两个列:`sja_Mno08`和`sja_Mname08`。`sja_Mno08`列是一个字符类型(CHAR),长度为10个字符,且不允许为空(NOT NULL)。它将用作表格的主键(PRIMARY KEY),这意味着每个记录在该列上必须具有唯一的值。`sja_Mname08`列也是一个字符类型(CHAR),长度为10个字符。它使用了DEFAULT关键字,表示如果没有为该列提供值,则该列的默认值为NULL。除了主键之外,还定义了一个约束(CONSTRAINT),名为`Major_Mno`。该约束确保`shengja_Majors08`表格中的`sja_Mno08`列的值是唯一的,即每个记录都必须具有不同的`sja_Mno08`值。这个表格的主要目的是存储专业(Major)的信息。每个专业由一个唯一的10位字符编码(sja_Mno08)和一个最长为10个字符的名称(sja_Mname08)组成。约束确保了每个专业编码的唯一性。图5-3 建立专业表(3) 班级表建立该表格包含三个列:`sja_Classno08`、`sja_Classname08`和`sja_Mno08`。`sja_Classno08`列是一个字符类型(CHAR),长度为10个字符,且不允许为空(NOT NULL)。它将用作表格的主键(PRIMARY KEY),这意味着每个记录在该列上必须具有唯一的值。`sja_Classname08`列也是一个字符类型(CHAR),长度为10个字符。它使用了DEFAULT关键字,表示如果没有为该列提供值,则该列的默认值为NULL。`sja_Mno08`列也是一个字符类型(CHAR),长度为10个字符。它使用了DEFAULT关键字,表示如果没有为该列提供值,则该列的默认值为NULL。除了主键之外,还定义了两个约束:1. UNIQUE约束,确保每个记录的组合值(sja_Mno08和sja_Classno08)都是唯一的。这意味着每个班级编号在给定的专业中必须是唯一的。2. FOREIGN KEY约束,将`sja_Mno08`列作为外键,引用了`shengja_Majors08`表格中的`sja_Mno08`列。这意味着`sja_Mno08`列的值必须存在于`shengja_Majors08`表格的`sja_Mno08`列中的某个记录中。同时,ON DELETE RESTRICT和ON UPDATE RESTRICT指定了在删除或更新`shengja_Majors08`表格中的记录时对关联的班级表格进行限制操作,即不允许删除或更新被引用的记录。因此,这个表格的主要目的是存储班级(Class)的信息。每个班级由一个唯一的10位字符编码(sja_Classno08)、一个最长为10个字符的名称(sja_Classname08)和对应的专业编码(sja_Mno08)组成。约束确保了班级编号和对应的专业编码的唯一性,并且与`shengja_Majors08`表格建立了外键关系。图5-4 建立班级表(4) 学生表建立该表格包含七个列:`sja_Sno08`、`sja_Sname08`、`sja_Ssex08`、`sja_Sage08`、`sja_Sorig08`、`sja_Scredits08`和`sja_Classno08`。`sja_Sno08`列是一个字符类型(CHAR),长度为12个字符,且不允许为空(NOT NULL)。它将用作表格的主键(PRIMARY KEY),这意味着每个记录在该列上必须具有唯一的值。`sja_Sname08`列是一个字符类型(CHAR),长度为20个字符。它表示学生的姓名。`sja_Ssex08`列是一个字符类型(CHAR),长度为5个字符。它表示学生的性别。`sja_Sage08`列是一个整数类型(INT),用于表示学生的年龄。`sja_Sorig08`列是一个字符类型(CHAR),长度为20个字符。它表示学生的来源地。`sja_Scredits08`列是一个整数类型(INT),表示学生的学分。`sja_Classno08`列是一个字符类型(CHAR),长度为10个字符。它表示学生所属的班级编号。除了主键之外,还定义了两个约束:1. UNIQUE约束,确保每个记录的组合值(sja_Sno08和sja_Classno08)都是唯一的。这意味着每个学生编号在给定的班级中必须是唯一的。2. FOREIGN KEY约束,将`sja_Classno08`列作为外键,引用了`shengja_Classes08`表格中的`sja_Classno08`列。这意味着`sja_Classno08`列的值必须存在于`shengja_Classes08`表格的`sja_Classno08`列中的某个记录中。同时,ON DELETE RESTRICT和ON UPDATE RESTRICT指定了在删除或更新`shengja_Classes08`表格中的记录时对关联的学生表格进行限制操作,即不允许删除或更新被引用的记录。因此,这个表格的主要目的是存储学生(Student)的信息。每个学生由一个唯一的12位字符编码(sja_Sno08)、一个最长为20个字符的姓名(sja_Sname08)、性别(sja_Ssex08)、年龄(sja_Sage08)、来源地(sja_Sorig08)、学分(sja_Scredits08)和所属班级编号(sja_Classno08)组成。约束确保了学生编号和所属班级编号的唯一性,并且与`shengja_Classes08`表格建立了外键关系。图5-5 建立学生表(5) 课程表建立图5-6 建立课程表(6) 报告表建立图5-7 建立报告表(7) 教师-班级表建立图5-8 建立教师-班级表(8) 学生-课程表建立图5-9 建立课程学习表(9) 教师-课程表建立图5-10 建立教师-课程表(10) 管理员表建立图5-11 创建登录表5.2 视图的建立(1)视图每门课程平均成绩统计的建立图5-10:每门课程平均成绩统计图5-11:学生成绩图5-12:每门课程学分统计图5-13:教师教授课程统计图5-14:班级开设课程统计v图5-15:学生生源地统计图5-16:教师课程成绩统计5.3 索引的建立图5-17:表教师唯一索引的建立图5-18:其他表唯一索引的建立5.4 触发器建立(1)更新学分:图5-19:更新学分触发器建立(2)删除学生:图5-20:删除学生触发器的建立5.5存储过程建立自动计算学分:图5-21:自动计算学分创建学生信息:图5-22:创建学生信息创建教师信息:图5-23:创建教师信息5.6 业务处理和查询功能SQL语句1)学生成绩按每学年进行成绩统计的SQL语句:图5-24学生成绩按每学年进行成绩统计运行结果2)学生成绩名次排定SQL语句: 图5-25学生成绩名次排定运行结果3)每门课程平均成绩统计SQL语句:图5-26每门课程平均成绩统计运行结果4)学生所学课程及学分统计SQL语句:图5-27学生所学课程及学分统计运行结果5)输入每个学生成绩时,自动生成该学生已修总学分SQL语句:图5-28自动生成该学生已修总学分运行结果图5-29自动生成该学生已修总学分运行结果图5-30自动生成该学生已修总学分运行结果图5-31自动生成该学生已修总学分运行结果6)学生成绩查询SQL语句:图5-32学生成绩查询运行结果7)教师任课查询SQL语句:图5-33教师任课查询运行结果8)班级课程开设查询SQL语句:图5-34班级课程开设查询运行结果六、应用系统开发与试运行6.1 开发平台和开发环境介绍(1)硬件环境:处理器    Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz 2.59 GHz机带 RAM    16.0 GB (15.8 GB 可用)系统类型    64 位操作系统, 基于 x64 的处理器(2) 软件环境:系统:windows 10 64家庭教育版数据库软件:Microsoft SQLSever2014应用系统开发环境:IntelliJ IDEA 2019.1.1 x64, Tomcat 8.0.50,6.2 前台界面与后台数据库连接说明,代码实现Web端与SQL连接代码:const { Client } = require('pg');var sql = `SELECT r.sja_Cno08, c.sja_Cname08, Round(AVG(r.sja_Grade08),2) as AvgGradeFROM shengja.shengja_Reports08 rJOIN shengja.shengja_Courses08 c ON c.sja_Cno08 = r.sja_Cno08GROUP BY r.sja_Cno08, c.sja_Cname08; `;const client = new Client({ host: '192.168.198.129', port: 26000, database: 'shengjamis08', user: 'opengaussuser', password: 'openGauss@123',});// 连接数据库client.connect((err) => { if (err) { console.error('Failed to connect to database:', err); } else { console.log('Connected to database'); // 在连接成功后可以执行其他数据库操作 client.query(sql, (err, result) => { if (err) { console.error('Error executing query:', err); } else { console.log('Query result:', result.rows); } // 关闭数据库连接 // client.end(); }); }});module.exports = { query: (sql) => client.query(sql),};首先,通过`require('pg')`引入了`pg`库,它是一个用于连接和操作PostgreSQL数据库的Node.js库。然后,定义了一个SQL查询语句,该查询语句从表格`shengja_Reports08`和`shengja_Courses08`中检索数据,计算每个课程的平均成绩,并返回课程编号(sja_Cno08)、课程名称(sja_Cname08)和平均成绩(AvgGrade)。接下来,创建了一个`Client`对象,配置了连接数据库所需的信息,包括主机名、端口号、数据库名称、用户名和密码。使用`client.connect()`方法连接到数据库。如果连接成功,输出"Connected to database"。如果连接失败,输出"Failed to connect to database"。在成功连接数据库后,使用`client.query()`方法执行SQL查询。传入查询语句和一个回调函数作为参数。回调函数用于处理查询结果。如果查询成功,输出查询结果的行数据;如果查询失败,输出错误信息。最后,通过`module.exports`将`query`方法导出,使其可以在其他模块中使用。`query`方法接受一个SQL语句作为参数,并使用`client.query()`方法执行该查询。总体而言,以上代码的功能是连接到指定的PostgreSQL数据库,执行给定的SQL查询语句,并将查询结果输出到控制台。6.3 系统各功能设计和运行界面截图1) 登陆界面在正式进入系统前,系统会要求用户输入账号和密码进行登录。图6-1系统登录界面2) 欢迎界面进入系统后,对用户显示欢迎信息。图6-2系统登录界面3) 学生成绩查询界面-学年成绩查询展示所选学年中学生学号,选修课程以及成绩图6-3学年成绩查询界面4) 学年排名查询界面展示所选学年学生的平均绩点以及排名图6-4学年排名查询界面5) 指定学号查询界面展示指定学生的课程成绩信息图6-5指定学号查询界面6) 学生成绩编辑界面修改编辑所选学生指定课程成绩图6-6学生成绩编辑界面7) 学生成绩插入及删除界面插入及删除所选学生选课信息以及成绩图6-7学生成绩插入界面图6-8学生成绩插入结果界面图6-9学生成绩删除界面8) 课程平均成绩查询界面查询各课程学生平均成绩图6-10课程平均成绩查询界面9) 班级课程查询界面查询指定班级开设课程图6-11班级课程查询界面10) 生源地统计界面统计学生生源地图6-12生源地统计界面11) 教师任课查询界面查询指定教师或所有教师任课信息图6-13教师任课查询界面
  • [热门活动] 【数据库专题直播有奖提问】DTSE Tech Talk 技术直播 NO.48:看直播提问题赢华为云定制长袖卫衣、华为云定制保温杯等好礼!
    中奖结果公示感谢各位小伙伴参与本次活动,本次活动获奖名单如下:请获奖的伙伴在11月20日之前点击此处填写收货地址,如逾期未填写视为弃奖。再次感谢各位小伙伴参与本次活动,欢迎关注华为云DTSE Tech Talk 技术直播更多活动~直播简介【直播主题】openGemini全新列存引擎,为您解决时序数据高基数难题【直播时间】2023年11月15日 16:30-18:00【直播专家】黄飞腾 华为云数据库DTSE技术布道师【直播简介】在数据库中,基数是指一列数据中唯一值的个数,高基数即表示该列中唯一值的数量非常大,常见的比如IP地址、电子邮件等。高基数问题长期困扰着众多时序数据库产品,主要表现为索引膨胀,内存资源消耗增加,读写性能下降等,openGemini 全新列存引擎,旨为解决高基数问题,性能表现出色。本次直播主要分享高基数场景的问题与挑战,以及 openGemini 列存引擎的设计思路、最佳实践等。直播链接:cid:link_1活动介绍【互动方式】直播前您可以在本帖留下您疑惑的问题,专家会在直播时为您解答。直播后您可以继续在本帖留言,与专家互动交流。我们会在全部活动结束后对参与互动的用户进行评选。【活动时间】即日起—2023年11月16日【奖励说明】评奖规则:活动1:直播期间在直播间提出与直播内容相关的问题,对专家评选为优质问题的开发者进行奖励。奖品:华为云定制长袖卫衣活动2:在本帖提出与直播内容相关的问题,由专家在所有互动贴中选出最优问题贴的开发者进行奖励。奖品:华为云定制保温杯更多直播活动直播互动有礼:官网直播间发口令“华为云 DTSE”抽华为云定制钢笔礼盒、填写问卷抽华为云定制鼠标等好礼【注意事项】1、所有参与活动的问题,如发现为复用他人内容或直播间中重复内容,则取消获奖资格。2、为保证您顺利领取活动奖品,请您在活动公示奖项后2个工作日内私信提前填写奖品收货信息,如您没有填写,视为自动放弃奖励。3、活动奖项公示时间截止2023年11月17日,如未反馈邮寄信息视为弃奖。本次活动奖品将于奖项公示后30个工作日内统一发出,请您耐心等待。4、活动期间同类子活动每个ID(同一姓名/电话/收货地址)只能获奖一次,若重复则中奖资格顺延至下一位合格开发者,仅一次顺延。5、如活动奖品出现没有库存的情况,华为云工作人员将会替换等价值的奖品,获奖者不同意此规则视为放弃奖品。6、其他事宜请参考【华为云社区常规活动规则】。
  • [问题求助] 华为云数据库和GaussDB数据库的区别
    华为云数据库和GaussDB数据库的区别
  • [问题求助] 华为云RDS数据库是否支持弹性伸缩?如何实现?
    华为云RDS数据库是否支持弹性伸缩?如何实现?
  • [问题求助] 默认情况下,每个用户在华为云RDS中最多可以创建多少个数据库实例?
    默认情况下,每个用户在华为云RDS中最多可以创建多少个数据库实例?
  • [问题求助] 华为云RDS数据库的备份文件存储在何处,是否对租户可见?
    华为云RDS数据库的备份文件存储在何处,是否对租户可见?
  • [问题求助] 分布式数据库和集中式数据库有什么区别,各有什么优势?
    分布式数据库和集中式数据库有什么区别,各有什么优势?
  • [其他] 数据库板块2023.10月集合
    java线程池怎么用最安全?https://bbs.huaweicloud.com/forum/thread-02116134011080136032-1-1.htmlmysql大批量数组in查询的时候效率问题https://bbs.huaweicloud.com/forum/thread-0296134011022191031-1-1.html针对枚举类型字段是否有必要加索引https://bbs.huaweicloud.com/forum/thread-0273134010894710031-1-1.html 如何修改RDS实例管理员信息? https://bbs.huaweicloud.com/forum/thread-0275133665350138039-1-1.html在控制台输入mysql -u root -p 报错command not foundhttps://bbs.huaweicloud.com/forum/thread-0235133622699806025-1-1.html华为云数据库的延迟和处理突发事件上对数据的安全怎么样?https://bbs.huaweicloud.com/forum/thread-0235133622363369024-1-1.html看到小助手回复的华为云数据库种类 这么多种类都是做什么的?https://bbs.huaweicloud.com/forum/thread-0291133621815356028-1-1.html什么是云数据库RDS?https://bbs.huaweicloud.com/forum/thread-0255132906325432004-1-1.htmlmysql数据库和华为云数据库的区别https://bbs.huaweicloud.com/forum/thread-0258133535980717021-1-1.htmljava解析KAKFKA从DRS获取的AVRO数据反序列化问题https://bbs.huaweicloud.com/forum/thread-02125134119629898043-1-1.html如何在华为云数据库中实现数据的加密和安全性?https://bbs.huaweicloud.com/forum/thread-0248134149790237068-1-1.html
  • [技术干货] Milvus 环境搭建
    什么是向量检索向量是具有一定大小和方向的量,可以简单理解为一串数字的集合,就像一行多列的矩阵,比如:[2,0,1,9,0,6,3,0]。每一行代表一个数据项,每一列代表一个该数据项的各个属性。特征向量是包含事物重要特征的向量。大家比较熟知的一个特征向量是RGB (红-绿-蓝)色彩。每种颜色都可以通过对红®、绿(G)、蓝(B)三种颜色的比例来得到。这样一个特征向量可以描述为:颜色 = [红,绿,蓝]。向量检索是指从向量库中检索出距离目标向量最近的 K 个向量。一般我们用两个向量间的欧式距离,余弦距离等来衡量两个向量间的距离,一次来评估两个向量的相似度Milvus 介绍Milvus的官网地址是:cid:link_10 github地址是:https://github.com/milvus-io/milvus,目前Fork数为:2.6k,Star有23.7kMilvus创建于2019年,其目标只有一个:存储、索引和管理由深度神经网络和其他机器学习(ML)模型生成的大量嵌入向量。作为一个专门设计用于处理输入向量查询的数据库,它能够在万亿规模上对向量进行索引。与现有的关系数据库主要按照预定义的模式处理结构化数据不同,Milvus是从自底向上设计的,以处理从非结构化数据转换而来的嵌入向量。Milvus 是一款开源的向量数据库,支持针对 TB 级向量的增删改操作和近实时查询,具有高度灵活、稳定可靠以及高速查询等特点。Milvus 集成了 Faiss、NMSLIB、Annoy 等广泛应用的向量索引库,Milvus 支持数据分区分片、数据持久化、增量数据摄取、标量向量混合查询、time travel 等功能,同时大幅优化了向量检索的性能,可满足任何向量检索场景的应用需求,提供了一整套简单直观的 API,让你可以针对不同场景选择不同的索引类型。此外,Milvus 还可以对标量数据进行过滤,进一步提高了召回率,增强了搜索的灵活性。Milvus 采用共享存储架构,存储计算完全分离,计算节点支持横向扩展。从架构上来看,Milvus 遵循数据流和控制流分离,整体分为了四个层次:分别为接入层(access layer)、协调服务(coordinator service)、执行节点(worker node)和存储层(storage)。各个层次相互独立,独立扩展和容灾。随着互联网的发展和发展,非结构化数据变得越来越普遍,包括电子邮件、论文、物联网传感器数据、Facebook照片、蛋白质结构等等。为了让计算机理解和处理非结构化数据,使用嵌入技术将这些数据转换为向量。Milvus存储并索引这些向量。Milvus能够通过计算两个向量的相似距离来分析它们之间的相关性。如果两个嵌入向量非常相似,则表示原始数据源也非常相似。Milvus 向量数据库专为向量查询与检索设计,能够为万亿级向量数据建立索引。与现有的主要用作处理结构化数据的关系型数据库不同,Milvus 在底层设计上就是为了处理由各种非结构化数据转换而来的 Embedding 向量而生。虚拟机+docker 搭建 milvus的硬件要求使用Docker Compose独立安装Milvus,安装前请检查硬件和软件的要求:至少使用2个vcpu和8gb的初始内存。否则可能导致安装失败。详见官网:cid:link_4下载docker-compose配置文件先安装wget命令yum install wget下载配置文件,通过docker-compose进行安装wget https://github.com/milvus-io/milvus/releases/download/v2.3.2/milvus-standalone-docker-compose.yml如果无法访问github,可以直接通过浏览器下载,然后通过rz命令上传到虚拟机yum install lrzsz -y由于后期milvus会在当前目录生成很多数据,因此建议创建一个新的目录,然后再上传 docker-compose.yml 文件mkdir milvus cd milvus rz安装 dockeryum install docker安装docker-compose直接下载release版本手动安装首先找到docker-compose的github路径:cid:link_5uname -a 查看系统信息uname -s 查看系统名称uname -a 查看系统架构下载安装文件sudo curl -L "cid:link_5/download/2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose赋予执行权限sudo chmod +x /usr/local/bin/docker-compose创建软连接sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose测试,能出版本号就说明安装成功了docker-compose --version使用pip 命令自动安装安装 pipyum -y install epel-release yum -y install python-pip升级 pip pip install --upgrade pip安装 docker-compose 插件 pip install docker-compose验证安装是否成功 docker-compose --version通过 docker-compose 启动容器在没启动docker 的情况下,直接启动docker-compose会报错,因此要先启动docker,并将docker设置为开机自动启动systemctl start docker systemctl enable docker通过docker-compose up -d 命令在后台运行docker-compose容器,默认的配置文件名是 docker-compose.yml,可以通过-f 参数进行修改 sudo docker-compose -f milvus-standalone-docker-compose.yml up -d使用下面命令,查看容器允许状态,我这里全部启动失败了docker-compose ps -a使用下面命令,查看容器出错日志sudo docker-compose logs etcd | grep error报错的内容是目录无法访问: open /etcd: permission denied,这是因为CentOS7中的安全模块selinux把权限禁掉了。解决办法在控制台里面输入 setenforce 0 关闭 selinux在docker 命令中配置 --privileged=true,或者在 docker-compose 服务里增加 privileged: true (因为milvus会启动3个服务,因此这里一共要加3处) 注意,如果你修改了存放目录,这里要先删除之前的镜像docker rm milvus-standalone docker rm milvus-minio docker rm milvus-etcd然后重新启动容器(我这里在前台启动,目的是方便查看日志,所以我没加-d参数)sudo docker-compose -f milvus-standalone-docker-compose.yml up搭建python环境在外部window系统搭建如果你是在window上通过python连接虚拟机中的Milvus,只需要在window中安装 python 环境即可( 我这里装的是 anaconda + python3.11.4 )pip3 install pymilvus==2.3.2在虚拟机中搭建如果你要直接在虚拟机中运行python,则需要在虚拟机中安装python环境,具体步骤如下首先,安装anaconda:cid:link_8wget cid:link_8Anaconda3-2023.09-0-Linux-x86_64.sh由于我这里wget出错,因此我是直接从window上下载,然后使用rz命令传到centos7虚拟机的下载完sh安装文件后,先给文件加上可执行权限chmod +x Anaconda3-2023.09-0-Linux-x86_64.sh然后执行命令进行安装bash Anaconda3-2023.09-0-Linux-x86_64.sh# 按照安装提示,键入回车 Please, press ENTER to continue >>> ENTER # 中途大概按两次回车进行翻页 # 输入yes,表示同意安装协议 Do you accept the license terms? [yes|no][no] >>> yes # 多次yes后安装成功安装好后,anaconda会写入你的环境变量(用户名不同,地址也会有所不同)执行 source ~/.bashrc 命令刷新环境变量,然后就可以使用 conda --version 查看 conda 的版本了。目前最新版的 conda 是 23.7.4 版本,对应的python 版本是 3.11.5 (centos7 默认的python版本是2.7.5,这里是进入了conda 的base环境,所以是3.11.5版本)milvus需要的是python3.7+ 的运行环境。环境搭建好后,需要安装依赖库 pymilvus详见官网介绍:cid:link_6conda install numpy pip3 install pymilvus==2.3.2注意:pymilvus 默认conda 是安装不来的,但是pip3 可以直接安装可以用如下命令验证环境是否搭建成功(没报错就说明成功了)python3 -c "from pymilvus import Collection"也可以使用 python 命令行的方式执行连接 Milvusmilvus使用的端口是19530查看容器使用的端口:docker port milvus-standalone查看容器是否在某个端口上进行监听:docker port milvus-standalone 19530/tcp我这里使用的是开源的Virtual box,网络使用的是net方式。因此找到端口转发,配置19530即可这里,子系统的IP,可以通过ip a 查看并替换成你的虚拟机上的IP Milvus在Github上为其提供了python语言的测试demo,地址为:https://github.com/milvus-io/pymilvus/tree/master/examples。用来测试的文件是 hello_milvus.py由于需要vpn才能访问,我贴一下它的代码# hello_milvus.py demonstrates the basic operations of PyMilvus, a Python SDK of Milvus. # 1. connect to Milvus # 2. create collection # 3. insert data # 4. create index # 5. search, query, and hybrid search on entities # 6. delete entities by PK # 7. drop collection import time import numpy as np from pymilvus import ( connections, utility, FieldSchema, CollectionSchema, DataType, Collection, ) fmt = "\n=== {:30} ===\n" search_latency_fmt = "search latency = {:.4f}s" num_entities, dim = 3000, 8 ################################################################################# # 1. connect to Milvus # Add a new connection alias `default` for Milvus server in `localhost:19530` # Actually the "default" alias is a buildin in PyMilvus. # If the address of Milvus is the same as `localhost:19530`, you can omit all # parameters and call the method as: `connections.connect()`. # # Note: the `using` parameter of the following methods is default to "default". print(fmt.format("start connecting to Milvus")) connections.connect("default", host="localhost", port="19530") has = utility.has_collection("hello_milvus") print(f"Does collection hello_milvus exist in Milvus: {has}") ################################################################################# # 2. create collection # We're going to create a collection with 3 fields. # +-+------------+------------+------------------+------------------------------+ # | | field name | field type | other attributes | field description | # +-+------------+------------+------------------+------------------------------+ # |1| "pk" | VarChar | is_primary=True | "primary field" | # | | | | auto_id=False | | # +-+------------+------------+------------------+------------------------------+ # |2| "random" | Double | | "a double field" | # +-+------------+------------+------------------+------------------------------+ # |3|"embeddings"| FloatVector| dim=8 | "float vector with dim 8" | # +-+------------+------------+------------------+------------------------------+ fields = [ FieldSchema(name="pk", dtype=DataType.VARCHAR, is_primary=True, auto_id=False, max_length=100), FieldSchema(name="random", dtype=DataType.DOUBLE), FieldSchema(name="embeddings", dtype=DataType.FLOAT_VECTOR, dim=dim) ] schema = CollectionSchema(fields, "hello_milvus is the simplest demo to introduce the APIs") print(fmt.format("Create collection `hello_milvus`")) hello_milvus = Collection("hello_milvus", schema, consistency_level="Strong") ################################################################################ # 3. insert data # We are going to insert 3000 rows of data into `hello_milvus` # Data to be inserted must be organized in fields. # # The insert() method returns: # - either automatically generated primary keys by Milvus if auto_id=True in the schema; # - or the existing primary key field from the entities if auto_id=False in the schema. print(fmt.format("Start inserting entities")) rng = np.random.default_rng(seed=19530) entities = [ # provide the pk field because `auto_id` is set to False [str(i) for i in range(num_entities)], rng.random(num_entities).tolist(), # field random, only supports list rng.random((num_entities, dim)), # field embeddings, supports numpy.ndarray and list ] insert_result = hello_milvus.insert(entities) hello_milvus.flush() print(f"Number of entities in Milvus: {hello_milvus.num_entities}") # check the num_entities ################################################################################ # 4. create index # We are going to create an IVF_FLAT index for hello_milvus collection. # create_index() can only be applied to `FloatVector` and `BinaryVector` fields. print(fmt.format("Start Creating index IVF_FLAT")) index = { "index_type": "IVF_FLAT", "metric_type": "L2", "params": {"nlist": 128}, } hello_milvus.create_index("embeddings", index) ################################################################################ # 5. search, query, and hybrid search # After data were inserted into Milvus and indexed, you can perform: # - search based on vector similarity # - query based on scalar filtering(boolean, int, etc.) # - hybrid search based on vector similarity and scalar filtering. # # Before conducting a search or a query, you need to load the data in `hello_milvus` into memory. print(fmt.format("Start loading")) hello_milvus.load() # ----------------------------------------------------------------------------- # search based on vector similarity print(fmt.format("Start searching based on vector similarity")) vectors_to_search = entities[-1][-2:] search_params = { "metric_type": "L2", "params": {"nprobe": 10}, } start_time = time.time() result = hello_milvus.search(vectors_to_search, "embeddings", search_params, limit=3, output_fields=["random"]) end_time = time.time() for hits in result: for hit in hits: print(f"hit: {hit}, random field: {hit.entity.get('random')}") print(search_latency_fmt.format(end_time - start_time)) # ----------------------------------------------------------------------------- # query based on scalar filtering(boolean, int, etc.) print(fmt.format("Start querying with `random > 0.5`")) start_time = time.time() result = hello_milvus.query(expr="random > 0.5", output_fields=["random", "embeddings"]) end_time = time.time() print(f"query result:\n-{result[0]}") print(search_latency_fmt.format(end_time - start_time)) # ----------------------------------------------------------------------------- # pagination r1 = hello_milvus.query(expr="random > 0.5", limit=4, output_fields=["random"]) r2 = hello_milvus.query(expr="random > 0.5", offset=1, limit=3, output_fields=["random"]) print(f"query pagination(limit=4):\n\t{r1}") print(f"query pagination(offset=1, limit=3):\n\t{r2}") # ----------------------------------------------------------------------------- # hybrid search print(fmt.format("Start hybrid searching with `random > 0.5`")) start_time = time.time() result = hello_milvus.search(vectors_to_search, "embeddings", search_params, limit=3, expr="random > 0.5", output_fields=["random"]) end_time = time.time() for hits in result: for hit in hits: print(f"hit: {hit}, random field: {hit.entity.get('random')}") print(search_latency_fmt.format(end_time - start_time)) ############################################################################### # 6. delete entities by PK # You can delete entities by their PK values using boolean expressions. ids = insert_result.primary_keys expr = f'pk in ["{ids[0]}" , "{ids[1]}"]' print(fmt.format(f"Start deleting with expr `{expr}`")) result = hello_milvus.query(expr=expr, output_fields=["random", "embeddings"]) print(f"query before delete by expr=`{expr}` -> result: \n-{result[0]}\n-{result[1]}\n") hello_milvus.delete(expr) result = hello_milvus.query(expr=expr, output_fields=["random", "embeddings"]) print(f"query after delete by expr=`{expr}` -> result: {result}\n") ############################################################################### # 7. drop collection # Finally, drop the hello_milvus collection print(fmt.format("Drop collection `hello_milvus`")) utility.drop_collection("hello_milvus")下载好后,使用 docker ps -a 确保虚拟机中的milvus服务器已全部开启执行命令 python hello_milvus.py,或是使用编辑器执行均可(我这里用的是Anaconda提供的Spyder)完整的执行结果如下=== start connecting to Milvus === Does collection hello_milvus exist in Milvus: False === Create collection `hello_milvus` === === Start inserting entities === Number of entities in Milvus: 3000 === Start Creating index IVF_FLAT === === Start loading === === Start searching based on vector similarity === hit: id: 2998, distance: 0.0, entity: {'random': 0.9728033590489911}, random field: 0.9728033590489911 hit: id: 1262, distance: 0.08883658051490784, entity: {'random': 0.2978858685751561}, random field: 0.2978858685751561 hit: id: 1265, distance: 0.09590047597885132, entity: {'random': 0.3042039939240304}, random field: 0.3042039939240304 hit: id: 2999, distance: 0.0, entity: {'random': 0.02316334456872482}, random field: 0.02316334456872482 hit: id: 1580, distance: 0.05628091096878052, entity: {'random': 0.3855988746044062}, random field: 0.3855988746044062 hit: id: 2377, distance: 0.08096685260534286, entity: {'random': 0.8745922204004368}, random field: 0.8745922204004368 search latency = 0.4576s === Start querying with `random > 0.5` === query result: -{'embeddings': [0.20963514, 0.39746657, 0.12019053, 0.6947492, 0.9535575, 0.5454552, 0.82360446, 0.21096309], 'pk': '0', 'random': 0.6378742006852851} search latency = 0.5080s query pagination(limit=4): [{'random': 0.6378742006852851, 'pk': '0'}, {'random': 0.5763523024650556, 'pk': '100'}, {'random': 0.9425935891639464, 'pk': '1000'}, {'random': 0.7893211256191387, 'pk': '1001'}] query pagination(offset=1, limit=3): [{'random': 0.5763523024650556, 'pk': '100'}, {'random': 0.9425935891639464, 'pk': '1000'}, {'random': 0.7893211256191387, 'pk': '1001'}] === Start hybrid searching with `random > 0.5` === hit: id: 2998, distance: 0.0, entity: {'random': 0.9728033590489911}, random field: 0.9728033590489911 hit: id: 747, distance: 0.14606499671936035, entity: {'random': 0.5648774800635661}, random field: 0.5648774800635661 hit: id: 2527, distance: 0.1530652642250061, entity: {'random': 0.8928974315571507}, random field: 0.8928974315571507 hit: id: 2377, distance: 0.08096685260534286, entity: {'random': 0.8745922204004368}, random field: 0.8745922204004368 hit: id: 2034, distance: 0.20354536175727844, entity: {'random': 0.5526117606328499}, random field: 0.5526117606328499 hit: id: 958, distance: 0.21908017992973328, entity: {'random': 0.6647383716417955}, random field: 0.6647383716417955 search latency = 0.1996s === Start deleting with expr `pk in ["0" , "1"]` === query before delete by expr=`pk in ["0" , "1"]` -> result: -{'random': 0.6378742006852851, 'embeddings': [0.20963514, 0.39746657, 0.12019053, 0.6947492, 0.9535575, 0.5454552, 0.82360446, 0.21096309], 'pk': '0'} -{'random': 0.43925103574669633, 'embeddings': [0.52323616, 0.8035404, 0.77824664, 0.80369574, 0.4914803, 0.8265614, 0.6145269, 0.80234545], 'pk': '1'} query after delete by expr=`pk in ["0" , "1"]` -> result: [] === Drop collection `hello_milvus` ===停止 milvussudo docker-compose down删除milvus的数据在停止Milvus后,可以使用如下命令删除 milvus 容器挂载在本机上的数据sudo rm -rf volumes安装 Attu 可视化工具在github上有安装命令,地址为:cid:link_0docker 的运行命令如下docker run -p 8000:3000 -e HOST_URL=http://{ your machine IP }:8000 -e MILVUS_URL={your machine IP}:19530 zilliz/attu:latest注意:这里的本机IP可以直接使用127.0.0.1docker run -p 8000:3000 -e HOST_URL=http://127.0.0.1:8000 -e MILVUS_URL=127.0.0.1:19530 zilliz/attu:latest启动报错了$ node node[9]: ../src/node_platform.cc:61:std::unique_ptr<long unsigned int> node::WorkerThreadsTaskRunner::DelayedTaskScheduler::Start(): Assertion `(0) == (uv_thread_create(t.get(), start_thread, this))' failed. 1: 0xb57f90 node::Abort() [node] 2: 0xb5800e [node] 3: 0xbc915e [node] 4: 0xbc9230 node::NodePlatform::NodePlatform(int, v8::TracingController*, v8::PageAllocator*) [node] 5: 0xb1b3d1 node::InitializeOncePerProcess(int, char**, node::InitializationSettingsFlags, node::ProcessFlags::Flags) [node] 6: 0xb1bc89 node::Start(int, char**) [node] 7: 0x7f2ca389fd90 [/lib/x86_64-linux-gnu/libc.so.6] 8: 0x7f2ca389fe40 __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6] 9: 0xa93f0e _start [node] Aborted (core dumped)使用 docker version 查看docker 版本可以发现 centos7 通过yum安装的docker版本应该是太旧了下面需要重装docker卸载原有dockersudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine安装yum-utils和源sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo开始安装docker引擎和客户端等sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin启动docker并设置为开机自启动sudo systemctl start docker sudo systemctl enable docker查看docker版本(可以看到,现在显示的这个版本号才是正常的)docker version逐个删除原有镜像:docker rm <镜像ID>(镜像ID可以使用 docker ps -a进行查看)接着分别启动 milvus-standalone和attu(这个命令要在官网的基础上加个-d参数,使其在后台启动)docker-compose -f milvus-standalone-docker-compose.yml up -d docker run -d -p 8000:3000 -e HOST_URL=http://127.0.0.1:8000 -e MILVUS_URL=127.0.0.1:19530 zilliz/attu:latest然后在 Virtualbox 的 net网络地址转发这里加一个8000的端口映射接着打开window上的浏览器,输入 cid:link_9点击连接之后报错:Error: 14 UNAVAILABLE: No connection established查看所有容器ID,先关闭掉错误的attu容器,然后再进行删除docker ps -a docker stop <attu容器ID> docker rm <attu容器ID>将Milvus的IP从127.0.0.1改为本机IP(enp0s3)或docker(docker0)的IP,都是可以的。使用 ip a 或 ifconfig 命令,均可以查看到本机所有网卡对应的IP使用修改IP后的命令重启Attu容器docker run -d -p 8000:3000 -e HOST_URL=http://127.0.0.1:8000 -e MILVUS_URL=172.17.0.1:19530 zilliz/attu:latest然后使用外部浏览器输入 cid:link_9,进行连接,并登录。这里连接成功了另外,你也可以将Attu加入到docker-compose.xml文件中,让它们一起启动,命令如下 attu: container_name: attu image: zilliz/attu:v2.2.6 environment: MILVUS_URL: milvus-standalone:19530 ports: - "8000:3000" depends_on: - "standalone"参考:cid:link_2
  • PostgreSQL 主从复制:流复制【转】
    背景PostgreSQL 9.1之前,主从复制传输以WAL日志文件为单位,主库写完WAL日志后再传输给从库,导致主从延迟较大。基于这种情况,PostgreSQL 9.1引入主从流复制,以WAL日志的record为传输单位,从库及时同步主库数据,并且应用每个WAL record,因此能做到同步复制。另外实现了Hot Standby,从库在应用WAL record的同事能够提供制度服务。架构PG主从流复制的核心有三个进程构成:    walsender:用于主库发送WAL日志记录到从库;    walreceiver:用于从库接收主库的WAL日志记录;    startup:用于从库apply日志。流复制的启动1.启动过程    启动主从服务器;    从库节点启动startup进程;    从库节点启动walreceiver进程;    walreceiver进程向主库节点发送连接请求,如果主库尚未启动,walreceiver会定期重发该请求;    当主节点收到连接请求时,将启动walsender进程,并建立walsender和walreceiver之间的TCP连接;    walreceiver发送从库节点最新的LSN;    如果从库最新的LSN小于主库最新LSN,即落后。walsender会将前一个LSN到后一个LSN之间的wal数据发送到walreceiver;    流复制开始工作。2.walsender状态通过pg_stat_replication试图可以查看所有运行的walsender状态walsender进程可能的状态如下    start-up    catch-up    streaming    backup3。从库节点长期停机启动    PostgreSQL 9.4之前,如果从库节点请求的WAL段在主库节点已经被覆盖,那么备份节点将无法追上主节点。暂时没有解决方案,需要把wal_keep_segments参数调大;    PostgreSQL 9.4之后,使用复制槽(replication)来预防——通过暂停walreceiver进程,将含有未发送wal段的pg_xlog保存在复制槽中。复制槽可以提供wal数据发送的灵活性,主要用于逻辑复制。流复制过程流复制包括两个方面:日志传输和数据同步    流复制基于日志传输,主节点会在写入日志记录时,将WAL数据发送到已连接的从库节点;    同步复制需要数据库同步,主库节点和多个从库节点通信,从而同步整个数据库集群。1.主从之间的通信假设在从库节点处于同步模式,hot_standby参数已禁用、wal_level为‘archive’,即主库配以下参数:synchronous_standby_names = 'standby1'hot_standby = offwal_level = archive假设后台进程在自动提交模式下在主节点发出一条insert语句。后台进程启动事务、发出一条insert语句、然后立即提交事务。分析如下:​    后台进程执行XLogInsert()和XLogFlush()函数,将wal数据写入缓存并flush到wal段文件;    walsender将写入的wal段文件的wal数据发送到walreceiver;    主库节点发送wal数据后,后端进程继续等待来自从库节点的ACK响应。即后台进程执行内部函数SyncRepWaitForLSN()来获取latch,并等待释放;    walreceiver通过write()函数将接收到的wal数据写入从库节点的wal缓存,并且返回ACK响应给walsender;    walreceiver通过fsync()函数将wal数据全部flush到wal段文件,并且再返回一个ACK响应给walsender,通知startup进程wal相关数据已更新;    startup进程应用写入wal段文件的wal数据;    walsender在接受到ACK响应后释放latch,然后后端进程完成commit或abort动作。latch释放的时间取决于synchronous_commit:如果参数是on,则在步骤(5)接收ACK响应后释放latch;如果是remote_wirte,那么在步骤(4)接收ACK就释放latch,2.ACK响应内容ACK响应将从库节点的内部信息发送给主节点,包括以下四个项目:    已写入的最新WAL数据的LSN位置    已刷新的最新WAL数据的LSN位置    startup进程最新应用的wal数据的LSN位置    发送此ACK的时间戳walreceiver不仅在写入和刷新WAL数据的时候返回ACK响应,还定期发送从库节点心跳(心跳发送间隔通过wal_receiver_status_interval设置,默认10秒)。因此,主库节点始终掌握所有已连接的从库节点状态。可通过以下查询看到所连接从库相关的LSN信息SELECT application_name AS host,write_location AS write_LSN,flush_location AS flush_LSN,replay_location AS replay_LSN FROM pg_stat_replication;   host   | write_lsn | flush_lsn | replay_lsn----------+-----------+-----------+------------ standby1 | 0/5000280 | 0/5000280 | 0/5000280 standby2 | 0/5000280 | 0/5000280 | 0/5000280    1    2    3    4    5    63.发生故障时的行为即使同步从库节点故障,不能再返回ACK响应到主库节点,主库节点也会继续等待从库节点的ACK响应。因此,在主库节点运行的事务会无法提交,后续的查询也无法执行,即主库节点的所有操作都停止(流复制不支持由于超时自动降级为异步模式)。两种方法避免该情况的发生:    提供多台从库节点来提高系统的可用性;    通过手动执行以下步骤来同步流复制转换到异步流复制。    # 1.设置synchronous_standby_names为空串    synchronous_standby_names = ''         #2.执行reload命令重载配置文件    pg_ctl -D $PGDATA reload    ​管理多个从库节点1.同步优先级与同步状态主库节点会为所有的从库节点指定sync_priority(同步优先级)和sync_state(同步状态);同步优先级sync_priority表示从库节点在同步模式下的优先级,是一个固定值,其值越小则优先级越高。0是一个特殊值,表示异步模式。从库节点优先级是一个有序列表,按照synchronous_standby_names中的顺序一次给出。同步状态sync_stat表示从库节点的状态,其值是由各个从库节点的运行状态以及优先级而定,以下是可能的值:    sync:具有最高优先级的同步模式从库节点状态;    potential:同步模式下优先级>=2的从库节点状态。如果sync状态(最高优先级)的从库节点故障,优先级第二稿(优先级=2)的从库节点会代替故障节点变为最高优先级;    aysnc:异步模式从库节点的固定值,除非修改同步模式,否则他们的状态永远不会是sync和potential;可以通过pg_stat_replication视图查看这两个值SELECT application_name AS host,sync_priority,sync_state FROM pg_stat_replication;   host   | sync_priority | sync_state----------+---------------+------------ standby1 |             1 | sync standby2 |             2 | potential2.主库节点管理多个从库节点主库节点仅等待sync状态从库节点的ACK响应,即主库节点只确保sync主功能太从库节点已写入并刷新WAL数据。因此,在流复制中,只有sync状态的从库节点和主库节点是一直同步的;下图展示potential状态下从库ACK响应应该早于sync状态从库的情况:此时主库不会完成当前事务的提交,而是要继续等待sync状态从库ACK响应。当收到sync状态从库ACK响应时,主库后端进程才会释放latch并完成事务提交。    如果主库先接受到sync从库的ACK响应,则会立即完成当前事务的提交,而不会去确认potential状态的从库是否已经写入并刷新WAL数据。3.从库发生故障时的行为potential或async状态从库节点故障主库节点会终止连接到故障备份节点walsender进程,并继续进行职级的事务处理。即主库节点的事务处理不厚道这两类从库节点故障的而影响。sync状态从库节点故障主库终止连接到故障节点的walsender进程,potential状态从库节点顶替故障的从库节点变为sync状态(期间主库不可用),在替换完成后主库胡肌肤事务处理。因此,从库节点的故障检查对提高流复制高可用至关重要。从库节点的故障检测1.从库节点的故障检测    当检测到walsender与walreceiver的连接中断时,主节点立即判定从库节点或walreceiver出现故障;    当底层网络函数由于未能成功读/写walreceiver套接字接口而返回错误时,主库节点会立即判定其失效。2.硬件或者网络故障检测如果walreceiver在wal_sender_timeout(默认60s)乜有返回任何节点,则主库节点会认为从库节点出现故障。根据故障类型,在故障和检测之间可能会有时间差,特别是如果在sync状态从库中发生第二种故障,那么即使有多个potential状态从库节点正常工作,检测到sync状态从库失效,主库仍然会有一段时间不可用。进程通信详细过程1.walsender和walreceiver的流复制过程2.walreceiver和startup进程————————————————版权声明:本文为CSDN博主「LanceJerry」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/LanceJerry/article/details/123641114
  • [技术干货] 一文彻底弄懂PostgreSQL流复制(全网最详细)【转】
    0、前言之前也做了一些流复制的实验,今天就想着把了解的PostgreSQL流复制的内容总结下,整理了这篇文章。1、概述1.1、什么是流复制?如果有人问你PostgreSQL的流复制究竟是什么?你大概会说通过wal日志来进行数据同步之类的,的确如此,流复制大概就是这么回事。但是准确的来说:PostgreSQL通过wal日志来传送的方式有两种:基于文件的日志传送和流复制。不同于基于文件的日志传送,流复制的关键在于“流”,所谓流,就是没有界限的一串数据,类似于河里的水流,是连成一片的。因此流复制允许一台后备服务器比使用基于文件的日志传送更能保持为最新的状态。比如我们有一个大文件要从本地主机发送到远程主机,如果是按照“流”接收到的话,我们可以一边接收,一边将文本流存入文件系统。这样,等到“流”接收完了,硬盘写入操作也已经完成。1.2、流复制发展历史流复制之前的手段:像我们上面说的,pg在流复制出现之前,使用的就是基于文件的日志传送:对wal日志进行拷贝,因此从库始终落后主库一个日志文件,并且使用rsync工具同步data目录。而流复制出现是从2010年推出的pg9.0开始的,其历史大致为:    起源:pg9.0开始支持流式物理复制,用户可以通过流式复制,构建只读备库    (主备物理复制,块级别一致)。流式物理复制可以做到极低的延迟(通常在1毫秒以内)。    同步流复制:pg9.1开始支持同步复制,但是当时只支持一个同步流复制备节点(例如配置了3个备,只有一个是同步模式的,其他都是异步模式)。同步流复制的出现,保证了数据的0丢失。    级联流复制:pg9.2支持级联流复制。即备库还可以再连备库。    流式虚拟备库:pg9.2还支持虚拟备库,即就是只有WAL,没有数据文件的备库。    逻辑复制:pg9.4开始可以实现逻辑复制,逻辑复制可以做到对主库的部分复制,例如表级复制,而不是整个集群的块级一致复制。    增加多种同步级别:pg9.6版本开始可以通过synchronous_commit参数,来配置事务的同步级别。1.3、流复制概述流复制其原理为:备库不断的从主库同步相应的数据,并在备库apply每个WAL record,这里的流复制每次传输单位是WAL日志的record。PostgreSQL物理流复制按照同步方式分为两类:    异步流复制    同步流复制物理流复制具有以下特点:1、延迟极低,不怕大事务2、支持断点续传3、支持多副本4、配置简单5、备库与主库物理完全一致,并支持只读2、流复制基础在学习流复制之前,我们先来了解一些相关的基础知识。2.1、wal日志介绍在这篇文章中我曾经详细介绍过PostgreSQL中的wal日志:PostgreSQL WAL日志详解这里再简单介绍下:WAL日志机制保证了事务的持久性和数据完整性,同时又避免了频繁IO对性能的影响。为了保证数据库中数据的持久性,即事务提交后,即使数据库出现故障也能保证数据的可靠性。最简单的方法就是:数据一提交就刷到磁盘上。但是这样对于事务非常频繁的系统,一有事务提交就去刷新脏数据,会对数据库性能产生非常不好的影响。因此使用wal日志来记录数据的更改,这样每当发生事务提交,只需要通过提交wal日志即可,而且wal日志的提交是顺序的,性能也很高。2.2、wal日志解读对数据库操作会以record为单位首先记录到wal日志中,在checkpoint时才对数据进行刷盘(background writer会定时刷脏数据,但最终还是都由checkpoint确认都刷盘成功)。聊了这么久wal日志,我们都还不知道wal日志在哪?长啥样。。。wal日志位置:$PGDATA/pg_wal(pg10之前叫pg_xlog)wal日志文件命名规则:我们看到的wal日志是这样的:000000010000000100000092其中前8位:00000001表示timeline;中间8位:00000001表示logid;最后8位:00000092表示logsegwal日志LSN编号规则:1/920001F8(高32位/低32位)对照关系:1、wal日志的logseg前6位始终是0,后两位是LSN低32位/16MB(2*24),即LSN的前两位。如上例中logseg最后两位是92,LSN低32前两位也是92。2、LSN在wal日志中的偏移量即LSN低32位中后24位对应的十进制值。例如当前wal日志偏移量为504bill=# select pg_walfile_NAME_OFFSET(pg_current_wal_lsn());     pg_walfile_name_offset     -------------------------------- (000000010000000100000092,504)(1 row)bill=# select x'1F8'::int; int4------  504(1 row)    1    2    3    4    5    6    7    8    9    10    112.3、wal日志内部详解接下来我们来看看wal日志里面究竟记录的是些什么内容。如果你直接查看wal日志,可能会收到下面这样的提示:因为wal日志是二进制格式的文件,不过我们可以使用pg_waldump这个工具来将其转换成可读的文件。例1:首先来看看insert数据时wal日志里面记录了些什么。bill=# begin;BEGINbill=# select pg_current_wal_lsn(); pg_current_wal_lsn-------------------- 1/92021290(1 row)bill=# insert into tbl values(1,'bill');INSERT 0 1bill=# select pg_current_wal_lsn(); pg_current_wal_lsn-------------------- 1/92021308(1 row)bill=# end;COMMIT    1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18接下来我们看看wal日志里面内容:可以看到wal日志里面记录了上面的insert操作。例2:我们再看看update时wal日志里面记录的内容:bill=# select pg_current_wal_lsn(); pg_current_wal_lsn-------------------- 1/92021450(1 row)bill=# update tbl set info = 'foucus' where id = 1;UPDATE 1    1    2    3    4    5    6    7    8在这里插入图片描述这里简单介绍下这条记录的内容:rmgr: Heap        len (rec/tot):     65/   177, tx:        717, lsn: 1/92021450, prev 1/92021418, desc: HOT_UPDATE off 1 xmax 717 flags 0x20 ; new off 2 xmax 0, blkref #0: rel 1663/16395/17623 blk 0 FPW    1    rmgr: Heap :PostgreSQL内部将WAL日志归类到20多种不同的资源管理器。这条WAL记录所属资源管理器为 Heap,即堆表。除了Heap还有Btree,Transaction等。    len (rec/tot): 65/ 177:wal记录的长度。    tx: 717: 事务号。    lsn: 1/92021450:本条wal记录的lsn。    prev 1/92021418:上条wal记录的lsn。    desc: HOT_UPDATE off 1 xmax 717 flags 0x20 ; new off 2 xmax 0: 这是一条热更新类型的记录,旧数据    offset为1,xmax为717。旧tuple在page中的位置为1(即ctid的后半部分),新tuple在page中的位置为2。    blkref #0: rel 1663/16395/17623 blk 0 :引用的第一个page(新tuple所在page)所属的堆表文件为1663/13543/16469,块号为0(即ctid的前半部分)。3、流复制原理3.1、日志提交过程从上图我们可以看到流复制中日志提交的大致流程为:1、事务commit后,日志在主库写入wal日志,还需要根据配置的日志同步级别,等待从库反馈的接收结果。2、主库通过日志传输进程将日志块传给从库,从库接收进程收到日志开始回放,最终保证主从数据一致性。3.2、流复制同步级别PostgreSQL通过配置synchronous_commit (enum)参数来指定事务的同步级别。我们可以根据实际的业务需求,对不同的事务,设置不同的同步级别。synchronous_commit = off                # synchronization level;                                          # off, local, remote_write, or on      1    2    remote_apply:事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化,并且其redo在同步    standby(s)已apply。    on:事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化。    remote_write:事务commit或rollback时,等待其redo在primary已持久化; 其redo在同步standby(s)已调用write接口(写到 OS, 但是还没有调用持久化接口如fsync)。    local:事务commit或rollback时,等待其redo在primary已持久化;    off:事务commit或rollback时,等待其redo在primary已写入wal buffer,不需要等待其持久化;不同的事务同步级别对应的数据安全级别越高,对应的对性能影响也就越大。上述从上至下安全级别越来越低。详细的同步流复制原理见:CommitTransaction @ src/backend/access/transam/xact.c   RecordTransactionCommit @ src/backend/access/transam/xact.c      1    24、流复制配置过程PostgreSQL物理流复制大致过程为:1、PG软件安装2、postgresql.conf参数配置3、pg_hba.conf配置4、pg_basebackup方式部署备库5、配置简单6、备库与主库物理完全一致,并支持只读具体的配置步骤可以参考我之前写的这篇文章:PostgreSQL流复制4.1、异步流复制参数配置postgresql.conf :wal_level = replica # minimal, replica, or logicalmax_wal_senders = 10 wal_keep_segments = 1024 hot_standby = on    1    2pg_hba.conf :host replication postgres# max number of walsender processes# in logfile segments, 16MB each; 0 disables192.168.7.180/32 md5    1    2    3    4standby recovery.conf :recovery_target_timeline = 'latest'standby_mode = onprimary_conninfo = 'host=192.168.7.180 port=1921 user=bill password=xxx    1    2    34.2、同步流复制参数配置postgresql.conf :wal_level = replica # minimal, replica, or logicalmax_wal_senders = 10 # max number of walsender processes wal_keep_segments = 1024 # in logfile segments, 16MB each; 0 disables hot_standby = onsynchronous_commit = remote_write、on、remote_apply synchronous_standby_names = 'standby2'    1    2    3pg_hba.conf :host replication postgres 192.168.7.180/32 md5    1standby recovery.conf :recovery_target_timeline = 'latest'standby_mode = onprimary_conninfo = 'host=192.168.7.180 port=1921 user=bill password=xxx application_name=standby2'    1    2    3另外我们可以通过设置synchronous_standby_names参数来指定一个支持同步复制的后备服务器的列表,其支持格式大致为:1、synchronous_standby_names =standby_name [, ...]2、synchronous_standby_names =[FIRST] num_sync ( standby_name [, ...])3、synchronous_standby_names =ANY num_sync ( standby_name [, ...] )    1    2    34.3、主备切换流程1、关闭主库,建议使用-m fast模式关闭。2、在备库上执行pg_ctl promote命令激活备库,如果recovery.conf变成recovery.done表示 备库已切换成为主库。3、这时需要将老的主库切换成备库,在老的主库的$PGDATA目录下创建recovery.conf文 件(如果此目录下不存在recovery.conf文件,可以根据$PGHOME/recovery.conf. sample模板文件复制一个,如果此目录下存在recovery.done文件,需将recovery.done文件重命名为 recovery.conf),配置和老的从库一样,只是primary_conninfo参数中的IP换成对端IP。4、启动老的主库,这时观察主、备进程是否正常,如果正常表示主备切换成功。    1    2    3    4    5    6    7    84.4、复制槽slot因为pg在归档模式下,对于已经完成归档的wal日志会自动清理,所以提供了复制槽来避免主库在所有的备库收到 WAL 日志之前不会移除它们,并且主库也不会移除可能导致恢复冲突的行,即使备库断开也是如此。例子:创建一个复制槽:bill=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');slot_name | lsn-------------+----- node_a_slot |    1    2    3    4bill=# SELECT slot_name, slot_type, active FROM pg_replication_slots; slot_name | slot_type | active-------------+-----------+--------node_a_slot | physical | f(1 row)    1    2    3    4要配置后备机使用这个槽,在备库的recovery.conf中应该配置 primary_slot_name,例如:standby_mode = 'on'primary_conninfo = 'host=192.168.7.180 port=1921 user=bill password='xxx' primary_slot_name = 'node_a_slot'    1    25、pg12流复制新特性PostgreSQL12中流复制有了一些改变:把recovery.conf的内容全部移入postgresql.conf,配置恢复、archive based standby、stream based standby,都在postgresql.conf中。postgresql.conf以及对应的两个signal文件来表示进入recovery 模式或standby模式。在这里插入图片描述例子:1、典型恢复模式配置postgresql.conf:# stream恢复模式配置  #primary_conninfo = ''  或  # archvie恢复模式配置  #restore_command = ''  hot_standby = on   # 配置是否跨时间线  #recovery_target_timeline = 'latest'  # 配置恢复目标,例如  # 立即(达到一致性即停止恢复)、时间、XID、restore point name, LSN.  #recovery_target = ''           # 'immediate' to end recovery as soon as a                                  # consistent state is reached                                  # (change requires restart)  #recovery_target_name = ''      # the named restore point to which recovery will proceed                                  # (change requires restart)  #recovery_target_time = ''      # the time stamp up to which recovery will proceed                                  # (change requires restart)  #recovery_target_xid = ''       # the transaction ID up to which recovery will proceed                                  # (change requires restart)  #recovery_target_lsn = ''       # the WAL LSN up to which recovery will proceed                                  # (change requires restart)  #recovery_target_inclusive = on # Specifies whether to stop:                                  # just after the specified recovery target (on)                                  # just before the recovery target (off)                                  # (change requires restart)  # 恢复目标到达后,暂停恢复、激活、停库  #recovery_target_action = 'pause'  # 'pause', 'promote', 'shutdown'                                     # (change requires restart)      1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25    26    27    28    29    30    31    32在 $PGDATA目录中,touch recovery.signal2、典型standby模式配置postgresql.conf:# stream恢复模式配置  #primary_conninfo = ''  或   # archvie恢复模式配置  #restore_command = ''  hot_standby = on   # 配置是否跨时间线  #recovery_target_timeline = 'latest'      1    2    3    4    5    6    7    8    9    10在 $PGDATA目录中,touch standby.signal3、如果standby.signal , recovery.signal两个文件都配置了,则优先为standby modePostgreSQL12中流复制的搭建详见我之前的这篇文章:PostgreSQL12同步流复制搭建及主备切换参考链接:https://www.postgresql.org/docs/12/release-12.htmlhttps://www.postgresql.org/docs/12/high-availability.html————————————————版权声明:本文为CSDN博主「foucus、」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_39540651/article/details/106122610
  • [技术干货] PostgreSQL中in、exists、= any 的区别
    判断对象是否存在于某个子查询的3种写法在数据库中中,我们可以使用in、exists语句来判断对象是否存在某个子查询中。在PostgreSQL里,除了支持上述两种语法外,还支持= any的语法。例如下面这3个SQL返回的结果都是一样的select * from tbl where id in (select id from t); select * from tbl where exists (select 1 from t where t.id=tbl.id); select * from tbl where id = any (array( select id from t )); 性能分析in 操作: in 操作会在查询时遍历指定的范围,检查目标值是否在给定的范围内。如果范围内存在匹配的值,则结果为真。in 操作在以下情况下性能较好:范围较小:当给定的范围较小时,in 操作的性能较好,因为数据库可以快速地查找匹配的值。索引可用:如果查询涉及的自建索引包含目标范围,则 in 操作的性能会显著提高。exists 操作: exists 操作用于检查表中是否存在满足条件的记录。它会在查询时遍历表中的所有记录,判断给定条件是否满足。exists 操作在以下情况下性能较好:条件较简单:当查询条件较简单,如仅涉及单一列时,exists 操作的性能较好。数据量较小:数据量较小的情况下,exists 操作的性能优于 in 操作,因为 exists 操作不需要遍历范围,只需判断条件是否满足。= any 操作: = any 操作用于检查目标值是否存在于给定的值列表中。它会在查询时使用 UNION ALL 操作将给定的值列表与表中的数据进行比较。= any 操作在以下情况下性能较好:值列表较小:当给定的值列表较小时,= any 操作的性能较好,因为数据库可以快速地查找匹配的值。索引可用:如果查询涉及的自建索引包含目标值,则 = any 操作的性能会显著提高。 总结: 在实际应用中,in、exists 和 = any 的性能取决于数据量、范围和查询条件。在评估性能时,请根据具体场景和实际需求选择合适的操作。如果数据量较小且条件简单,可以考虑使用 exists 操作;如果需要使用索引,则 in 和 = any 操作可能具有更好的性能。在实际应用中,建议根据具体需求进行性能测试和优化。总结不同的写法,性能还是有一定的差距。可能对in、exists这两种写法比较熟悉的人都知道这么一个结论:IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。具体的说:= any这种写法,不会走subplan,因此不涉及hash table的问题。和work_mem设置大小无关。因此在pg中十分推荐使用这种写法。exists,由于优化器会默认它只需要搜索到1条命中目标就不搜了,所以优化器评估是否使用hash table时,需要的内存相对较少,即使较小的work_mem也可能使用hashtable。in ,当出现在subquery中时,优化器评估这个subquery是否要构建哈希TABLE,直接和subquery的大小相关,所以需要较大的work_mem才会选择使用hashtable。
  • [技术干货] 存算分离架构
    典型分布式数据库系统架构数据库系统架构典型的架构有Shared Everything、Shared Memory、Shared Disk和Shared Nothing。这里Share的资源主要是指内存,磁盘。单机数据库系统称为shared everything,因为是单机节点,有自己独立的内存空间和独立的磁盘Shared Memory 现实中并不常见,多个cpu通过网络来共享一个内存地址空间,并且共享同一个disk。Shared Disk 多个数据库实例,每个实例有自己的内存,但是通过网络共享同一个disk。Shared Disk架构在云原生比较常见,这样的好处就是利于存算分离,计算层和存储层能够解耦,因此计算层和存储层在扩缩容的时候彼此不影响。缺点是因为共享disk,因此对于高并发的写请求势必性能会比较差,因此这种架构比较适合OLAP这种读多写少的数仓。Shared Nothing 各个数据库实例,都有自己的内存和disk,这些实例之间只能通过网络通信来感知各个实例的状态变化。通常表会水平划分到各个节点,每个节点只负责其本地磁盘上的数据行。 这种设计非常适合星型模式查询,因为连接维度比较小的维度表和宽的事实表所需的带宽非常少。Share Nothing架构的优点在于性能和效率比较高,缺点在于灵活性较差,因为扩缩容的时候,需要在节点之间移动数据。而且对于事物的支持性较差,因为各个节点之间必须通过网络进行协调。什么是存算分离存算分离是一种数据处理技术,它将数据存储和数据处理(计算)分开,使得存储和计算可以独立地进行优化和扩展。在这种架构下,存储系统负责存储和管理数据,而计算系统负责对数据进行处理和分析。这种技术旨在提高数据处理的效率、降低成本并满足大规模数据存储和分析的需求。为什么需要存算分离异构的工作负载: 得益于现在云原生的环境,用户可以自由配置每台云服务器的cpu型号,内存,磁盘,带宽。但是存在的问题是适合高 I/O 带宽、轻计算的系统配置不适合复杂查询,而适合复杂查询的系统配置却不能满足高吞吐的要求。简单的理解为需要在计算和IO之间做平衡。扩缩容: 由于计算和存储的耦合,当扩缩容的时候势必需要在节点之间移动数据,而节点同时需要对外提供计算服务,因此此时的性能可能会收到很大影响。如果存储分离,那么计算层和存储层可以独立增加减少节点而互不干扰。存算分离的优点存算分离的主要优点如下:高效性:存储和计算可以独立进行优化,使得存储系统可以专注于提高数据的存储和访问效率,计算系统可以专注于优化数据处理算法和提高计算性能。可扩展性:存算分离架构允许存储和计算资源独立扩展,方便应对不断增长的数据存储和处理需求。成本降低:通过将存储和计算分开,可以更加灵活地选择适合各自任务的硬件和软件,从而降低整体成本。灵活性:存算分离使得数据处理任务可以根据实际需求进行调整,如快速切换计算引擎、修改查询算法等。容错性:存算分离架构可以提高系统的容错性,当某个部件出现问题时,其他部分可以继续正常工作。易于维护:存算分离使得系统组件的维护和升级更加容易进行,无需对整个系统进行改造。 在实际应用中,存算分离技术已在大数据、云计算、人工智能等领域得到广泛应用。例如,海量数据公司自主研发的 Vastbase 海量数据库,就致力于提高数据库的存算分离能力,并向 openGauss 社区持续贡献代码。
  • [技术干货] 向量数据库简介
    向量数据库定义向量数据库(Vector Database)是一种专门用来处理向量嵌入(Vector Embedding)的数据库。它通过比较向量值并找到彼此相似的值来索引和检索数据,适用于处理复杂数据类型,如文档、图像、视频和网页等非结构化数据。向量数据库与传统数据库的主要区别在于数据存储方式、数据类型和应用场景。向量数据库与传统数据库的主要区别如下:数据存储方式:传统数据库通常采用表格形式存储结构化数据,如客户信息、订单记录等。向量数据库则将数据存储为高维空间中的点,通过向量表示数据,便于计算和比较相似度。数据类型:传统数据库主要处理结构化数据,如关系型数据。向量数据库可以处理非结构化数据,如文本、图像、音频等,并将这些数据转化为向量形式进行存储和检索。应用场景:传统数据库适用于处理关系型数据和结构化数据,如查询、统计和分析等任务。向量数据库适用于人工智能和机器学习领域,如相似度搜索、推荐系统、图像识别等任务。 向量数据库在处理非结构化数据和复杂场景方面具有优势,能够更快地找到相似的数据点,从而在搜索引擎、内容推荐、图像识别等领域发挥作用。随着大数据和人工智能技术的发展,向量数据库在未来有望得到更广泛的应用。常见的向量数据库向量数据库近年来逐渐受到关注,一些知名的向量数据库包括:Pinecone:这是一家成立于 2021 年的初创公司,专注于向量数据库领域。Pinecone 可以为各种应用提供高性能的向量搜索,包括计算机视觉、自然语言处理、音频识别等。该公司已成为 OpenAI 的合作伙伴,并获得了 7.5 亿美元的估值。Zilliz:作为一家中国初创公司,Zilliz 致力于提供高性能的向量数据库解决方案。其产品 Metis 向量数据库适用于各种场景,如广告推荐、内容审核、图像识别等。Zilliz 已完成数亿元人民币的融资。Chroma:Chroma 是一家成立于 2021 年的美国初创公司,专注于构建向量数据库。其产品可以处理大规模的图像、视频和文本数据,并应用于相似度搜索、推荐系统和内容审核等领域。Milvus:Milvus 是一款开源的向量数据库,由北京智臻科技有限公司开发。它可以处理多种非结构化数据类型,如文本、图像和音频等,并支持高效的向量搜索。Milvus 已在多个行业领域得到广泛应用,如智能客服、短视频推荐等。Faiss:Faiss(Facebook AI Similarity Search)是 Facebook 人工智能研究院开发的一款向量数据库。它旨在为大规模图像、文本和视频数据提供高效的相似度搜索功能。Faiss 已经开源,可供研究人员和开发者免费使用。 这些向量数据库在处理复杂数据类型和相似度搜索方面具有优势,随着人工智能技术的发展,它们在各个领域的应用将进一步拓宽。
总条数:514 到第
上滑加载中