dream

一个菜鸟程序员的成长历程

0%

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

MCP

在AI技术大火的当下,各个公司都在搞AI-Agent技术。对于Agent来说,其实还处于一个早期的阶段,没有所谓的最佳实践。更没有一些标准规则。可谓是一片蓝海。

大厂们也都在做着自己的一些尝试。但是总有人在建设底层规则。

MCP就是一个标准化的产物。它定义了一些标准

Model Context Protocol(MCP)是由Anthropic于2024年11月推出的开放标准,旨在为大型语言模型(LLM)应用提供统一的上下文交互接口,使其能够与外部数据源和工具进行无缝集成。​MCP采用客户端-主机-服务器架构,基于JSON-RPC 2.0协议,支持有状态连接和功能协商,允许AI模型访问文件、执行函数和处理上下文提示。

截至2025年4月,MCP已被多个AI平台和开发工具采纳,包括OpenAI、Google DeepMind、Replit、Sourcegraph等。​其应用场景涵盖了软件开发、企业助手、自然语言数据访问等领域。

MCP服务器开发

许多大模型目前没有能力获取预报和恶劣天气警报。让我们使用MCP 来解决这个问题!

我们将构建一个公开两个工具的服务器:get-alertsget-forecast。然后我们将服务器连接到 MCP 主机(在本例中,Claude for Desktop):

MCP本身提供了一些语言的SDK,包括:

  • Python
  • Node.js
  • Java
  • C#
  • Kotlin

以Java为例,要求如下:

  • Java 17 或更高版本
  • Spring Boot 3.3.x或更高版本

使用SDK可以大大简化我们的开发过程,比如JAVA里面的,我们只需要一个注解@Tool就可以搞定了。

需要添加Maven依赖:

  • spring-web:web开发依赖
  • spring-ai-starter-mcp-server:MCP服务器开发依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <dependencies>
    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    </dependency>
    </dependencies>

我们增加一个Service类,该类有两个方法,这两个方法提供了我们上面说的两个工具的能力。

  • getWeatherForecastByLocation: 提供给Agent或者大模型的工具,输入参数是精度和纬度,根据经纬度获取天气预报。
  • getAlerts:提供给Agent或者大模型的工具,输入参数是地区,获取该地区的天气警报。
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@Service
public class WeatherService {
private final RestClient restClient;

public WeatherService() {
this.restClient = RestClient.builder()
.baseUrl("https://api.weather.gov")
.defaultHeader("Accept", "application/geo+json")
.defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)")
.build();
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Points(@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props(@JsonProperty("forecast") String forecast) {
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Forecast(@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props(@JsonProperty("periods") List<Period> periods) {
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Period(@JsonProperty("number") Integer number, @JsonProperty("name") String name,
@JsonProperty("startTime") String startTime, @JsonProperty("endTime") String endTime,
@JsonProperty("isDaytime") Boolean isDayTime, @JsonProperty("temperature") Integer temperature,
@JsonProperty("temperatureUnit") String temperatureUnit,
@JsonProperty("temperatureTrend") String temperatureTrend,
@JsonProperty("probabilityOfPrecipitation") Map probabilityOfPrecipitation,
@JsonProperty("windSpeed") String windSpeed, @JsonProperty("windDirection") String windDirection,
@JsonProperty("icon") String icon, @JsonProperty("shortForecast") String shortForecast,
@JsonProperty("detailedForecast") String detailedForecast) {
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Alert(@JsonProperty("features") List<Feature> features) {

@JsonIgnoreProperties(ignoreUnknown = true)
public record Feature(@JsonProperty("properties") Properties properties) {
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Properties(@JsonProperty("event") String event, @JsonProperty("areaDesc") String areaDesc,
@JsonProperty("severity") String severity, @JsonProperty("description") String description,
@JsonProperty("instruction") String instruction) {
}
}

@Tool(description = "Get weather forecast for a specific latitude/longitude")
public String getWeatherForecastByLocation(
double latitude, // Latitude coordinate
double longitude // Longitude coordinate
) {
// 使用 RestClient 发起 GET 请求,获取指定经纬度的点信息
var points = restClient.get()
.uri("/points/{latitude},{longitude}", latitude, longitude) // 构建请求 URI,替换占位符为具体的经纬度
.retrieve() // 执行请求并获取响应
.body(Points.class); // 将响应体解析为 Points 类型的对象

// 使用 RestClient 发起 GET 请求,获取点信息中的天气预报 URL 对应的天气预报数据
var forecast = restClient.get()
.uri(points.properties().forecast()) // 从点信息中提取天气预报 URL
.retrieve() // 执行请求并获取响应
.body(Forecast.class); // 将响应体解析为 Forecast 类型的对象

// 遍历天气预报的各个时间段,生成格式化的天气预报文本
String forecastText = forecast.properties().periods().stream().map(p -> {
return String.format("""
%s:
Temperature: %s %s
Wind: %s %s
Forecast: %s
""",
p.name(), // 时间段名称
p.temperature(), // 温度值
p.temperatureUnit(), // 温度单位
p.windSpeed(), // 风速
p.windDirection(), // 风向
p.detailedForecast()); // 详细天气预报
}).collect(Collectors.joining()); // 将所有时间段的天气预报文本拼接为一个字符串

// 返回生成的天气预报文本
return forecastText;
}

@Tool(description = "Get weather alerts for a US state")
public String getAlerts(
@ToolParam(description = "Two-letter US state code (e.g. CA, NY") String state
) {
// 使用 RestClient 发起 GET 请求,获取指定州的天气警报信息
Alert alert = restClient.get().uri("/alerts/active/area/{state}", state).retrieve().body(Alert.class);

// 遍历警报信息的特性列表,将每个特性格式化为字符串并拼接为最终的警报信息文本
return alert.features()
.stream()
.map(f -> String.format("""
Event: %s
Area: %s
Severity: %s
Description: %s
Instructions: %s
""",
// 获取警报事件名称
f.properties().event(),
// 获取警报影响区域描述
f.properties.areaDesc(),
// 获取警报严重程度
f.properties.severity(),
// 获取警报详细描述
f.properties.description(),
// 获取警报的操作指令
f.properties.instruction()))
// 将所有警报信息拼接为一个字符串,以换行符分隔
.collect(Collectors.joining("\n"));
}
}

再通过MCP提供的MethodToolCallbackProvider 工具类来将 @Tool 标记的方法转换成统一的方法,对外部提供工具。

这是因为MCP服务器规定要给外部提供统一的接口、返回值、参数。所以要转换成符合MCP规范的接口。

1
2
3
4
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}

接下来构建服务器,运行成功以后,会在target目录下生成一个jar包,运行该jar包应该能成功启动:

1
./mvnw clean install

接下来我们无需启动这个jar包,直接使用Claude 桌面版客户端即可。

可以在这里下载:claude 桌面版下载

下载以后,打开应用,登陆上去,选择 Claude -》 Settings

settings

选择Developer -〉Edit Config。会自动创建一个MCP的配置文件。如果已经存在了则不会创建。

Config

创建的配置文件名称如下:

1
claude_desktop_config.json

修改这个配置文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
{
"mcpServers": {
"spring-ai-mcp-weather": { // MCP服务器名称,可以随便输入
"command": "java", // 使用java命令启动MCP服务器
"args": [ // java后面跟的参数
"-Dspring.ai.mcp.server.stdio=true",
"-jar",
"/mcpServer/target/mcpServer-0.0.1-SNAPSHOT.jar" //jar包的绝对路径地址
]
}
}
}

接下来重新启动Claude桌面版即可。在启动Claude的时候,Claude会根据上述配置文件来启动MCP服务器。启动命令类似:

1
java -Dspring.ai.mcp.server.stdio=true -jar /mcpServer/target/mcpServer-0.0.1-SNAPSHOT.jar

如果通过jps命令,可以看到jar包启动成功。

1
jps -l

在Claude桌面版上可以看到我们刚刚配置的MCP服务器名称和提供的工具。

mcp server

mcp server tool

可以自主启用和禁用工具。

接下来我们和Claude大模型交流的时候,大模型会自己判断需不需要调用工具,调用哪个工具,如何调用。当调用的时候,会让你进行确认。

来试试输入:

1
纽约的天气怎么样?

mcp server tool

可以看到大模型自己调用了我们的工具,并展示了输入输出。

mcp server tool

看到这里我们可以得出MCP+大模型的运行流程图。

mcp server tool

日志

如果需要查看日志信息,可以到如下目录:~/Library/Logs/Claude

有多个文件:

  • mcp.log : 包含了MCP所有的日志
  • mcp-server-xxxxx.log: 一个MCP服务一个日志文件,记录了这个服务相关的所有日志

总结

我们主要介绍了MCP的概念,MCP的通信协议、通信方式、生命周期。MCP到底是什么东西,实现了哪些内容,使用场景以及为什么要使用MCP。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

MCP

在AI技术大火的当下,各个公司都在搞AI-Agent技术。对于Agent来说,其实还处于一个早期的阶段,没有所谓的最佳实践。更没有一些标准规则。可谓是一片蓝海。

大厂们也都在做着自己的一些尝试。但是总有人在建设底层规则。

MCP就是一个标准化的产物。它定义了一些标准

Model Context Protocol(MCP)是由Anthropic于2024年11月推出的开放标准,旨在为大型语言模型(LLM)应用提供统一的上下文交互接口,使其能够与外部数据源和工具进行无缝集成。​MCP采用客户端-主机-服务器架构,基于JSON-RPC 2.0协议,支持有状态连接和功能协商,允许AI模型访问文件、执行函数和处理上下文提示。

截至2025年4月,MCP已被多个AI平台和开发工具采纳,包括OpenAI、Google DeepMind、Replit、Sourcegraph等。​其应用场景涵盖了软件开发、企业助手、自然语言数据访问等领域。

架构

Model Context Protocol (MCP) 遵循客户端-主机-服务器架构,每个主机可以运行多个客户端实例。此架构使用户能够在应用程序中集成 AI 功能,同时保持明确的安全边界和隔离关注点。基于 JSON-RPC,MCP 提供了一个有状态的会话协议,专注于上下文交换和客户端与服务器之间的采样协调。

MCP架构

为什么使用MCP?

为什么要有MCP这个东西呢?我不用它是否可以?

当然可以。MCP更像是一个USB接口的作用。不使用的话会有一些麻烦而已。我们来看两个案例。

案例一:USB接口案例

我们先想象一下如果电脑没有USB接口,会怎么样?

每个品牌的鼠标键盘显示器接口都不一样,比如

  • 罗技鼠标键盘使用罗技接口
  • 牧马人鼠标键盘使用牧马人接口
  • 雷蛇鼠标键盘使用雷蛇接口
  • 等等,每个品牌有自己的接口

那现在我们买了一台戴尔的电脑,这个电脑只支持戴尔的接口和罗技的接口,这样就导致我们只能买这两个品牌的鼠标键盘显示器了。

像下面这个图,我如果家里是华硕显示器,但是我买了一个戴尔的电脑,就用不了了,很不方便。

MCP架构

案例二:充电接口案例

现在很现实的一个问题就是苹果手机的充电接口。它的充电接口和安卓的充电接口是不一样的,因此两个手机的充电器无法共用。

如果你从安卓换到苹果,你需要重新买一个充电器,如果你家里两个人用的是一个安卓一个苹果,那么你们出门需要带两个充电器。。。

MCP架构

案例三:Agent案例

经过上面两个案例,我们应该明白了MCP解决了什么问题,也明白了为什么要使用MCP这种标准化协议。

那么我们再看看对于Agent而言,MCP到底干了什么呢?

我们当前Agent的现状如下:
我们实现了一个查询天气信息,并根据下雨下雪刮风等进行邮件通知预警的Agent。我们的Agent需要调用2个接口:

  1. 获取天气信息
  2. 发送邮件

我们知道,这两个接口是不一样的,因此,我们需要实现两套调用逻辑

这样是很不方便的。甚至,如果说第一个获取天气信息的接口不好使了,我们要进行切换,其他的获取天气信息的接口也需要重新实现

但是,如果我们使用了MCP,那么我们只需要一套逻辑即可了。

MCP组件

接下来我们来了解一下MCP有哪些内容,包含什么东西。

  • 主机:也就是你的Agent,或者大模型。严格来说,这个并不属于MCP,因为这个是你必备的一个东西。只不过我们的Agent需要支持MCP才可以。
  • MCP服务器:轻量级程序,每个程序都通过标准化的模型上下文协议公开特定的功能。MCP服务器就是一个提供接口程序的服务器程序,可以简单的理解为HTTP服务器,提供一些接口。比如我们上面提到的两个接口都放到这个服务器里面。
  • MCP客户端:这个是重中之重。客户端程序负责调度主机MCP服务器进行交互。

资源

资源表示 MCP 服务器希望提供给客户端的任何类型的数据。这可以包括:

  • 文件内容
  • 数据库记录
  • API 响应
  • 实时系统数据
  • 屏幕截图和图像
  • 日志文件
  • 更多

资源主要用来读取文件内容,比如我们经常使用大模型的时候会上传文件,还有使用Idea的时候可以直接让大模型获取我们的代码文件内容。

资源可以包含两种类型的内容:

  • 文本资源
    • 源代码
    • 配置文件
    • 日志文件
    • JSON/XML数据
    • 纯文本
  • 二进制资源
    • 图像
    • PDF
    • 音频
    • 视频
    • 其他非文本格式

MCP服务器需要实现下面的接口来支持资源功能

  • 资源列表接口:接口地址必须是resources/list,返回内容需要包含4个信息,这个接口会告诉Agent我们拥有哪些资源
    1
    2
    3
    4
    5
    6
    {
    uri: string; //资源的URI
    name: string; //资源的名称,方便人类看
    description?: string; //可选的资源描述
    mimeType?: string; //可选的资源类型
    }
  • 获取资源接口:接口地址必须是resources/read, 返回内容必须是以下格式,这个接口就是当Agent需要获取资源内容的时候进行调用的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    contents: [
    {
    uri: string; //资源的URI
    mimeType?: string; //可选的资源类型

    // 根据资源类型进行返回的信息:
    text?: string; // 文本资源的内容
    blob?: string; // 二进制资源的内容
    }
    ]
    }
  • 资源列表更新接口:接口地址必须是notifications/resources/list_changed,这个接口是当我们能获取的资源列表有变化的时候,服务器来通知客户端说列表改变了,你需要重新获取一下。
  • 资源内容更新接口:
    • 客户端进行资源订阅,接口地址必须是resources/subscribe
    • 当资源内容改变的时候,服务器会发送notifications/resources/updated来通知客户端资源内容变化了
    • 客户端可以重新调用获取资源接口来获取最新内容。
    • 客户端可以取消资源订阅,接口地址必须是resources/unsubscribe

提示

提示这个功能也是比较常见的,当你输入/以后,会出现一些提示的命令,很多大模型都支持这么做了。

通过提示这个功能,我们自己的Agent也可以轻松支持这个功能了。

提示符使服务器能够定义可重用的提示模板和工作流,客户端可以轻松地向用户和 LLM 显示。它们提供了一种强大的方式来标准化和共享常见的 LLM 交互。

MCP服务器需要支持以下接口:

  • 提示列表接口:接口地址prompts/list.展示提示列表。返回内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    prompts: [
    {
    name: "analyze-code", // 提示名称
    description: "Analyze code for potential improvements", //提示描述
    arguments: [ // 提示需要的参数列表
    {
    name: "language", //参数名称
    description: "Programming language", //参数描述
    required: true //是否必填
    }
    ]
    }
    ]
    }
  • 使用提示接口:接口地址prompts/get,服务器会返回一些信息如下,客户端可以将这个信息喂给服务器。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    description: "Analyze Python code for potential improvements",
    messages: [
    {
    role: "user",
    content: {
    type: "text",
    text: "Please analyze the following Python code for potential improvements:\n\n```python\ndef calculate_sum(numbers):\n total = 0\n for num in numbers:\n total = total + num\n return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
    }
    }
    ]
    }

工具

工具功能是MCP的核心功能,作用就是我们一开始讲的那个。通过这个功能我们可以接入所有支持MCP的工具。

工具是模型上下文协议(MCP)中的一个强大的原语,它使服务器能够向客户端公开可执行功能。通过工具,LLM 可以与外部系统交互,执行计算,并在真实的世界中采取行动。

MCP服务器需要实现以下接口:

  • 工具列表接口:接口地址是tools/list,作用是获取MCP服务器支持的所有工具列表。可以返回如下内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    name: "github_create_issue", //工具名称
    description: "Create a GitHub issue", // 工具描述
    inputSchema: { // 输入参数
    type: "object", // 参数类型
    properties: { //详细的请求参数名称和类型
    title: { type: "string" },
    body: { type: "string" },
    labels: { type: "array", items: { type: "string" } }
    }
    }
    }
  • 工具调用接口:接口地址是tools/call,作用是调用指定的工具。客户端同样可以把调用工具的结果喂给大模型。

采样

采样是一个强大的 MCP 功能,允许服务器通过客户端请求 LLM 完成,在维护安全和隐私的同时实现复杂的代理行为。

采样流程如下:

  1. 服务器请求客户端的接口sampling/createMessage
  2. 客户端检查请求并可以修改它
  3. 客户端获取大模型的样本
  4. 客户端复审样本信息
  5. 客户端将结果返回给服务器

服务器可以指定要采样的大模型,如果客户端没有这个大模型,服务器还可以指定一些模型偏好,也就是一些优先级,如果没有这个大模型,就按照服务器指定的优先级来选择一些其他的大模型进行采样。

模型偏好设置

  • hits:模型名称建议的数组,客户端可以使用它来选择合适的模型,客户端可以将提示映射到来自不同提供程序的等效模型,多个提示按优先级顺序进行评估
  • costPriority: 降低成本的重要性,低成本的模型优先级更高
  • speedPriority: 低延迟响应的重要性,速度更快的模型优先级更高
  • intelligencePriority:高级模型功能的重要性,功能强大的模型优先级更高

是 MCP 中的一个概念,它定义了服务器可以操作的边界。它们为客户端提供了一种方法,可以将相关资源及其位置通知服务器。

根是客户端建议服务器应该关注的 URI。当客户端连接到服务器时,它声明服务器应该使用哪些根。虽然主要用于文件系统路径,但根可以是任何有效的 URI,包括 HTTP URL。

根有几个重要的用途:

  • 指导 :它们通知服务器相关资源和位置
  • 清晰度 :根目录清楚地表明哪些资源是您工作空间的一部分
  • 组织 :多个根允许您同时使用不同的资源

当客户端支持根时,它:

  • 在连接期间声明功能
  • 向服务器提供建议的根目录列表
  • 根目录更改时通知服务器(如果支持)

虽然根是信息性的,并不严格执行,但服务器应该:

  • 尊重提供的根
  • 使用根 URI 定位和访问资源
  • 优先考虑根边界内的操作

根通常用于定义:

  • 项目目录
  • 存储库位置
  • API 端点
  • 配置目录
  • 资源边界

客户端应该返回如下根信息

1
2
3
4
5
6
7
8
9
10
11
12
{
"roots": [
{
"uri": "file:///home/user/projects/frontend",
"name": "Frontend Repository"
},
{
"uri": "https://api.example.com/v1",
"name": "API Endpoint"
}
]
}

MCP服务器

MCP服务器作为接口的提供方。需要遵循MCP协议本身的一些规则。

需要支持上面的一些功能,主要是支持工具。至于资源和提示功能可以不支持。

MCP客户端

MCP客户端作为接口的调用方。也需要遵循MCP协议本身的一些规则。

可以选择支持采样功能。

MCP客户端还需要调用MCP服务器和大模型进行沟通。

MCP通信协议

所有传输都使用 JSON-RPC2.0 来交换消息。

JSON-RPC2.0

JSON-RPC 2.0是一个轻量级的远程过程调用(RPC)协议。它使用JSON格式的数据进行通信,这使得它非常易于理解和操作。下面是对JSON-RPC 2.0协议的详细解释以及案例说明:

在JSON-RPC 2.0中,通信的基本单位是请求和响应。客户端发送请求给服务器,服务器处理请求后返回响应。

一个典型的JSON-RPC 2.0请求格式如下:

1
2
3
4
5
6
7
8
9
{
"jsonrpc": "2.0", // 协议版本,固定2.0即可
"method": "methodName", //要调用的方法名称,可以简单理解为接口地址
"params": { //请求参数
"param1": "value1",
"param2": "value2"
},
"id": 1 // 请求的唯一标识
}

服务器处理请求以后,通常会返回以下两个格式:

  • 正确返回的格式。
    1
    2
    3
    4
    5
    {
    "jsonrpc": "2.0", // 协议版本,固定2.0即可
    "result": "Success", //成功的结果
    "id": 1 // 请求的唯一标识,要和请求的id是一样的,代表是这个请求的返回
    }
  • 如果出现错误,需要返回错误码和错误信息。
    1
    2
    3
    4
    5
    6
    7
    8
    {
    "jsonrpc": "2.0", // 协议版本,固定2.0即可
    "error": { // 错误信息,包括错误码和错误描述
    "code": -32601,
    "message": "Method not found"
    },
    "id": 1 // 请求的唯一标识,要和请求的id是一样的,代表是这个请求的返回
    }

除了正常的请求和返回以外,还有一种通知类型。作为单向消息从客户端发送到服务器,反之亦然。接收方不得发送响应。

1
2
3
4
5
6
7
{
"jsonrpc": "2.0",
"method": "methodName",
"params?": { // 请求参数
[key: string]: unknown
}
}

MCP传输方式

MCP 使用JSON-RPC对消息进行编码。JSON-RPC消息必须是 UTF-8 编码的。

该协议目前为客户端-服务器通信定义了两种标准传输机制:

  • STDIO: 通过标准输入输出来进行传输,通常用在MCP客户端和服务器都在同一主机的情况下。
  • HTTP:通过HTTP来进行传输,这允许MCP客户端和服务器不在同一主机。

客户端应尽可能支持stdio传输。

STDIO

