redis集群理论和搭建

目录

环境

一,安装和部署redis

1,安装

 2,部署

​编辑 3,允许非本机连接redis

二、主从模式

主从模式搭建:

三,哨兵模式

哨兵模式搭建

四,集群模式 

架构细节:

心跳机制

集群模式搭建:

jedis连接集群 

最后


环境


centOS7

redis5

vm12


一,安装和部署redis

1,安装

Redis是C语言开发的,安装redis需要先去官网下载源码进行编译,编译需要依赖于GCC编译环境,如果 CentOS上没有安装gcc编译环境,需要提前安装,安装命令如下:(这里我们使用root用户处理这些操 作)

键入命令:

yum install gcc

有一个redis压缩包在/usr/myapps下,至于redis的压缩包是如何放入centos的,我在此就不做演示了(我是使用Mobaterm连接虚拟机,然后传输文件到虚拟机中),然后解压该压缩包

键入命令:

tar -zxvf redis-5.0.5.tar.gz

4f19c5d1820947559c92c17254c8e0dc.png

 解压后,解压完的文件名为redis.5.0.5,然后我们进入到此文件下,键入如下命令进行安装:

make PREFIX=/usr/myapps/redis install    //这里指定了redis的安装目录为/usr/myapps/redis

efa2a0ae2dd841959dee84fce33a94c8.png

 安装完之后,我们还需要将redis的配置文件复制过来,因为在/usr/myapps/redis中,只有bin/文件夹,而没有配置文件,配置文件在redis-5.0.5中,将其复制过来就可以了。

19faadd5f56a4692876da1de4796c203.png

 2,部署

redis的启动方式有两种,一种是前端启动,还有一种是端启动,如下:

启动的时候注意要关闭防火墙

systemctl disable firewalld //永久关闭
systemctl stop firewalld //临时关闭
systemctl status firewalld //查看防火墙状态

2.1,前端启动(用的很少)

在/usr/myapps/redis下键入命令:

./bin/redis-server

6dac8b1740e24e38aa95b9e42170e237.png 前端启动之后,,不能再进行其他操 作,如果要操作必须使用ctrl+c,同时redis-server程序结束,不推荐此方法。

2.2,后端启动

在后端启动之前,我们首先要修改redis.conf文件来允许后端启动,在redis.conf的文件中的如下位置修改daemonize,将no改为yes,然后保存退出。

9c621fcdc1344545b81d61e9bcf41195.png

 之后,键入命令后端启动redis

 ./bin/redis-server ./redis.conf

键入命令连接redis

 ./bin/redis-cli -h 127.0.0.1 -p 6379    //默认是127.0.0.1和6379,所以也可以不用写

a326138f99884fc88cbdc11efc4a6606.png

键入一下命令关闭redis连接:

./bin/redis-cli shutdown

 当然其实也可以使用直接杀死redis进程的方式来结束redis连接。

bdc2e6754baf428382f9b5ee52ceb100.png 3,允许非本机连接redis

同样我们还是需要来修改redis.conf文件,在redis.conf找到如下地方,修改bind后面的ip,改成允许连接的ip,

7632a7b46fca4b4b9c388b247e277b3d.png

修改完之后,可以看到我们可以使用192.168.58.100来连接redis,可以看到连接成功。

650ec2dc8b504089b495325b21a9e0bd.png 我们也可以使用redis的图形化工具在windows上链接contos上的redis服务器。如下:

abea1aa9fb744ce09e643c3b567e3658.png

我们在redis客户端上存入数据

a17b67d12abe4c9baea9cf336b797477.png 可以看到在redis的图形化工具上也存在我们存入的值。

61175b13956a4ee5984bbb990fcdf2ce.png

系统中只有一台redis服务器是不可靠的,容易出现单点故障。为了避免单点故障,可以使用多台redis服务器组成redis集群。redis支持三种集群模式。

二、主从模式