  • 客户端将 MCP服务器作为子进程启动。
  • 服务器从其标准输入(stdin)读取 JSON-RPC 消息,并将消息发送到其标准输出(stdout)。
  • 消息可以是 JSON-RPC 请求、通知、响应或 JSON-RPC 包含一个或多个请求和/或通知的批处理。
  • 消息由换行符分隔,并且不能包含嵌入的换行符。
  • 服务器可以将 UTF-8 字符串写入其标准错误(stderr)以进行日志记录。客户端可以捕获、转发或忽略此日志记录。
  • 服务器不能向它的 stdout 写入任何不是有效的 MCP 消息。
  • 客户端不得向服务器的 stdin 写入任何不是有效 MCP 消息的内容。

初始化

HTTP

HTTP 传输中,服务器作为一个独立的进程运行, 可以处理多个客户端连接。

此传输使用HTTPPOSTGET 请求。

服务器可以选择使用服务器发送的事件 (SSE)以流式传输多个服务器消息。

这允许基本的 MCP 服务器,以及支持流媒体和服务器到客户端通知和请求的功能更丰富的服务器。

MCP生命周期

模型上下文协议(MCP)为客户端-服务器连接定义了严格的生命周期,以确保适当的能力协商和状态管理。

初始化

初始化

初始化

  • 客户端发送包含协议版本功能初始化请求
  • 服务器返回服务器支持的协议版本功能进行响应
  • 客户端发送初始化通知作为确认

此阶段要确认的内容如下:

  • 建立协议版本兼容性
  • 交换和谈判能力
  • 分享实施细节

请求示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize", //初始化方法
"params": {
"protocolVersion": "2024-11-05", // MCP协议版本
"capabilities": { // 支持的能力
"roots": { //支持根
"listChanged": true // 根变化的时候可以通知
},
"sampling": {} // 不支持采样
},
"clientInfo": { //客户端基本信息
"name": "ExampleClient", //客户端名称
"version": "1.0.0" //客户端版本
}
}
}

对应的服务器接收到请求以后可以返回如下内容:

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
{
"jsonrpc": "2.0",
"id": 1,
"result": { // 返回结果
"protocolVersion": "2024-11-05", //MCP协议版本
"capabilities": { // 服务器支持的能力
"logging": {}, // 日志能力
"prompts": { // 提示能力
"listChanged": true
},
"resources": { // 资源能力
"subscribe": true,
"listChanged": true
},
"tools": { // 工具能力
"listChanged": true
}
},
"serverInfo": { // 服务器基本信息
"name": "ExampleServer",
"version": "1.0.0"
},
"instructions": "Optional instructions for the client"
}
}

成功初始化以后,客户端必须发送一个初始化通知来确认初始化完成。

1
2
3
4
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}

在初始化请求中,客户端必须发送它支持的协议版本。这应该是客户端支持的最新版本。

如果服务器支持请求的协议版本,它必须以相同的版本响应。否则,服务器必须使用它支持的另一个协议版本进行响应。这应该是服务器支持的最新版本。

如果客户端不支持服务器响应中的版本,它应该 断开连接。

操作阶段

这个阶段表示已经初始化完成了,可以双方开始通信了。

在这个阶段就可以开始上面说的交流了。

这里应该注意

  • 尊重协商的协议版本
  • 仅使用已成功协商的功能

关闭

在关闭阶段,一方(通常是客户端)干净地终止协议连接。没有定义特定的关闭消息-相反,应该使用底层传输机制来发出连接终止的信号:

​对于STDIO的传输方式来说,可以进行如下步骤关闭MCP:

  • 首先,关闭子进程(服务器)的输入流
  • 等待服务器退出,或者如果服务器没有在合理的时间内退出,则发送SIGTERM信号
  • 如果服务器SIGTERM之后的合理时间内没有退出,则发送SIGKILL信号
    服务器可以通过关闭到客户端的输出流并退出来启动关机。

对于 HTTP传输方式来说,通过关闭关联的 HTTP 连接就可以了。

总结

我们主要介绍了MCP的概念,MCP的通信协议、通信方式、生命周期。MCP到底是什么东西,实现了哪些内容,使用场景以及为什么要使用MCP。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

大厂中的Code Review

周末没事的时候,出来和朋友喝酒撸串,谈论国(chui)家(chui)大(niu)事(bi)。

喝到尽兴的时候,免不了一顿八卦盛宴。男人嘛,聚在一起吹牛逼的时候,无非就是江山美人这些事。

从中华上下五千年聊到他们公司新来了美女实习生

到这里,我已经脑补出来了下文,比如“领导强迫美女实习生”或是“我和美女不得不说的故事”这种狗血剧情。

朋友A说这个美女是挺漂亮,就是技术能力不太行。改代码的时候,误删除了一行代码,导致线上出现问题了。

这时候,朋友B估计也是喝上头了,开始为美女打抱不平了。

朋友B说,人家实习生,你们也不能要求太多吧,你们公司不要让她来我们公司好了。

好小子,算盘珠子都崩我脸上了。。。

我也奇怪的问了一句,你们厂没有代码Review吗?按理说,这种问题不应该发生吧。

朋友A说,有是有,可能大家都不太认真吧,谁都没发现这个问题。。。也就稀里糊涂的上线了。。。

这个问题,还是过了一个月才发现的。。。

朋友A继续吐糟。这代码Review啊,也不是个什么好活,感觉大家都不太乐意干。主要是

  • 增加了工作量
  • 出事了还要担责

比如这次,对于这两位代码Review的同事,肯定要被追问,为啥当时没有发现。

说实话,大部分人做代码Review的时候都不做不到很认真。这是没办法的事,毕竟你又要去了解需求逻辑,还要一行行代码的去过。那没几个小时是过不完的,同时你还要干自己的工作。哪有那么多时间去认真Review呢。

所以大部分人应该也就是粗略看一下,没啥问题就给过了。

朋友A也说,他们这次痛定思痛。决定优化代码Review的流程。

优化后的代码Review

角色定义如下:

  • 提交人:代码Review的发起人。写代码的人。
  • 审查人:负责进行代码Review工作的人。需要对代码进行Review。

对于提交人来说,需要提前拉会进行CR会议,正常需求来说至少提前1天拉会。并明确以下几点:

  • 需求文档
  • 技术方案
  • 代码CR地址
  • 主要修改逻辑点
  • 自测报告
  • 上线时间

这些要求可以帮助审查人快速了解自己需要做什么,重点放在哪里,避免浪费审查人的时间。

当然了这样做确实会增加一些提交人的工作量。但是还算好的。

毕竟,需求文档、技术方案、自测报告都是现成的。也就是写一下主要修改逻辑点而已。

对于提交人来说,还需要选择合适的审查人,至少有一个审查人是了解需求的。

对于审查人来说,同样有一些要求,主要为了让审查人能够认真的对待这件事情。

  • 代码逻辑是否正确(需要了解需求)
  • 并发处理是否正确
  • 单元测试是否覆盖了核心逻辑
  • 代码命名、格式、注释。是否能让人一下子就明白这个代码是干啥的。
  • 方法抽象是否合理
  • 异常处理
  • 性能问题
  • 安全问题

至少需要检查以上几点,如果有问题及时提出来,让提交人进行解决。

FAQ

Q:如果审查人没有时间怎么办?
A:审查人需要及时回复提交人,方便提交人选择新的审查人。

Q:如果审查人意见不一致怎么办?
A:建议选择奇数个审查人,少数服从多数。

Q:审查人是否会对技术方案提出问题?
A:审查人应该聚焦于技术实现,技术方案的问题应该在技术方案评审上提出而不是代码Review上提出。

Q:紧急需求没时间代码Review了怎么办?
A:紧急需求紧急处理,快速找人Review一下即可。

总结

对于所有人来说,代码Review好像都是一个巨大的问题,很多公司甚至难以推动执行。

我觉得根本的问题在于,代码Review的收益是一个长期的、利他的收益。对于大家来讲,最直观的就是工作量的增加,如果出问题了还要担责任。

大家的公司都是怎么做代码Review的呢?可以发出来大家一起聊聊。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

大厂中的Code Review

周末没事的时候,出来和朋友喝酒撸串,谈论国(chui)家(chui)大(niu)事(bi)。

喝到尽兴的时候,免不了一顿八卦盛宴。男人嘛,聚在一起吹牛逼的时候,无非就是江山美人这些事。

从中华上下五千年聊到他们公司新来了美女实习生

到这里,我已经脑补出来了下文,比如“领导强迫美女实习生”或是“我和美女不得不说的故事”这种狗血剧情。

朋友A说这个美女是挺漂亮,就是技术能力不太行。改代码的时候,误删除了一行代码,导致线上出现问题了。

这时候,朋友B估计也是喝上头了,开始为美女打抱不平了。

朋友B说,人家实习生,你们也不能要求太多吧,你们公司不要让她来我们公司好了。

好小子,算盘珠子都崩我脸上了。。。

我也奇怪的问了一句,你们厂没有代码Review吗?按理说,这种问题不应该发生吧。

朋友A说,有是有,可能大家都不太认真吧,谁都没发现这个问题。。。也就稀里糊涂的上线了。。。

这个问题,还是过了一个月才发现的。。。

朋友A继续吐糟。这代码Review啊,也不是个什么好活,感觉大家都不太乐意干。主要是

  • 增加了工作量
  • 出事了还要担责

比如这次,对于这两位代码Review的同事,肯定要被追问,为啥当时没有发现。

说实话,大部分人做代码Review的时候都不做不到很认真。这是没办法的事,毕竟你又要去了解需求逻辑,还要一行行代码的去过。那没几个小时是过不完的,同时你还要干自己的工作。哪有那么多时间去认真Review呢。

所以大部分人应该也就是粗略看一下,没啥问题就给过了。

朋友A也说,他们这次痛定思痛。决定优化代码Review的流程。

优化后的代码Review

角色定义如下:

  • 提交人:代码Review的发起人。写代码的人。
  • 审查人:负责进行代码Review工作的人。需要对代码进行Review。

对于提交人来说,需要提前拉会进行CR会议,正常需求来说至少提前1天拉会。并明确以下几点:

  • 需求文档
  • 技术方案
  • 代码CR地址
  • 主要修改逻辑点
  • 自测报告
  • 上线时间

这些要求可以帮助审查人快速了解自己需要做什么,重点放在哪里,避免浪费审查人的时间。

当然了这样做确实会增加一些提交人的工作量。但是还算好的。

毕竟,需求文档、技术方案、自测报告都是现成的。也就是写一下主要修改逻辑点而已。

对于提交人来说,还需要选择合适的审查人,至少有一个审查人是了解需求的。

对于审查人来说,同样有一些要求,主要为了让审查人能够认真的对待这件事情。

  • 代码逻辑是否正确(需要了解需求)
  • 并发处理是否正确
  • 单元测试是否覆盖了核心逻辑
  • 代码命名、格式、注释。是否能让人一下子就明白这个代码是干啥的。
  • 方法抽象是否合理
  • 异常处理
  • 性能问题
  • 安全问题

至少需要检查以上几点,如果有问题及时提出来,让提交人进行解决。

FAQ

Q:如果审查人没有时间怎么办?
A:审查人需要及时回复提交人,方便提交人选择新的审查人。

Q:如果审查人意见不一致怎么办?
A:建议选择奇数个审查人,少数服从多数。

Q:审查人是否会对技术方案提出问题?
A:审查人应该聚焦于技术实现,技术方案的问题应该在技术方案评审上提出而不是代码Review上提出。

Q:紧急需求没时间代码Review了怎么办?
A:紧急需求紧急处理,快速找人Review一下即可。

总结

对于所有人来说,代码Review好像都是一个巨大的问题,很多公司甚至难以推动执行。