至少需要两台redis服务器,一台主节点(master)、一台从节点(slave),组成主从模式的Redis集群。通常来说,master主要负责写,slave主要负责读,主从模式实现了读写分离。

cc5867fde030403f8b5abe4afbedc0af.png

集群中有多台redis节点,就必须保证每个节点中的数据是一致的。redis中,为了保持数据一致性,数据总是从master复制到slave,这就是redis的主从复制。

主从复制的作用:

数据冗余:实现了数据的热备份,是持久化之外的另一种数据冗余方式
故障恢复:master故障时,slave可以提供服务,实现故障快速恢复
负载均衡:master负责写,slave负责读。在写少读多的场景下可以极大提高redis吞吐量
高可用基石:主从复制是redis哨兵模式和集群模式的基础。
主从复制实现原理:

主从复制过程主要可以分为3个阶段:连接建立阶段、数据同步阶段、命令传播阶段。

连接建立阶段:在主从节点之间建立连接,为数据同步做准备。
数据同步阶段:执行数据的全量(或增量)复制(复制RDB文件)
命令传播阶段:主节点将已执行的命令发送给从节点,从节点接收命令并执行,从而实现主从节点的数据一致性
主从模式中,一个主节点可以有多个从节点。为了减少主从复制对主节点的性能影响,一个从节点可以作为另外一个从节点的主节点进行主从复制。

不足之处:主节点宕机之后,需要手动拉起从节点来提供业务,不能达到高可用。

主从模式搭建:

我们将上述使用的redis复制一份到/usr/myapps/redis1中

f77e0ce9b1704b15a0c6aaef9239a629.png

清除redis1中的持久化文件,键入命令:

rm -rf dump.rdb

然后修改redis1的配置文件redis.conf,在文件中找到如下内容的位置,然后修改 replicaof <masterip> <masterport>,改为主服务器的ip和端口号。

f94c0c7d0c1d4c06a6e768d1e76ac848.png

然后再修改从机的监听端口为6380

b6cccb44b8f8496db415c9036f2cc01a.png

之后保存并且退出,然后启动从机。连接从机之后,输入info命令

144095cec15948179df1a398f0d4eb55.png

我们可以在info命令下看到此从机的信息。 

08585fd813b44874b414a3612f78e441.png

在从机中可以取到主机中的数据,但是自己不可以上传数据。

e7f034f9e520449f966de55a81a2700e.png

主从模式的搭建就到这里。

三,哨兵模式

Redis Sentinel是Redis的高可用实现方案,它可以实现对redis的监控、通知和自动故障转移,当redis master挂掉之后,可以自动拉起slave提供业务,从而实现redis的高可用。为了避免Sentinel本身出现单点故障,Sentinel自己也可采用集群模式。

1801b7a9e1a74633baa7d03bf90fdffc.png

 哨兵模式的原理

Sentinel是一种特殊的redis节点,每个sentinel节点会维护与其他redis节点(包括master/slave/sentinel)的心跳。

当一个sentinel节点与master节点的心跳丢失时,这个sentinel节点就会认为master节点出现了故障,处于不可用的状态,这种判定叫作主观下线(即sentinel节点自己主观认为master下线了)

之后,这个sentinel节点会与其他sentinel节点交换信息,如果发现认为主节点发生故障的sentinel节点的个数超过了某个阈值(通常为sentinel节点总数的1/2+1,即超过半数),则sentinel会认为master节点已经处于客观下线的状态,即大家都认为master故障不可用了。

之后,sentinel节点中会选举处一个sentinel leader来执行redis主节点的故障转移。

被选举出的 Sentinel 领导者进行故障转移的具体步骤如下:

(1)在从节点列表中选出一个节点作为新的主节点

过滤不健康或者不满足要求的节点;

选择 slave-priority(优先级)最高的从节点, 如果存在则返回, 不存在则继续;

选择复制偏移量最大的从节点 , 如果存在则返回, 不存在则继续;

选择 runid 最小的从节点。

(2)Sentinel 领导者节点会对选出来的从节点执行 slaveof no one 命令让其成为主节点。