我觉得根本的问题在于,代码Review的收益是一个长期的、利他的收益。对于大家来讲,最直观的就是工作量的增加,如果出问题了还要担责任。

大家的公司都是怎么做代码Review的呢?可以发出来大家一起聊聊。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

OpenAI发布新文生图模型,免费、逼真到难以分辨

25年3月底,OpenAI进行了技术直播,对GPT-4o和Sora进行了重大更新,提供了全新文生图(Text-to-Image)模型。

除了文生图之外,该模型还支持自定义操作、连续发问、风格转换、图像PPT等超实用功能,例如,更改图片的宽高比、图像物体中的角度,用十六进制代码指定精确颜色等。

尤其是生成图像的逼真度,肉眼几乎看不出任何破绽,在精细度、细节和文本遵循方面非常出色,可以媲美甚至在某些功能超过该领域的头部平台Midjourney。

OpenAI文生图的优势

ChatGPT的文生图功能自推出以来,迅速引起了广泛关注,其主要优势包括:​

  • 精准的文本渲染能力:​ChatGPT的文生图功能能够准确理解用户的文本描述,并在生成的图像中清晰地呈现文本内容。这对于需要在图像中包含特定文字信息的场景,如制作菜单、信息图表或徽标等,具有重要意义。
  • 严格遵循用户指令:​该功能能够精确执行用户的指令,生成符合预期的图像。例如,用户可以要求生成特定风格、构图或包含特定元素的图像,ChatGPT会尽力满足这些要求。 ​
  • 深度知识调用与创意拓展:​ChatGPT不仅能够利用其广泛的知识库生成图像,还能在创意上进行拓展。例如,用户可以要求生成具有特定艺术风格或融合多种元素的图像,ChatGPT能够提供多样化的创意选项。 ​
  • 高效的图像生成速度:​在实际测试中,ChatGPT能够在约30秒内生成高质量的图像,速度远超预期。这使得用户能够迅速获取所需的图像,提升了创作效率。 ​

需要注意的是,尽管ChatGPT的文生图功能在多个方面表现出色,但在处理非拉丁语系文字时仍存在一定局限性。

文生图功能的发展历史

文生图技术其实很早就有了,只是最近随着大模型的火热以及一些文生图的应用,迎来了一波爆发。

我们也来看一下文生图的历史。​

  1. 初期探索(2014年以前)
    在深度学习兴起之前,文生图的尝试主要依赖于基本的图像处理技术,如将现有图像素材拼贴在一起,形成类似拼贴画的效果。​

  2. 深度学习引入(2014年-2018年)
    随着卷积神经网络(CNN)等深度学习模型的成功应用,研究者开始尝试使用神经网络生成图像。​2015年,多伦多大学的研究人员提出了alignDRAW模型,这是第一个现代文生图模型,能够根据文本序列生成图像。​然而,这些早期模型生成的图像质量有限,通常较为模糊。​

  3. GAN和Transformer的应用(2016年-2021年)
    2016年,研究者开始将生成对抗网络(GAN)应用于文生图任务,取得了更好的生成效果。​2019年,Transformer架构被引入文生图模型,进一步提升了生成质量。​2021年,OpenAI发布了DALL·E模型,采用Transformer架构,能够根据文本描述生成高质量的图像。​

  4. 扩散模型的兴起(2021年至今)
    2021年,以扩散模型(Diffusion Model)为基础的文生图技术取得了显著进展。​这种模型通过逐步添加噪声并学习反向过程,能够生成更高质量的图像。​例如,Stable Diffusion模型在2022年发布,提供了高质量且多样化的图像生成能力。​

openAI文生图功能演示

提示词如下:

1
帮我生成一个图片,是一座科幻风格的城市,路上有一些未来的车辆,空中还有浮空设备,路上有一些行人,有机械改造的行人

来看一下生成的图片,很高清的一张图片,也符合描述。

ai

再换成其他的一些提示词。

1
帮我生成一个图片,是一座赛博朋克风格的城市,路上有一些车辆,空中还有浮空船,路上有一些行人,有的行人装了机械假肢等。夜晚。

看一下效果。

ai

可以看到能实现的效果还是很棒的,我们在看一下图里面加上文字呢?

提示词

1
帮我生成一个五一劳动节的海报,标题是“五一劳动节快乐”,背景是学校,学校门口有一些卖东西的学生

可以看到生成的图里面对于文字的支持还是比较好的,虽然有一些瑕疵,但是比其他的文生图软件要好一些。

ai

再来试试英文的文字呢?

提示词

1
帮我生成一个五一劳动节的海报,标题是“5.1 vacation happy”,背景是学校,学校门口有一些卖东西的学生

图片效果

ai

总结

ChatGPT的文生图功能的推出,标志着内容创作进入了一个新的时代。​无论是微信公众号运营者,还是自媒体创作者,都可以借助这一功能,提升内容质量和创作效率。​随着技术的不断发展,未来的文生图功能将更加智能化、多样化,为创作者提供更多可能性。

而且,OpenAI作为大模型界的老大,它的更新速度也是很快的,说不定过不了多久,其他的文生图软件就要被甩在后面了。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

MySQL零基础教程

本教程为零基础教程,零基础小白也可以直接学习,有基础的可以跳到后面的原理篇学习。
基础概念和SQL已经更新完成。

接下来是应用篇,应用篇的内容大致如下图所示。

概念学习

表设计

表设计可以聊的点其实是比较多的,这个也比较看具体的业务、流量等。

比如,某一个字段是否应该放在这张表里?一张表里应该有哪些字段?如何设计字段类型?

甚至于,如何设计字段的顺序?

这里很多人不知道的一个点在于,字段的顺序也会影响性能,至于为什么,这个就偏低层一些了,下面会讲到。

想要做表设计,那你首先需要知道是什么,所以我们先来看看表到底是什么东西。

表是什么?

有的人会说,表就是Navicat上看到的一张表呗,还能是什么啊?

还有的人说,表就是一行一行数据组成的。

其实说的都对,但是这是逻辑上的表,也就是mysql给我们展现出来的表。

大家有没有想过,表的物理形式是什么,msyql如何将它转化成逻辑上的表方便我们查看呢?

接下来进行揭秘吧!

如何表示磁盘上文件的数据

数据库的数据最终以文件的形式放在磁盘中。通过文件读写将数据读写到文件中。文件有特定的格式,具体的内容有数据库进行解析然后展示在数据库中。这就是storage manager or storage engine

比如MySQL数据库的存储引擎,常用的就是InnoDB存储引擎了,他就是负责干这个事情的。

storage manager负责文件的读写工作。所有的文件(不管是一个或者多个)以 page 的形式存储,管理多个 page 组成的集合。

一个page就是一个固定大小的数据块。page 可以保存任何东西,tupe, metadata, indexes, log等等。每个page有唯一的ID,是page ID

有些page要求是独立的,自包含的(self-contained)。比如mysql的InnoDB。因为这样的话一个表的元数据和本身的数据内容在一起,如果发生问题的话,可以找回元数据和数据。如果元数据和数据在不同的page中,如果发生问题导致元数据的page丢失,那么数据则恢复不了了。

indirection layer记录page ID的相对位置,方便找到对应的偏移量。这样page目录就能找到对应的page。

不同的DBMS对于文件在磁盘上的存储方式不一样,有下面几种

  • 堆存储
  • 树存储
  • 有序文件存储(ISAM)
  • hashing文件存储

像MySQL数据库使用的就是堆存储的方式了,所以我们主要看一下什么是堆存储。

堆存储具有以下特点:

  • 无序的,保存的顺序和存储的顺序无关。
  • 需要读写page
  • 遍历所有的page
  • 需要元数据记录哪些是空闲的page,哪些是已经使用的page。
  • 使用 page directory 方式来记录文件位置。

page directory:说白了就是目录,记录了一些映射关系,在代码里面其实就是个Map。

  • 存储page ID和所在位置的关系
  • 存储page的空闲空间信息

大体结构如图所示:
010

page header:每个page里面都有一段空间用来存储这个page的一些相关信息,这段空间就叫做page header。

  • page 大小
  • checksum 校验和
  • DBMS版本信息
  • 事务可见性
  • 压缩信息

看到这里,其实你就明白了,我们在聊索引的时候,很多时候会说索引的叶子节点是一个page。不知道大家当时有没有疑问,这个page到底是什么?

在这里我就给你讲明白,这个page到底是个什么东西。

每个page里面除了包含page header以外,就是page data了。

这些page里面的数据就是我们能看到的一行行的数据。

我们通常称这样按照一行行数据来存储的数据库叫做行式数据库。比如MySQL就是。

除此之外还有一些按照一列列数据存储的列式数据库

数据表示

我们在做表设计的时候还会考虑到字段的类型,那么如何选择类型呢?

这些类型的底层存储有什么区别?

上面我们已经知道了一个page里面存储了一行行的数据。那么一行数据是多长呢?

这个一行数据的长度自然就是这一行数据所有字段长度的总和了。

字段的长度分为两种类型

  • 固定长度的字段
  • 可变长度的字段

固定长度的字段有下面这些:

  • Int:整型,当然了这里也包括TinyIntSmallIntBigInt等。不同长度的Int类型只是所占的字节数不一样而已。
  • Char:字符型,Char类型就是个字符串,你创建的时候给了多长,就是一个多长的字符串。
  • Decimal:定点小数,也是最常用的小数类型。虽然运算速度慢一些,但是精度高。Flout和Double虽然也是小数,运算速度快但是会有精度丢失的问题。
  • Date:时间类型,包括DateTimeTime等。固定长度用来存储时间。TimeStamp类型已经快要达到上限了,不要再使用了。

可变长度的字段有下面这些,他们的长度会存储在header里面:

  • Varchar: 可变的字符串类型,存储时只占用实际需要的字节数,对比Char类型而言,更加节省空间。
  • VarBinary: 可变长度的二进制字符串类型,类似于 VARCHAR,但存储的是二进制数据。
  • Text:可变长度的长文本类型,存储时占用的字节数取决于实际内容。
  • BLOB:可变长度的二进制数据类型,存储时占用的字节数取决于实际内容。

表设计中尤其要注意的几点:

不要去使用TimeStamp类型了,使用DateTime或者Bigint来存储时间。
其次,避免去使用Flout和Double类型,而是使用Decimal来存储小数,或者使用Int类型来存储。
避免使用大值存储,比如Text和blob类型,而是使用Varchar来代替。

通常说的大值(large values)也就是里面存储的内容过多。

比如我们有一个字段是Text类型,这个字段的数据非常大,占据了Page的一半,再加上其他的数据,那么我们一个Page里面只能存储一行数据了,Page里面剩下的空间就会浪费掉了。

对于这种情况,数据库的设计者也考虑到了,所以他们实际存储的是一个指针,这个指针指向另外一个Page页面。将这个字段的内容存储到另外一个单独的Page里面。这个单独的Page页面叫做Overflow Page

虽然这样解决了上面的问题,但是也引入了新的复杂度,比如对于这个额外的Page页面的维护管理。

NULL存储,表设计中还有重要的一点处理就是NULL值,因此我们通常把字段设置为NOT NULL类型来避免NULL值。因为NULL值有如下问题:

  • 行数据库通常是在Header里面增加bit map来判断是否是null
  • 列数据库通常使用占位符来标识NULL
  • 在每个属性前面增加bit来标识是否是NULL,这么做会破坏对齐,或增加存储空间,MySQL曾使用这个方法,后来抛弃了这个方法。
  • NULL == NULL 是 NULL, NULL is NULL 是 true