(3)Sentinel 领导者节点会向剩余的从节点发送命令,让他们从新的主节点上复制数据。

(4)Sentinel 领导者会将原来的主节点更新为从节点, 并对其进行监控, 当其恢复后命令它去复制新的主节点。

哨兵模式搭建

首先我们将上述的redis1复制一份改为redis2,在redis.conf中修改port为6381,所以这个时候,主机redis拥有两个从机一个是redis1,还有一个是redis2,如下图:连接上redis之后输入info,可以看到两个从机的信息,说明主从运行正常。

33fcafc97f134c5091501d2c3f25a515.png

后面我们在redis2上安装一个哨兵。首先在redis2的bin下创建一个sentinel.conf文件,文件内容含义如下:

启动哨兵进程,首先需要创建哨兵配置文件vi sentinel.conf,可从源码配置redis5.0.5/sentinel.conf中复制内容,也可以直接自定义该文件到bin目录下 在配置中输入:sentinel monitor mastername 内网IP(127.0.0.1) 6379 1 说明: mastername 监控主数据的名称,自定义 127.0.0.1:监控主数据库的IP; 6379:端口 1:最低通过票数

3149f4d8cd7b4d9f9f9128e9a89942f2.png

 只有画红线的这一行是我们写的其他是我运行之后redis自行生成的。

9cd57912a5c741bcac7899f15203338a.png

之后我们创建一个日志文件,用于记录哨兵的日志

 ./redis-sentinel ./sentinel.conf >sent.log &

78ccab223de249dfafd1ec91a3493819.png 之后启动哨兵,键入命令:

./redis-server sentinel.conf --sentinel

查看进程可以看到我们的哨兵进程

4596c0bfbe1643a9ab494b8ff29cd66d.png

启动之后,我们将我们的redis主机宕机,之后我们查看redis1,我们发现 redis2成为主机了(redis1也可以,是随机的)

ab4a37de4286480597664c61e789a35f.png

0ec803dc49504668b61f9dca20104a6e.png 我们重新启动redis以后,查看信息,发现,redis2依然是主机,所以主机不可抢夺。

698eb76aeb5147bf830efb94acbf1a78.png

四,集群模式 


主从模式实现了数据的热备份,哨兵模式实现了redis的高可用。但是有一个问题,这两种模式都没有解决,这两种模式都只能有一个master节点负责写操作,在高并发的写操作场景,master节点就会成为性能瓶颈。

redis的集群模式中可以实现多个节点同时提供写操作,redis集群模式采用无中心结构,每个节点都保存数据,节点之间互相连接从而知道整个集群状态。

如图所示集群模式其实就是多个主从复制的结构组合起来的,每一个主从复制结构可以看成一个节点,那么上面的Cluster集群中就有三个节点。

193b20f59cfe4e67a6eab837ddb9fd70.png

架构细节:

(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

(2)节点的fail是通过集群中超过半数的节点检测有效时整个集群才生效.

(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可 用节点即可

(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护nodeslotvalue Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0- 16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点  

9dacda2eccd44a53baae9910f8ae9496.png

心跳机制

(1)集群中所有master参与投票,如果半数以上master节点与其中一个master节点通信超过(clusternode-timeout),认为该master节点挂掉.

(2):什么时候整个集群不可用(cluster_state:fail)?

 如果集群任意master挂掉,且当前master没有slave,则集群进入fail状态。也可以理解成集群的[0- 16383]slot映射不完全时进入fail状态。 

如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态。

集群模式搭建:

1,创建集群目录:

mkdir redis-cluster

78130340521d45cbacbf6e1b86c31d9a.png

 2,创建集群节点

搭建集群最少也得需要3台主机,如果每台主机再配置一台从机的话,则最少需要6台机器。 设计端口如 下:创建6个redis实例,需要端口号7001~7006

9b38e9ea22fc468399be42bd011859f7.png

3,删除持久化文件

就是删除以rdb或者aof结尾的文件,因为我这里的redis是新装的所以没有持久化文件。

4,修改redis.conf配置文件,打开Cluster-enable yes 

54bc13968ae04e61a0711bc895bed888.png

5,修改端口,修改后端启动

f61f3e01105e43a6a6affc20f31e96bd.png2ecb4b7b9d38449e9a38abeedc193e08.png 

6,复制出7002-7006机器

8d2716436fc44d14a6efd74a05f91559.png

7,修改7002-7006机器的端口

969aa6075e104d3eaa94bce32ac017a2.png 8,启动7001-7006这六台机器,写一个启动脚本:自定义shel脚本

974b61d0bb4e4d4ebfac796b18cb2a1c.png

cd 7001
./bin/redis-server ./redis.conf
cd ..
cd 7002
./bin/redis-server ./redis.conf
cd ..
cd 7003
./bin/redis-server ./redis.conf
cd ..
cd 7004
./bin/redis-server ./redis.conf
cd ..
cd 7005
./bin/redis-server ./redis.conf
cd ..
cd 7006
./bin/redis-server ./redis.conf
cd ..

 9,修改文件权限

为拥有者加上可执行的权限·。

 chmod u+x startall.sh

10,启动所有实例

e4812473c7ee43a8968e10dbcdaf75bb.pnga4ebd9042eef4c23a8cf84a57d0b4d70.png

11,配置集群关系

随便进入一个服务器的配置文件执行如下命令

./redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 

执行完之后,由弹出的信息 可以看到redis的主从关系已经配置好了。

4f35bbfb19f240c98b61ecd78a628bbe.png

11,连接集群

注意,连接集群和普通连接不同,加上-c代表以集群的方式连接

./bin/redis-cli -h 127.0.0.1 -p 7001 -c

 可以看到,连接上7001后,我们存入7001的uname却被重定向存入到时槽10359中,而这个时槽是交由7002管理的,所以,uname实际存入了7002。

 53a5b752c13049bca92f70db73f77765.png

 由上我们就可以知道。只要连接了集群其中的一个服务器,就相当于连接了整个集群。

12,查看集群信息

1c228788822f46c58e9b0439700446f4.png

jedis连接集群 

1,创建一个maven项目大家都会吧,这里就不演示了。

2,导入jedis依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>

3,创建类

package jedis;
/**@Author:天动万象*@Date:2023/10/27*@Description:*@VERSION: 1.8*/import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;public class cluster {public static void main(String[] args) throws IOException {
// 创建一连接,JedisCluster对象,在系统中是单例存在Set<HostAndPort> nodes = new HashSet<HostAndPort>();nodes.add(new HostAndPort("192.168.197.132", 7001));nodes.add(new HostAndPort("192.168.197.132", 7002));nodes.add(new HostAndPort("192.168.197.132", 7003));nodes.add(new HostAndPort("192.168.197.132", 7004));nodes.add(new HostAndPort("192.168.197.132", 7005));nodes.add(new HostAndPort("192.168.197.132", 7006));JedisCluster cluster = new JedisCluster(nodes);// 执行JedisCluster对象中的方法,方法和redis指令一一对应。cluster.set("test1", "test111");String result = cluster.get("test1");System.out.println(result);
//存储List数据到列表中cluster.lpush("site-list", "java");cluster.lpush("site-list", "c");cluster.lpush("site-list", "mysql");
// 获取存储的数据并输出List<String> list = cluster.lrange("site-list", 0 ,2);for(int i=0; i<list.size(); i++) {System.out.println("列表项为: "+list.get(i));}
// 程序结束时需要关闭JedisCluster对象cluster.close();System.out.println("集群测试成功!");}
}

注意:使用非本机连接前,先要修改集群中的每个服务器的redis.conf文件,至于修改什么,我在这篇博客中已经讲过,所以不再赘述。

最后

本篇博客对redis集群理论和搭建就到这里了,如果本篇博客对你有帮助的话请点赞收藏支持一下,谢谢!答应我!不要白嫖好吗?哈哈哈!咱们下篇博客见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/121035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

<多线程章节五>synchrosized的可重入特性

&#x1f490;专栏导读 本篇文章收录于多线程&#xff0c;也欢迎翻阅博主的其他文章&#xff0c;可能也会让你有不一样的收获&#x1f604; &#x1f341;JavaSE &#x1f33a;多线程 &#x1f342;数据结构 &#x1f490;synchrosized的可重入特性及死锁 可重入特性就是&…

力扣每日一题79:单词搜索

题目描述&#xff1a; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格…

如何查找特定基因集合免疫基因集 炎症基因集

温故而知新&#xff0c;再次看下Msigdb数据库。它更新了很多内容。给我们提供了一个查询基因集的地方。 关注微信&#xff1a;生信小博士 比如纤维化基因集&#xff1a; 打开网址&#xff1a;https://www.gsea-msigdb.org/gsea/msigdb/index.jsp 2.点击search 3.比如我对纤维…

结构体数组经典运用---选票系统

结构体的引入 1、概念&#xff1a;结构体和其他类型基础数据类型一样&#xff0c;例如int类型&#xff0c;char类型&#xff0c;float类型等。整型数&#xff0c;浮点型数&#xff0c;字符串是分散的数据表示&#xff0c;有时候我们需要用很多类型的数据来表示一个整体&#x…

Parity 战略转型引热议,将如何推动波卡生态去中心化?

Polkadot 生态的区块链基础设施公司 Parity Technologies&#xff0c;最近宣布了一项重要的战略调整&#xff0c;即正在寻求在未来几个月内&#xff0c;将部分现有的市场职能转移给 Polkadot 生态系统内的多个去中心化团队&#xff0c;这将影响 Parity Technologies 未来几个月…

ffmpeg中examples编译报不兼容错误解决办法

ffmpeg中examples编译报不兼容错误解决办法 参考examples下的README可知&#xff0c;编译之前需要设置 PKG_CONFIG_PATH路径。 export PKG_CONFIG_PATH/home/user/work/ffmpeg/ffmpeg/_install_uclibc/lib/pkgconfig之后执行make出现如下错误&#xff1a; 基本都是由于库的版…

(el-Table)操作(不使用 ts):Element-plus 中 Table 多选框的样式等的调整

Ⅰ、Element-plus 提供的 Table 表格组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供 Table 组件情况&#xff1a; 其一、Element-ui 自提供的 Table 代码情况为(示例的代码)&#xff1a; // Element-plus 自提供的代码&#xff1a; // 此时是使用了 ts 语言环境…

企业管理系统有哪些?

文章目录 企业管理系统一、ERP 企业资源计划&#xff08;Enterprise Resource Planning&#xff09;二、OMS 订单管理系统&#xff08;Order Management System&#xff09;三、WMS 仓库管理系统&#xff08;Warehouse Management System &#xff09;四、TMS 运输管理系统 (Tr…

第十三章---枚举类型与泛型

一&#xff0c;枚举类型 1.使用枚举类型设置常量 设置常量时&#xff0c;我们通常将常量放置在接口中&#xff0c;这样在程序中就可以直接使用。该常量稚因为在接口中定义常量时&#xff0c;该常量的修饰符为 final 与 static。 public interface Constants ( public static …

LVS集群-DR模式【部署高可用LVS-DR集群】

文章目录 2.2 实战&#xff1a;配置LVS-DR集群2.2.1 配置IP&#xff08;Director Server的部署配置&#xff09;2.2.2 生成ens33:1配置文件 &#xff08;Director Server的部署配置&#xff09;2.2.3 配置LVS-DR规则&#xff08;Director Server的部署配置&#xff09;2.2.4 两…

Docker 镜像读写层核心概念:rootfs、Union mount、image以及layser原理详解

Docker 镜像读写层核心概念&#xff1a;rootfs、Union mount、image以及layser原理详解 文章目录 Docker 镜像读写层核心概念&#xff1a;rootfs、Union mount、image以及layser原理详解rootfsUnion mount为什么镜像层都是只读的去掉读写层的话会有什么问题 Docker镜像imageDoc…

AcWing 1.2.1 最长上升子序列模型 + 动态规划 + 图解(详细)

&#xff08;1&#xff09;acwing 4557. 最长上升子序列 4557. 最长上升子序列 - AcWing题库 给定一个长度为 N 的整数序列 a1,a2,…,aN。请你计算该序列的最长上升子序列的长度。上升子序列是指数值严格单调递增的子序列 输入格式 第一行包含整数 N第二行包含 N个整数 a1,a…

大语言模型(LLM)综述(四):如何适应预训练后的大语言模型

A Survey of Large Language Models 前言5. ADAPTATION OF LLMS5.1 指导调优5.1.1 格式化实例构建5.1.2 指导调优策略5.1.3 指导调优的效果5.1.4 指导调优的实证分析 5.2 对齐调优5.2.1 Alignment的背景和标准5.2.2 收集人类反馈5.2.3 根据人类反馈进行强化学习5.2.4 无需 RLHF…

一个比较特别的串口工具

这是08年写的一个 并网带电池逆变器 的通讯工具&#xff0c;和普通的串口调试器相比&#xff0c;多了一个【脚本】功能。能够通过【脚本】完成通讯测试。 PC发给DSP的01命令 01 10 1B 00 CF A3 00 00 90 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 电…

小程序开发——小程序的事件

1.事件对象 事件与事件对象概述 事件是一种用户行为&#xff0c;用户的点击、滑动等操作都可以成为事件。事件也是一种通信方式&#xff0c;能够完成视图层&#xff08;WXML页面文件&#xff09;与逻辑层&#xff08;JS逻辑文件&#xff09;之间的通信。 事件对象是指用户在点…

uniapp开发app,在ios真机上出现的css样式问题

比如下面的问题&#xff0c;在iphone 13上出现&#xff0c;在iphone xR上正常。 问题一&#xff1a;border:1rpx造成边框显示不全 在iphone13上border边框有一部分不显示&#xff1a; 在iphone xR上显示正常&#xff1a; 解决办法是&#xff1a; 将border边框设置中的1rpx改…

分享一款基于 AI 的 Chrome 插件

最近使用大模型比较多&#xff0c;公司虽然提供了免费的 ChatGPT 但是需要跳转特定页面才能访问&#xff0c;比较麻烦&#xff0c;于是就想到是否可以开发一款类似于有道词典一样的 Chrome 插件&#xff0c;可以在任意页面使用&#xff0c;虽然市面上也有类似的插件&#xff0c…

【ROS入门】机器人系统仿真——URDF集成Gazebo

文章结构 URDF与Gazebo基本集成流程创建功能包编写URDF或Xacro文件启动 Gazebo 并显示机器人模型 URDF集成Gazebo相关设置collisioninertial颜色设置 URDF集成Gazebo实操编写封装惯性矩阵算法的 xacro 文件复制相关 xacro 文件&#xff0c;并设置 collision inertial 以及 colo…

一文搞懂 MineCraft 服务器启动操作和常见问题 2023年10月

文章目录 前言1. 新建文件夹2. 创建 bat 文件3. 编辑 bat 文件4. 启动服务器5. 恭喜完成 文章持续更新中&#xff0c;如果你有问题可以通过 qq 1317699264 获取免费协助&#xff0c;解决的问题将会被更新到本文章中 前言 无论你是使用服务端整合包&#xff0c;还是从上一篇我的…

【开源】基于SpringBoot的天然气工程业务管理系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四、数据库设计4.1 用户表4.2 分公司表4.3 角色表4.4 数据字典表4.5 工程项目表4.6 使用材料表4.7 使用材料领用表4.8 整体E-R图 五、系统展示六、核心代码6.1 查询工程项目6.2 工程物资…