page是什么样子的

上面我们讲完了一行数据是什么样子的,那么一个Page里面又是什么样子的呢?

一般想法,就是一行贴着一行直接存储,新的行数据直接在后面追加,但是对于可变数据长度很难管理。

  • 记录page数,也就是page内部可插入的偏移量
  • 一个一个tupe按照顺序存储

007

所以,page内部,通常不使用上面那种,而使用的是slotted pages

  • slotted pages
    • slot array 存储插槽信息的偏移量,通过他找到对应的行数据
    • 支持可变长度的行数据
    • 但是会产生一些碎片空间,因为太小,一行数据放不下。
    • 压缩可以去除碎片空间,但是压缩的时候这个page就不能读写了。

008

mysql innodb 压缩

innodb 在写入的时候可以不解压,但是读取的时候会先在buffer pool中解压在读取。因此Mysql innodb的压缩的好处是提升空间利用率,减少了磁盘IO,缺点是读取的时候需要解压,因此增加了这部分的时间和CPU功耗以及解压以后会占用更多的内存空间。
innodb 默认page 是 16KB,可以压缩到1/2/4/8KB。

016

数据对齐

现代CPU是64位对齐,创建表以后,DBMS会自动的将数据进行对齐存储,不过,如果在创建表的时候考虑对齐,可以优化速度和存储空间。

012

还记得我们上面说过的吗,字段的顺序也是会影响性能的,如果你的字段顺序能满足数据对齐的要求,那么就可以避免空间的浪费,同一个page里面就可以存储更多的行数据,也就意味着我们每次获取一个page的时候,能从磁盘拿到更多的数据,因此我们获取大量数据的时候,IO次数就会减少,从而起到提升性能的效果。

实战

表设计要考虑的其实是比较多的,相信你看完上面的内容,对于如何设计表,应该有了一些自己的方法论。

这里再讲一下实战中需要注意的事情吧。

再开始设计表之前,我们肯定要先分析需求,然后才能知道我们需要存储哪些数据。

比如,我们要做一个招聘网站,那么我们需要存储发布的职位信息。

职位信息都放到一个表里面吗?

职位信息其实还挺多的,不光是常见的职位名称、职位JD。还会涉及到比如职级信息、工资信息、面试轮次,是否支持视频面试、学历要求、学校要求、工作经历要求大厂等。

这里面有一个冷热数据的概念。

比如说,有一部分信息是经常要查询的,比如职位名称、学历要求、工作年限要求、工资、公司信息、招聘人头像、名称、标签等。

首先、这些数据肯定放在多个表里面的,比如招聘人头像、名称是用户表的数据、标签信息是标签表的数据、公司信息是公司表的数据。但是其中职位名称、学历要求、工作年限、工资是属于职位的基本信息。而且是在职位列表、IM聊天中的职位卡片、职位详情、职位浏览、职位收藏等多个维度高曝光的职位信息。

那么就代表这些职位信息属于热数据,会被经常一起查询。因此他们放到一个表里面是没有问题的。

剩下的职位信息我们可以分成两类。

  1. 在职位详情页面首屏展示的内容或者一些强依赖的内容,这些数据也可以和上面的放在一个表里面。
  2. 一些其他不经常查询的数据,这些数据可以放在另外一个表里面。

这样的话,每次查询热数据的时候从第一张职位信息表获取,查询到的page里面都是包含的有用的信息,就可以减少IO次数。

因此,我们可以设计两个表来存储职位信息。

  • 职位信息表:存储职位的主要数据、热数据、提升查询速度。
  • 职位扩展信息表:存储职位的次要数据、冷数据、只在需要的时候进行查询。

总结

授人以鱼不如授人以渔,相信经过上面的学习,你已经具备了一定的表设计的能力了。

这里讲的主要是表的设计,而不是整个数据的设计。因为还缺少了一些,比如索引该如何设计?

如何保证大量数据的查询?

其实对于MySQL来说上索引以后就可以查询百万级的数据了,但是对于非常要求速度和更高量级的数据而言。还可以使用一些其他的方法,比如使用列式数据库来进行查询。

这样的话可能还会涉及数据同步、数据清洗等等。

相信你学完我的整个系列以后、对于更高量级的数据设计也会有一定的经验的。

如果在面试中遇到类似的问题,你也可以游刃有余的回答面试官。

在学习的过程中,我们也要做到知其然也知其所以然

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

好的Prompt事半功倍

万能公式:定义角色 + 需求背景 + 实现目标 + 补充要求 + 示例

本教程为零基础教程,零基础小白也可以直接学习,有基础的可以跳到后面的原理篇学习。
基础概念和SQL已经更新完成。

接下来是应用篇,应用篇的内容大致如下图所示。

概念学习

表设计

表设计可以聊的点其实是比较多的,这个也比较看具体的业务、流量等。

比如,某一个字段是否应该放在这张表里?一张表里应该有哪些字段?如何设计字段类型?

甚至于,如何设计字段的顺序?

这里很多人不知道的一个点在于,字段的顺序也会影响性能,至于为什么,这个就偏低层一些了,下面会讲到。

想要做表设计,那你首先需要知道是什么,所以我们先来看看表到底是什么东西。

表是什么?

有的人会说,表就是Navicat上看到的一张表呗,还能是什么啊?

还有的人说,表就是一行一行数据组成的。

其实说的都对,但是这是逻辑上的表,也就是mysql给我们展现出来的表。

大家有没有想过,表的物理形式是什么,msyql如何将它转化成逻辑上的表方便我们查看呢?

接下来进行揭秘吧!

总结

通常来说,ER图是在设计阶段完成的,先有ER图再有表结构。

可如果你已经有了表结构,有没有办法生成ER图呢?

也是有方法的,比如著名的Navicat工具,就支持这么做。

此外,还有一个方法,就是使用在线工具dbdiagram,这个工具可以导入现有的SQL,会生成ER图,如下。

概念学习

这个网站是通过左边的一个叫dbml的语言来生成ER图的,也支持直接导入SQL,转化成dbml格式再生成ER图。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

MIT6.824

你在尝试构建分布式系统之前,应该先尝试单机系统,如果能满足的话不要搞分布式。

因为单机系统比分布式简单的多。

分布式的原因是需要获得更高的性能、某种并行性、大量的CPU、大量的内存、大量的磁盘。另一个原因是容忍故障,一个机器挂了还有别的机器提供服务。
还有些可能是自然的物理分布式,比如银行多个地方的转账。还有安全性,可以隔离出环境运行代码。来保证主环境的安全。

分布式系统的挑战

  • 并发性
  • 部分故障
  • 性能

本课程的目标是构建面向应用程序的基础设施:

  • 存储
  • 网络通信
  • 计算

主要目的为抽象这些能力给外部应用提供接口,隐藏内部的分布式实现。

一些已有的实现示例:

  • RPC:隐藏了通信能力
  • Thread:隐藏了多核操作系统的并发能力

构建分布式系统的高层次的目标,这也解释了为什么构建分布式系统很难:

  1. 可扩展性():系统的横向扩展能力,理想情况是加n台机器可以获得n台机器的性能。
  2. 容错性(Fault Rable):
    • 可用性:系统的高可用能力,如果有多个机器的话,当一个机器挂了,还有其他机器可以提供服务,保证系统可用。
    • 故障恢复性:当故障恢复以后,系统可以和故障恢复之前一样运行,没有数据损失等。可以通过复制的能力实现,存储多个数据副本。
  3. 一致性(Consistency):语义是当put(k,v)以后,一定会get(k)能得到v。但是对于分布式系统来说这是不一定的。
    • 强一致性
    • 弱一致性

MapReduce

最开始是Google提出的MapReduce,这篇论文可以追溯到2004年。有兴趣的可以阅读这个论文:http://nil.csail.mit.edu/6.824/2020/papers/mapreduce.pdf

当时Google面临的问题是要对数TB的数据进行计算。因为他们要从海量的数据中找出优先级最高的页面展示出来。

他们迫切的希望用数千台计算机来共同完成,来加速这个工作,而不是用一台计算机独立完成。

MapReduce希望开发者只需要编写Map函数和Reduce函数,其他的交给MapReduce框架来做。将这些函数放到无数的计算机上执行。

核心思想:将输入分成多份,产生多个输入。并对每个输入调用Map函数。

Map

Map函数将输入内容进行处理,输出一组key=>value结构。你可以把 Map 理解成分类处理的过程。

示例1:Map将从输入中统计每个英文字母出现的次数。

1
2
3
Input1 => Map => 输出:a:1, b:2, c:0
Input2 => Map => 输出:a:0, b:1, c:1
Input3 => Map => 输出:a:1, b:0, c:0

简化的Map函数如下:
Map函数接收两个参数,k是文件名称,v是文件内容

1
2
3
4
5
6
7
8
9
10
11
12
split file // 拆分文件内容
Map(k, v){
// 循环输出每个字母
for(w:words) {
//3. 输出格式同样是k=》v,k是字母,v是出现次数。
// emit函数是输出函数,由MapReduce框架提供
// 为什么输出1?因为是简单的计数,每个字母出现1次
emit(w,1);
}
}

// 实际输出结果:a:1, b:1, b:1

Reduce

Map 阶段产生的键值对,按 key 分组并聚合处理,得到最终的结果。

你可以把 Reduce 理解成对同一类的东西做总结的过程。

而Reduce函数同样接收输入,在这个示例中,我们的Reduce函数可以接收某一个字母和出现的次数做为输入,输出总的出现次数。比如:

1
2
3
Input1 (a:1, a:0, a:1) => Reduce => 输出:a:2
Input2 (b:2, b:1, b:0) => Reduce => 输出:b:3
Input3 (c:0, c:1, c:0) => Reduce => 输出:c:1

简化的Reduce函数如下:
Reduce函数同样接收文件做为输入。

1
2
3
4
5
6
// reduce函数的输入,k是用来聚合的key,在这里,这个k就是字母,可能是a,b,c。对于计算来说,用不到这个k,v是map输出的值的list。
// 比如a:1, a:0, a:1, k是a, v是[1,1]
Reduce(k, v) {
// 直接输出计数即可 因为list的长度就是计数,这是因为每个值都是1.
emit(len(v));
}

这样就计算出了所有输出中,abc三个字母出现的次数。

最妙的设计在于,按照上述的例子,我们可以部署6个机器来同时完成任务。

而且,对于Map和Reduce函数来说,优点有两个:

  1. 逻辑简单,仅仅是简单的计算逻辑。因此运行速度快
  2. 可以方便的横向扩展。

最关键的点在于

1
程序员只需关心逻辑,不用操心分布、容错、调度等复杂细节

整体执行流程

[执行流程图片]

  1. 输入数据被分片(Split)
  • 原始的大数据(比如 1TB 的日志)被切分成多个小片(通常 64MB 或 128MB 一片)。
  • 每个数据片(split)会由一个 Map Task 处理。

👉 比喻:像把一本厚书分成一页一页,由多个读者同时阅读处理。

  1. Map 阶段执行(并行执行)
  • 系统在多台机器上启动多个 Map Worker,每个负责一个 split。
  • 每个 Map Worker:
    • 读取数据片
    • 执行用户定义的 Map() 函数
    • 输出一组键值对 (key, value)
    • 把输出缓存在本地磁盘上,并根据 key 做分区(为接下来的 Reduce 做准备)

👉 比喻:每位工人处理一摞原材料,并将成果放入不同颜色的桶(按 key 分类)。

  1. 分区与 Shuffle(洗牌阶段)
  • 系统自动将所有 Map 的输出,按 key 分发给不同的 Reduce Worker
  • 这个过程称为 shuffle,是 MapReduce 的核心。
  • Reduce Worker 从各个 Map Worker 取自己负责的那一部分 key。

👉 比喻:每个桶被送到对应的收集员手里,收集员只关心自己那种颜色的桶。

  1. Reduce 阶段执行

每个 Reduce Worker:

  • 接收所有属于自己负责 key 的 (key, [value list])
  • 执行 Reduce() 函数,输出最终结果

👉 比喻:每个收集员把收到的同一类物品合并、统计或总结。

  1. 结果输出
  • Reduce 结果被写入分布式文件系统(如 GFS 或 HDFS)
  • 每个 Reduce Worker 写一个文件,形成最终的输出集合。
  1. 容错机制(Fault Tolerance)
  • MapReduce 最大的优势之一是它对机器故障有强大容错支持:
  • 任务失败了?——Master 会把任务重新分配给另一台机器。
  • 机器宕机?——系统检测心跳超时,把任务转移。

Reduce 不会从内存里读数据,而是从 Map 的本地磁盘拉,这样更安全。

👉 比喻:如果一个工人累了/走了,另一个人接手继续干,不影响整体结果。

容错机制

这是 MapReduce 的亮点之一,它自动处理各种失败情况:

🧯 Map 或 Reduce 任务失败
- Worker 崩了?任务失败?
- ✅ Master 重新调度任务,由其他空闲 Worker 重做

💀 Worker 节点宕机
- Master 检测不到心跳信号(比如 10 秒没回应)
- ✅ 所有该节点上的任务都会被视为失败,重新调度

📉 Reduce 不会因为 Map 崩了而挂掉
- 因为 Map 的中间结果会写入磁盘,且 Reduce 是拉数据

优化点 说明
数据本地性 尽量将 Map 任务调度到数据所在的机器,减少网络传输
备份任务(Backup Tasks) 在任务快结束时,为剩余最慢的任务启动副本,避免尾部拖慢整个任务(称为“straggler mitigation”)
流水线执行 Reduce Worker 可以在 Map 未完全结束时,开始拉部分数据
特性 好处
主从架构(Master/Worker) 易于调度和管理
本地磁盘缓存中间结果 提高容错性和效率
Shuffle 自动进行 程序员无需处理网络传输
容错机制完备 任意节点失败不会影响整体任务
自动调度和重试 解放程序员双手

性能

🧪 案例1:构建倒排索引(Inverted Index)
🌟 应用背景:
Google 搜索引擎需要知道每个词在哪些网页中出现。这个操作就叫“构建倒排索引”。

📦 处理规模:

  • 输入数据:约 20TB(网页内容)
  • Map 任务数:1万个
  • Reduce 任务数:2千个

⏱ 执行时间:
整个任务在几百台机器上并行,只花了几小时

✅ 意义:
传统方式实现这样的任务要花几周甚至几个月,而 MapReduce 能快速完成,还能处理节点故障。

🧪 案例2:分析网页连接图(PageRank 计算)
🌟 应用背景:
PageRank 是 Google 搜索排名的核心算法,需要处理整个互联网的网页链接关系。

📦 处理规模:

  • 输入数据:超过 1TB 的链接图
  • 运行多个 MapReduce 迭代(每一轮都读取+写入)

⏱ 执行时间:
单轮耗时在几十分钟到几小时之间,取决于迭代次数

✅ 意义:
MapReduce 适合这种需要反复运行、聚合中间结果的图算法。

可扩展性实验

论文还专门做了 实验测试 MapReduce 的可扩展性,结果非常亮眼:

实验设置:

  • 任务:排序 1TB 的数据(标准大数据计算任务)
  • 测试变量:机器数量(从几十台到几百台)
机器数量 执行时间
100 台 ~60 分钟
200 台 ~35 分钟
400 台 ~20 分钟

✅ 说明:机器数量翻倍 → 执行时间几乎减半
这叫做“近线性扩展性”,是分布式系统性能的理想状态。

容错能力实验

论文还测试了在有机器故障的情况下系统能否稳住:

实验方法:

  • 在运行中故意杀掉部分 Worker
  • 查看任务是否恢复 + 时间是否增加很多

结果:

  • 系统能成功恢复失败任务
  • 整体执行时间仅略有增加(因为失败重试带来小延迟)

✅ 意义:说明 MapReduce 的容错机制在实践中可靠,不会因为单点失败拖垮整个任务。

一些优化细节

优化策略 效果
本地性调度(Data Locality) 避免 Map 任务跨机器读取数据,减轻网络负担
Map 输出写入本地磁盘 避免 Reduce 拉取失败,提高稳定性
Backup Task(备份任务) 减少 straggler 影响,加快尾部执行
Reduce 端部分排序 避免 Reduce 端内存爆炸,提高聚合效率

小结:为什么 MapReduce 性能优秀?

方面 优势
并行计算 成千上万台机器并发执行任务
任务分片合理 拆成很多小任务,调度灵活
自动容错 节点失败不会拖垮任务
IO 优化好 避免不必要的网络流量
扩展性强 机器越多,速度越快,效率不降反升

经验

  1. 编程模型简单但表达力强
    作者观点:

    MapReduce 的接口非常简单(就两个函数:Map() 和 Reduce()),但几乎可以表达大部分并行数据处理逻辑。

实际例子:

  • 排序、去重、合并日志
  • 构建索引、计算网页权重、图处理
  • 数据挖掘任务如聚类、统计分析

🧠 体会:
你不需要了解线程、锁、通信协议这些“硬核分布式知识”,也能写出能在几千台机器上跑的大数据程序。

  1. 对“失败”高度容忍是必须的
    作者观点:

    在几百上千台机器上运行任务,机器故障是常态,不是例外。系统设计要“默认它会失败”。

做法:

  • Map/Reduce Task 自动重试
  • Master 负责监控和再调度
  • 中间结果写磁盘、持久化,方便恢复

🧠 体会:
不要去“防止失败”,要“拥抱失败”,让失败变得对用户透明,这才是工业级分布式系统。

  1. 数据本地性是性能关键
    作者观点:

    尽量把计算调度到数据所在机器,可以显著减少网络压力。

原因:

  • 在 Google 文件系统(GFS)中,数据有副本
  • Master 可以根据副本位置,把 Map 任务调到数据“身边”

🧠 体会:
在分布式系统中,“移动计算”比“移动数据”更高效

  1. Straggler 问题是真实存在的
    作者观点:

    在成百上千个任务中,总会有几个“掉队者”(straggler),它们可能因为磁盘慢、CPU 抢占等原因拖慢整个作业。

解决方案:

  • 启动 Backup Task(备份任务)
  • 哪个先完成就用哪个,放弃另一个

🧠 体会:
在大规模并发中,整体速度由“最慢的少数人”决定(这就是“长尾延迟”问题)

  1. 开发调试工具非常重要
    作者观点:

    运行成千上万个任务后,你很难靠肉眼看日志找问题,需要专门的 监控与调试工具。

Google 实践:

  • 为每个任务生成详细的 web 页面
  • 可以追踪任务状态、失败原因、数据流向
  • 所有任务的标准输出也会被收集并存档

🧠 体会:
好的工具不仅能“看见”问题,更能“预防”问题。

  1. 通用性强,支持跨部门复用
    作者观点:

    最开始 MapReduce 是为构建索引设计的,后来被应用于:

  • 日志分析
  • 机器学习数据预处理
  • 图结构计算
  • 分布式 Grep、排序、压缩
  • 多语言支持(C++、Java、Python 等)

🧠 体会:
一个简单的思想,配上良好封装与容错机制,就能成为全公司的“生产力工具”

✅ 最后,作者对读者说了什么?
他们希望告诉大家:

“MapReduce 的核心思想是抽象:程序员只需要关注如何写 Map 和 Reduce,不需要去处理分布式的复杂性。”

这种思想不仅影响了后来的 Hadoop/Spark/Flink,也启发了很多 “让人类专注业务逻辑,其余交给系统” 的工程思维。

总结:

教训/反思 含义
简单接口胜过复杂灵活 简单更易学更普及
容错不是加上去的,是设计进来的 面向失败编程
调度比你想象的重要 数据本地性和长尾问题会拖垮系统
工具让大规模系统可维护 千万别忽视监控、调试界面
通用性不是副产物,是目标 抽象设计时就考虑不同场景

和其他的对比

🧭 MapReduce 提出前,世界在干什么?
在 MapReduce 出现之前,“处理海量数据”是非常痛苦的事情,常常需要:

  • 自己手写分布式代码(多线程、RPC、容错逻辑)
  • 手动分片、调度、失败重试
  • 大量系统调优

也就是说:门槛高、出错多、效率低。

🧓 1. 前辈系统(先驱者)
MapReduce 借鉴并超越了很多已有的系统。作者提到了几个重要的前辈:

🧱 Parallel Databases(并行数据库系统)
比如:Teradata, Gamma, Volcano

  • 通过 SQL 自动并行执行、查询优化

但局限性明显:

  • 灵活性低,只适合结构化数据
  • 编程模型不够通用(不能表达复杂业务逻辑)
  • 扩展性不足(难以横向扩展到上千台机器)

MapReduce 与之不同:

  • 不需要预定义 schema
  • 可处理任意数据(文本、图像、日志)
  • 扩展性和容错机制是核心设计点

🧑‍🔧 Message Passing Systems(消息传递系统)
比如 MPI(Message Passing Interface)

  • 程序员手动控制数据传输、任务调度
  • 常用于科学计算、模拟类应用

缺点:

  • 编程复杂(需要手动处理并发、同步)
  • 容错性差(一个节点挂掉,全盘失败)
  • 不适合动态大规模分布式系统

MapReduce 优势:

  • 自动分发任务与数据
  • 自动重试失败任务
  • 容错、调度机制隐藏在框架里

🧑‍🏫 2. 编程模型的灵感来源
📚 Lisp、Functional Programming 的 Map 和 Reduce
“Map”和“Reduce”其实来自函数式编程语言 Lisp 的标准操作:

  • map(f, list):对列表中每个元素应用函数 f
  • reduce(f, list):将列表聚合为一个值(如求和)

作者把这个小而美的思想推广到了分布式系统中:

  • 把一个“大列表”切成几千块,每块并发 map
  • 最后汇总(reduce)各部分结果

创新点在于:

不是函数名的新瓶装旧酒,而是加上了调度、分布式运行、容错、持久化、分区等“工程魂”。
把“函数式思想”变成了“工业级工具”。

MapReduce 是在 Google 内部“全家桶式架构”中运行的,依赖以下底层支撑:

系统 作用
GFS(Google File System) 存储海量数据块,支持副本、高可用
Bigtable 类似 NoSQL 的结构化数据存储
Scheduler + Monitoring 提供任务调度与健康监控能力

一些其他的系统:

系统 简介 特点
Hadoop MapReduce Apache 开源实现 模仿 Google MapReduce,支持 HDFS
Dryad(微软) 更灵活的数据流图模型 支持 DAG,但复杂度也更高
Spark 更快的内存计算模型 适合交互式、大规模迭代任务
Flink 强实时数据处理 支持流+批,语义更强
Beam 通用数据处理 API 可部署到 Spark/Flink 等系统之上

对比:

角度 MapReduce 相比如何?
与并行数据库相比 更灵活、可扩展、面向通用计算
与消息传递系统相比 更易用、具备自动容错
与函数式编程相比 加入工程实现,能在真实集群跑
与后续系统相比 是“大数据系统”的思想源头,影响深远

MapReduce 的贡献不是提出了什么新理论,而是把“分布式计算”这件复杂的事做得像“写两个函数”那么简单,并真正让它在几千台机器上跑起来。

为什么这门课要使用GO语言

这门课之前使用过C++进行。

使用GO的原因如下:

  1. GO有现成的RPC包,而C++没有。
  2. GO有线程和垃圾回收的支持。而C++需要自己管理内存进行垃圾回收。
    • 因此,GO更加安全。
    • GO更加简单。
    • GO更不容易出错。
  3. GO更加简单,错误处理也更加容易,C++的错误信息很难看出来是什么错误。

这里指的线程是GO的协程。GO Routine。

线程是分布式最大的难题。

使用线程的原因:

  • IO并发:不同的程序可以处于不同的状态。比如A线程在读取磁盘信息,而B线程在执行计算。比如很多线程发送了RPC请求,等待请求响应后进行处理。
  • 多核并行性:当遇到大量的计算任务时,使用多线程同时计算会显著提高效率。两个线程会同时运行在不同的CPU上面。
  • 便捷性:可能你就是希望在后台执行某些操作,或者定时执行某些操作。比如master用来确认其他的线程的存活状态,定时每秒发送一个请求这样。

事件驱动:除了使用线程以外,还可以使用事件驱动来实现。

  • 优点:事件驱动的实现比线程更加高效,更好调试,可以用顺序的方式来编程。
  • 缺点:事件驱动只能实现并发性,而不能实现多核的并行性。无法发挥多核性能。
    • 当然了,可以通过在每个核心上启动一个线程来实现事件驱动来发挥多核性能。

线程开发的挑战:

  1. 内存共享:多个线程共享同样的内存数据,会产生数据竞争
    • 解决办法1:使用锁,但是这会导致锁开销,还要解决可能得死锁问题。有些内部的数据结构可能并不需要锁,但是你不得不支付锁的开销。这并不总是一个好主意。
    • 解决办法2:使数据不共享。
  2. 协调:当我们使用锁的时候,涉及的不同线程可能不知道其他线程的存在,他们只是想不在任何人干扰的情况下获取数据。但也有时候,你希望线程知道其他线程的存在,比如一个线程等待另一个线程完成后读取它的数据。
    • 可以使用Channels来通信
    • 使用条件变量来通信
    • 使用Wait Group来通信
  3. 死锁:两个线程互相等待对方释放锁。

GFS(谷歌文件系统)

主要是Big Storage大型分布式存储系统。为分布式系统提供底层的存储功能。

将存储的数据放到多个机器上面。

有趣的循环:

  • 性能:将数据分散到多个机器上面。通常叫做分片
  • 错误:当数据分散到多个机器上面,其中一个机器就有可能宕机,因此需要容错性
  • 容错性:可以通过存储多个副本来解决容错性的问题。
  • 副本:存储多个副本又会引入数据不一致的问题。
  • 一致性:如果需要数据强一致性。又会需要牺牲性能。

强一致性

来一个小示例:

对于一个简单的服务器来说,没有分布式的功能。

这个时候两个客户端同时发来了请求。

  • C1 写入x的值为1
  • C2 写入x的值为2

请问,这个时候服务器的x值应该是多少?

  • 答案是不确定。

但是对于后面的所有读请求来说,x的值要么都是1,要么都是2.

对于分布式系统来说,只要能达到这个效果即可。

复制版本1(不好的复制设计)

对于最简单的复制来说,就是直接启动两个服务器,S1和S2。

对于所有客户端来说,都分别请求S1和S2进行写入,但是读的时候只读S1.当S1宕机以后读取S2.

这个时候两个客户端同时发来了请求。

  • C1 请求S1 写入x的值为1
  • C2 请求S2 写入x的值为2
  • C1 请求S2 写入x的值为1
  • C2 请求S1 写入x的值为2

客户端写入

那么最终,S1存储x的值为2.S2存储x的值为1.也就产生了数据不一致。所以这是个最简单但也不好的复制设计。

GFS架构

GFS将一个文件分成多个,每个块是64MB大小。每个块都可以存在不同的服务器上面。

有一个主服务器,主服务器负责分发请求,记录了文件名称文件块的映射。

还有多个块服务器。块服务器存储了实际的块数据。

主服务器存储了以下数据:

  • 文件名称和chunk handles数组的映射。也就是每个文件分成了哪些块。这个数据是需要持久化的。
  • chunk handles和chunk servers数组的映射,也就是每个块存在哪些块服务器上。这是因为每个块都是有副本的,所以是一个服务器数组。并且客户端可以选择最近的服务器进行获取。
  • 服务器版本,需要持久化。
  • 是否是主服务器。
  • 任期结束时间。

GFS的持久化

GFS使用Log来记录持久化的信息,并通过check point来进行辅助。恢复的时候只需要从check point恢复就可以了。

其实大多数的存储系统都是这么干的。

GFS读取过程

读取过程

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

MySQL零基础教程

本教程为零基础教程,零基础小白也可以直接学习,有基础的可以跳到后面的原理篇学习。
基础概念和SQL已经更新完成。

接下来是应用篇,应用篇的内容大致如下图所示。

概念学习

ER图

接下来讲讲ER图,这个是在实际工作中也会使用到的东西。

大家应该都听过ER图,全称是(Entity Relationship)实体关系图。

借用维基百科的介绍:

ER模型,全称为实体联系模型、实体关系模型或实体联系模式图(ERM)(英语:Entity-relationship model)由美籍台湾人计算机科学家陈品山发明,是概念数据模型的高层描述所使用的数据模型或模式图。

ER图通常用在设计数据库的阶段,当我们接到一个需求以后,我们要进行一些技术设计,在这个阶段如果涉及到一些对于数据库的修改,我们可以使用ER图进行设计。

万事万物都有两面性,因此我们看一下好处和坏处,如何使用由大家自己权衡。

  • 好处:好处是设计直观、在设计之后也方便给大家讲为什么这样设计,还有留存文档,当后续需要修改设计的时候直接修改即可。
  • 坏处:坏处就是需要花时间去画ER图了。

ER图的基本概念

ER图的主要组成部分

  • 实体(Entity):
    • 实体是数据库中具有相同属性集合的对象。例如,学生、课程、教师等。
    • 在ER图中,实体通常用矩形表示,矩形内写上实体的名称。
  • 属性(Attribute):
    • 属性是实体的特征或性质。例如,学生的属性可以包括学号、姓名、年龄等。
    • 在ER图中,属性通常用椭圆表示,椭圆内写上属性的名称,并用线连接到对应的实体。

简单来讲,实体就是一个表,属性就是表里面的一个字段。

下图就是一个ER图实体管理员用户属性有头像、密码、登录名、ID、邮箱、手机号。

概念学习

  • 关系(Relationship):
    • 关系描述了实体之间的联系。例如,学生和课程之间的关系可以是选修。
    • 在ER图中,关系通常用菱形表示,菱形内写上关系的名称,并用线连接到相关的实体。
  • 关系的类型:
    • 一对一关系(1:1):一个实体与另一个实体之间存在一对一的联系。例如,一个学生对应一个学号。
    • 一对多关系(1:N):一个实体与多个实体之间存在联系。例如,一个教师可以教授多个课程。
    • 多对多关系(M:N):多个实体与多个实体之间存在联系。例如,一个学生可以选修多个课程,一个课程也可以被多个学生选修。

下图就是一个ER图,实体是管理员用户和角色两个。关系拥有,表示管理员用户拥有角色的关系。关系的类型用m和n表示多对多关系。意思是一个管理员用户可以拥有多个角色,一个角色也可以被多个管理员用户拥有。

概念学习

关系这个东西,简单来说就是通过两个字段链接进行实现的。

多对多关系

比如我们有角色表和管理员用户表,这两个表,是多对多关系。

所以我们就需要用一张单独的表来存储这个关系。比如我们新建第三个表叫角色和管理员用户关系表。

角色和管理员用户关系表有字段内容如下:

  • id: 该表的主键。
  • roleId: 角色表的主键。用来关联角色信息。
  • adminId: 管理员用户表的主键。用来关联管理员用户信息。

可以看到,这个关系就是通过角色和管理员用户关系的两个字段来表示的。

角色表内容如下:

id name
1 前台角色
2 行政角色
3 开发角色

管理员用户表内容如下:

id name
1 张三
2 李四

角色和管理员用户关系表内容如下:

根据表的数据我们很清晰的能看出来,张三既拥有前台角色也拥有行政角色,可谓身兼数职。当代牛马。李四则拥有一个开发角色

id roleId adminId
1 1 1
2 2 1
3 1 2

记住上面说的,通过两个字段链接进行实现的。

这里的两个字段就是roleIdadminId

roleId = 1表示的是角色表中,id = 1前台角色,对应的数据是adminId = 1的数据,也就是管理员表中id = 1的管理员张三

如此,我们就知道了,张三拥有角色:前台角色。

一对多关系

一对多关系多对一关系基本一样。就是反过来了。

一个老师可以教授多个课程,比如我们有两个表,一个老师表,一个课程表

一对多关系比较灵活,记住上面说的,通过两个字段链接进行实现的。

先看老师表的数据如下:

id name
1 张老师
2 李老师

课程表的数据如下:

id name teacher_id
1 语文课程 1
2 数学课程 2
3 英语课程 1

为什么说一对多关系比较灵活呢,可以看到,我们这里用到的关联字段是老师表的id和课程表的teacher_id。而不需要再多加一张表了。

这里要记住一个重点!!!!
多出来的这个teacher_id 字段要放在一对多关系中的 多 的这个表里面。

什么是一对多关系中的的关系呢?

比如上面一个老师教多个课程,那么的关系就是课程信息。

当然了,如果你想要多加一张表来存储这两个字段的话也是可以的。

比如增加下表,教授课程表,如果增加了这张表,就可以把课程表中的teacher_id字段删除了:

下表也代表了张老师教授语文课程和英语课程,李老师教授数学课程。

id course_id teacher_id
1 1 1
2 2 2
3 3 1

一对一关系

一对一关系是最简单的。其实算是一对多关系的一个真子集

为什么这么说呢?

我们假设,一个老师只能教授一个课程,那么上面的两个表是不是就变成了一对一关系呢?所以我们说一对一关系是一对多关系的一个真子集。

ER图实践

接下来我们来实践一下ER图。

我这里使用的工具是免费的在线画图工具Processon

多对多关系的ER图

首先来画出多对多关系的ER图吧。

以上面的三个表为例子。

我们需要两个实体和一个关系。

  • 实体1: 管理员实体,管理员实体后面会转化成管理员表。
  • 实体2: 角色实体,角色实体后面会转化成角色表。
  • 关系:两个实体之间的多对多关系,多对多关系后期需要转换成一张关系表。

我们首先画出第一个实体,管理员实体使用矩形表示。如下图。

概念学习

接下来我们给管理员实体增加属性,属性使用椭圆来表示,如下图。

概念学习

现在我们画出第二个实体,角色实体和对应的属性。

概念学习

接下来我们画出关系,关系使用棱形来表示,并且使用线连接两个实体,表示是这两个实体之间的关系。多对多关系还需要在两边分别标记上nm

此外,关系还要有一个名称,用来描述,比如管理员拥有角色,角色被管理员拥有。所以我们可以用拥有关系来描述这两个实体之间的关系。

概念学习

如此,我们就完成了一个多对多关系的ER图。

一对多关系的ER图

接下来再实践一次一对多关系的ER图,最好大家先动手画一次,再跟我们的结果来对比,只有自己实操了才行。

实操时间。。。。。

好了,实操结束。

我们来看一下一对多关系如何画图。

我们需要两个实体和一个关系。

  • 实体1: 教师实体,后面变成教师表。
  • 实体2: 课程实体,后面变成课程表。
  • 关系:两个实体之间的一对多关系,一对多关系可以是一个字段也可以是一个表。

首先画出第一个实体和属性,也就是教师实体。

概念学习

接下来画出第二个实体和属性,也就是课程实体。课程实体这里有一个属性是教师ID,这代表我们使用这个字段,而不是增加了一个关系表。如果我们没有这个字段就代表我们要增加一个关系表。

概念学习

最后,画出关系并连线。这里关系名称我们选择教授,这个其实无所谓。能明白就好。重点是要标明一对多关系,可以看到教师实体上写的是1,课程实体上写的是n,代表一对多关系中,教师是1,课程是多。这个不能写反了。

概念学习

多对一关系的画起来是一样的,需要注意的就是关系上的1和n别写错了。

一对一关系的ER图

一对一关系的ER图和上面的差不多,我们举个例子吧。

  • 实体1: 用户实体,转化成用户表,存储用户信息。
  • 实体2: 用户账户实体,转化成用户账户表,存储用户的账号资金信息。
  • 关系:一对一关系,一个用户只能拥有一个用户账户。

首先画出第一个实体,用户实体。属性包括ID、用户名称、手机号。

概念学习

接下来画出第二个实体,用户账户实体,属性包括ID、账户余额、用户ID。用户ID作为关联关系的字段,和一对多关系一样,就不需要单独加表了。

概念学习

最后,画出关系并连线,标上关系是一对一关系就可以了。

概念学习

总结

通常来说,ER图是在设计阶段完成的,先有ER图再有表结构。

可如果你已经有了表结构,有没有办法生成ER图呢?

也是有方法的,比如著名的Navicat工具,就支持这么做。

此外,还有一个方法,就是使用在线工具dbdiagram,这个工具可以导入现有的SQL,会生成ER图,如下。

概念学习

这个网站是通过左边的一个叫dbml的语言来生成ER图的,也支持直接导入SQL,转化成dbml格式再生成ER图。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!
关注我一起挣大钱!文末有惊喜哦!

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。

什么是领域驱动设计DDD

领域驱动设计(Domain-Driven Design,简称DDD)是由美国软件专家埃里克・埃文斯(Eric Evans)在2004年提出的软件设计方法论,旨在解决复杂软件系统开发过程中业务逻辑与技术实现之间的矛盾,提升软件系统的可维护性、可扩展性和灵活性。

说人话就是:

  • What: 它是一种设计思想、一种指导原则。
  • When: 设计微服务的时候,或者说,不知道怎么拆分微服务的时候。
  • Why:为什么要用它,上面其实说了,不知道怎么拆分微服务的时候,可以用它来指导你如何拆分微服务。
  • How:这个后面讲。

很多人都说,DDD是用来处理复杂业务逻辑的,那多复杂才算复杂业务呢?

这个问题,其实和微服务什么时候用是一个问题。

所有的技术都不是银弹。都有适合它的使用场景。

拿微服务来说,你一个小公司,就两三个开发,硬要上微服务,拆好几个服务出来,有什么意义吗?

是提升性能了?

是增加开发效率了?

都不是,你会发现拆分完以后,程序反而三高了。

  • 高复杂度:程序变得更加复杂了。
  • 高维护成本:程序的维护成本增加了、当有需求需要修改的时候、开发效率反而降低了。
  • 高运维成本:原来一台机器就满足了,你拆的服务多了,一台机器不够了。要么加机器性能要么加机器数量。

所以,适合很重要。

俗话说的好,见人说人话,见鬼说鬼话。技术也一样。

基本概念

  • 实体:使用充血模型实现的实体,既有属性、也有方法。
  • 值对象:只有属性的类。
  • 聚合根:一个特殊的实体,聚合的入口。
  • 聚合:聚合是一个概念、也可以理解成一个模块。聚合内包含了聚合根、实体、值对象。
  • 限界上下文:分割领域的边界、也是分割微服务的边界,通过这个边界明确这个接口属于哪个领域,也就是属于哪个微服务。每个领域有每个领域的上下文。
  • 领域:领域也就是我们的领域模型,也可以是一个微服务。
  • 子领域:一个领域可以分成多个子领域。这个就是粒度的问题了。
  • 领域事件:领域之间通信的方法。通过这个来调用其他的微服务。

还有一些核心领域、支撑领域、通用领域等,都是领域的一种,作用不同而已。

领域

一个领域里面包含了多个子领域,如图所示。

ddd1-3

一个子领域里面包含了多个聚合,每个聚合里又有一个聚合根作为入口,还有若干个实体值对象

ddd1-4

那领域到底是什么?子领域又是什么?我们该如何划分领域?

如果大家看过仙侠小说、电视剧等,应该听过一些词,比如神之领域绝对领域恶魔的领地领主等等。

我们可以简单的把领域理解成领地、封地、画地为牢。

皇上将方圆百里的地分给你了,让你当这片封地的领主,那么这方圆百里就是你的领域了

那服务的领域是什么样子的呢?

同样的,我们把完整的服务想象成中国的领域,然后划分成各个省份。子领域就是每个省份下面的市区,再有子领域就是市区下面的县。

关于子领域也有一些划分,比如

  • 核心领域:核心领域是业务的核心竞争力所在,直接体现业务的独特价值。领域中的核心业务,核心竞争力,领域的重点。比如湖北省这个领域,他的核心子领域就是武汉市
  • 通用领域:通用领域是指那些在多个业务中都可以复用的领域,通常不包含业务特有的逻辑。一个领域中通用的能力,可以放到通用领域。比如湖北省这个领域,他的通用领域就有交通。因为不管是哪个市,下面都会用到交通,交通还可以划分为地铁、公交、高铁、火车等。
  • 支撑领域:既不属于核心领域、也不是通用领域。但是又必不可少的领域。为核心领域提供支持的领域,虽然重要但不直接体现业务的核心价值。

核心领域

特征:

  • 对业务成功至关重要。
  • 具有高复杂性和高价值。
  • 通常需要投入最多的资源和精力。
  • 一个领域中的核心,业务的重点发展对象。

为什么现在很多人开始使用DDD?无外乎是因为DDD更加契合业务,可以随着业务形态的改变,来带动代码的改变。

比如一个电商公司,一开始是以产品质量作为卖点,那么他们的核心领域就是产品质量领域。或者说商品领域。而商品领域再进行划分子领域,他们的核心子领域就是质量领域

而另外一家公司虽然也是电商公司,但是主要依靠广告业务赚钱,那么他们的核心领域就是广告领域

同样的,核心领域是会变化的,比如第一家公司一开始的产品卖点是质量过硬。后来做大了以后主要依靠流量的增涨来扩大收益,那么核心领域就会变为流量领域

在演进到后面,可能如何为用户推荐合适的商品来促成交易就变成了业务重点,那么这个时候的核心领域就是推荐算法领域了。

具体哪个领域是核心领域,需要大家一起讨论、深入了解公司的业务。

这样才能决定出核心领域。

通用领域

特征:

  • 低复杂性,易于复用。
  • 通常可以通过第三方工具或框架实现。
  • 不需要大量定制开发。

通用领域顾名思义,就是提供一些通用能力的领域,比如发送消息、权限认证等等。

这个根据公司的业务来看的。

比如一些公司需要签合同使用电子签章,那么这也算是一个通用的功能,可以是一个通用领域。

再比如常用的限流功能。也可以是一个通用领域。

支撑领域

特征:

  • 中等复杂性和中等价值。
  • 主要用于辅助核心领域的实现。
  • 可以通过一定程度的定制开发满足需求。
  • 支撑领域既不属于核心领域

支撑领域和通用领域不同的点就在于它和业务有一些关系。

比如,对于电商公司来说,核心可能是商品领域,那么为商品领域提供支撑的业务领域就是订单领域库存领域物流领域等。

如何划分不同的领域

  • 关注业务价值
    • 优先识别对业务成功最重要的部分,这通常是核心领域。
    • 核心领域的设计和实现需要投入最多的资源。
  • 关注领域的独立性
    • 每个领域应尽量独立,避免领域之间的强耦合。
    • 独立性强的领域更容易划分为限界上下文(Bounded Context)。
  • 关注领域的复用性
    • 通用领域应尽量复用现有的工具或框架,避免重复开发。
    • 支撑领域可以适当定制,但不应过度复杂化。
  • 动态调整
    • 领域划分并非一成不变,随着业务发展和需求变化,领域的类型可能需要重新评估和调整。

领域的划分是前期的重要工作,只有领域划分好了,后续的路才好走,如果领域划分的不对,那么后续还需要重构,就会比较浪费时间。

一般来说,领域划分都是首先通过事件风暴来进行的,参与整个事件暴风的人包括领域专家、业务专家、研发、架构师、产品等等。

须知人力有尽时,一个人的想法终归是有限的,只有集思广益,才是长久之道。大家在事件风暴之中应该畅所欲言、没有上下级的关系,如此方可,要不然很多人摄于上级的压迫,不敢说话,或是说了以后被上级否定,这样的话是不利于事件风暴进行的。

在事件风暴之中应该鼓励大家发言,不要去盲目的否定,更不要搞什么一言堂。

通过事件风暴我们可以识别出领域、子领域,还有聚合、事件等。

接下来我们就需要判断哪些是核心领域,哪些是通用领域,哪些是支撑领域。

有些人可能会问,为什么要划分出核心领域、支撑领域、通用领域呢?

这是因为大家的精力有限,要把大部分的精力投入到最重要的事情上去,也就是放到核心领域的建设上面。至于通用领域和支撑领域,如果人手不够也可以通过外包解决。

毕竟,核心领域才是最重要的领域。

上面也说过了,公司的不同时期,关注的业务重点不同,那么核心领域也会变化。

同样的,哪怕都是电商公司,他们的核心领域也不一定一样,一切都要看具体的业务。

而我们的重点就是要放在核心领域的建设上面。

总结

我们主要介绍了领域的概念,包括如何划分,还有核心领域、通用领域以及支撑领域等。还介绍了一些DDD的基本概念。

领域的核心思想就是两点

  • 将我们的业务自顶向下的进行细分,逐步的拆解。
  • 划分出不同的领域,将我们有限的精力投入到最重要的事情当中。

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习