2024年3月

1 快速上手

1.1 在项目中使用 npm 包引入

Step 1: 使用命令行在项目目录下执行以下命令

npm install --save @antv/g6

Step 2: 在需要用的 G6 的 JS 文件中导入

import G6 from '@antv/g6';

1.2 在 HTML 中使用 CDN 引入

<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-{$version}/build/g6.js"></script>

注意

2 快速试用

创建一个 G6 的关系图仅需要下面几个步骤:

  • 创建关系图的 HTML 容器;
  • 数据准备;
  • 创建关系图;
  • 配置数据源,渲染。

Step 1: 创建容器

<div id="mountNode"></div>

Step 2: 数据准备

引入 G6 的数据源为 JSON 格式的对象。该对象中需要有节点(nodes)和边(edges)字段,分别用数组表示:

const data = {
  // 点集
  nodes: [
    {
      id: 'node1', // String,该节点存在则必须,节点的唯一标识
      x: 100, // Number,可选,节点位置的 x 值
      y: 200, // Number,可选,节点位置的 y 值
    },
    {
      id: 'node2', // String,该节点存在则必须,节点的唯一标识
      x: 300, // Number,可选,节点位置的 x 值
      y: 200, // Number,可选,节点位置的 y 值
    },
  ],
  // 边集
  edges: [
    {
      source: 'node1', // String,必须,起始点 id
      target: 'node2', // String,必须,目标点 id
    },
  ],
};

注意

  • nodes
    数组中包含节点对象,唯一的
    id
    是每个节点对象中必要的属性,
    x

    y
    用于定位;
  • edges
    数组中包含边对象,
    source
    和 target 是每条边的必要属性,分别代表了该边的起始点
    id
    与 目标点
    id
  • 点和边的其他属性参见链接:
    图元素配置文档

Step 3: 创建关系图

创建关系图(实例化)时,至少需要为图设置容器、宽和高。

const graph = new G6.Graph({
  container: 'mountNode', // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
  width: 800, // Number,必须,图的宽度
  height: 500, // Number,必须,图的高度
});

Step 4: 配置数据源,渲染

创建关系图(实例化)时,至少需要为图设置容器、宽和高。

graph.data(data); // 读取 Step 2 中的数据源到图上
graph.render(); // 渲染图

最终的结果

efa007de18a21082cdb17820d9fd7d2d.jpeg

3 配置

3.1 元素及其配置

图的元素特指图上的节点Node和边Edge,不论是节点还是边,它们的属性分为两种:

  • 样式属性
    style

    :对应 Canvas 中的各种样式,在元素
    状态State
    发生变化时,可以被改变;
  • 其他属性
    :例如图形(
    shape
    )、id(
    id
    )一类在元素
    状态State
    发生变化时不能被改变的属性。
    例如,G6 设定 hover 或 click 节点,造成节点状态的改变,只能自动改变节点的
    样式属性
    (如
    fill

    stroke



    其他属性
    (如
    shape
    等)不能被改变。如果需要改变其他属性,要通过
    graph.updateItem
    手动配置。
    样式属性
    是一个名为
    style
    的对象,
    style
    字段与其他属性并行。

以节点元素为例,其属性的数据结构如下:

{
  id: 'node0',          // 元素的 id
  shape: 'circle',      // 元素的图形
  size: 40,             // 元素的大小
  label: 'node0'        // 标签文字
  labelCfg: {           // 标签配置属性
    positions: 'center',// 标签的属性,标签在元素中的位置
    style: {            // 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行
      fontSize: 12      // 标签的样式属性,文字字体大小
    }
  }
  // ...,                  // 其他属性
  style: {              // 包裹样式属性的字段 style 与其他属性在数据结构上并行
    fill: '#000',       // 样式属性,元素的填充色
    stroke: '#888',     // 样式属性,元素的描边色
    // ...                 // 其他样式属性
  }
}

边元素的属性数据结构与节点元素相似,只是其他属性中多了 source 和 target 字段,代表起始和终止节点的 id

全局配置

适用场景:所有节点统一的属性配置,所有边统一的属性配置。使用方式:使用图的两个配置项:

defaultNode:节点在默认状态下的样式属性(style)和其他属性;
defaultEdge:边在默认状态下的样式属性(style)和其他属性。

defaultNode: {
    size: 30, // 节点大小
    // ...                 // 节点的其他配置
    // 节点样式配置
    style: {
      fill: 'steelblue', // 节点填充色
      stroke: '#666', // 节点描边色
      lineWidth: 1, // 节点描边粗细
    },
    // 节点上的标签文本配置
    labelCfg: {
      // 节点上的标签文本样式配置
      style: {
        fill: '#fff', // 节点标签文字颜色
      },
    },
  },
  // 边在默认状态下的样式配置(style)和其他配置
  defaultEdge: {
    // ...                 // 边的其他配置
    // 边样式配置
    style: {
      opacity: 0.6, // 边透明度
      stroke: 'grey', // 边描边颜色
    },
    // 边上的标签文本配置
    labelCfg: {
      autoRotate: true, // 边上的标签文本根据边的方向旋转
    },
  },

3.2 使用图布局 Layout

当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。G6 提供了 7 种一般图的布局和 4 种树图的布局:

一般图:

  • Random Layout:随机布局;
  • Force Layout:经典力导向布局:
  • Circular Layout:环形布局;
  • Radial Layout:辐射状布局;
  • MDS Layout:高维数据降维算法布局;
  • Fruchterman Layout:Fruchterman 布局,一种力导布局;
  • Dagre Layout:层次布局。

树图布局:

  • Dendrogram Layout:树状布局(叶子节点布局对齐到同一层);
  • CompactBox Layout:紧凑树布局;
  • Mindmap Layout:脑图布局;
  • Intended Layout:缩进布局。
const graph = new G6.Graph({
  ...                      // 其他配置项
  layout: {                // Object,可选,布局的方法及其配置项,默认为 random 布局。
    type: 'force',         // 指定为力导向布局
    preventOverlap: true,  // 防止节点重叠
    // nodeSize: 30,       // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中
    linkDistance: 100,     // 指定边距离为100设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
    center: [500, 300]
  }
});

更多属性(
https://g6.antv.vision/zh/docs/api/graphLayout/guide)

3.3 图的交互 Behavior

G6 中的交互行为。G6
内置
了一系列交互行为,用户可以直接使用。简单地理解,就是可以一键开启这些交互行为:

  • drag-canvas
    :拖拽画布;
  • zoom-canvas
    :缩放画布。

更多详见:
交互行为 Behavior

const graph = new G6.Graph({
  // ...                                          // 其他配置项
  modes: {
    default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
  },
});
  • activate-relations
    含义:当鼠标移到某节点时,突出显示该节点以及与其直接关联的节点和连线;

参数:

  • trigger: 'mouseenter'。可以是 mousenter,表示鼠标移入时触发;也可以是 click,鼠标点击时触发;
  • activeState: 'active'。活跃节点状态。当行为被触发,需要被突出显示的节点和边都会附带此状态,默认值为 active;可以与 graph 实例的 nodeStyle 和 edgeStyle 结合实现丰富的视觉效果。
  • inactiveState: 'inactive'。非活跃节点状态。不需要被突出显示的节点和边都会附带此状态。默认值为 * * * inactive。可以与 graph 实例的 nodeStyle 和 edgeStyle 结合实现丰富的视觉效果;
  • resetSelected:高亮相连节点时是否重置已经选中的节点,默认为 false,即选中的节点状态不会被 activate-relations 覆盖;
{
  type: 'activate-relations',
  activeState: 'actives',
  inactiveState: 'inactives',
  resetSelected: false
},
// 当前节点的多状态样式
nodeStateStyles: {
  actives: {
    opacity: 1,
    lineWidth: 0
  },
  inactives: {
    opacity: 0.2,
    lineWidth: 0
  }
},
edgeStateStyles: {
  actives: {
    opacity: 1
  },
  inactives: {
    opacity: 0.2
  }
}


https://g6.antv.vision/zh/docs/manual/middle/states/defaultBehavior)

3.4 监听和绑定事件

  • 全局事件
    只要在画布上范围内发生均会被触发,如 mousedown,mouseup,click,mouseenter,mouseleave 等。
graph.on('click', (ev) => {
  const shape = ev.target;
  const item = ev.item;
  if (item) {
    const type = item.getType();
  }
});
  • canvas 事件
    只在 canvas 空白处被触发,如 canvas:mousedown,canvas:click 等,以canvas:eventName 为事件名称。
graph.on('canvas:click', (ev) => {
  const shape = ev.target;
  const item = ev.item;
  if (item) {
    const type = item.getType();
  }
});
  • 节点/边/combo 上的事件
    例如 node:mousedown,edge:click, combo:click 等,以 type:eventName 为事件名称。
graph.on('node:click', (ev) => {
  const node = ev.item; // 被点击的节点元素
  const shape = ev.target; // 被点击的图形,可根据该信息作出不同响应,以达到局部响应效果
  // ... do sth
});

graph.on('edge:click', (ev) => {
  const edge = ev.item; // 被点击的边元素
  const shape = ev.target; // 被点击的图形,可根据该信息作出不同响应,以达到局部响应效果
  // ... do sth
});

graph.on('combo:click', (ev) => {
  const combo = ev.item; // 被点击 combo 元素
  const shape = ev.target; // 被点击的图形,可根据该信息作出不同响应,以达到局部响应效果
  // ... do sth
});
  • 图形上的事件
    指定图形上的事件,如 circle-shape:mousedown,circle-shape:click 等,以 shapeName:eventName 为事件名称。可用于绑定节点/边/combo 中对局部图形做出响应的场景。效果类似上文 graph.on('node:click', fn) 中通过 target 信息作出不同响应。

关于图形的 name:

内置节点/边/combo 上每个图形的名称在开发过程中可以通过 graph.on('node:click', (ev) => console.log(ev.target.get('name'))) 得知;

自定义节点/边/combo 中通过 addShape 增加的图形,可添加与 attrs 平级的 name 字段指定任意(同元素中唯一)字符串作为 name。请注意同个元素(节点/边/combo)中不同图形尽量给予不同的 name 值。

下面例子为图中所有 name 为 circle-shape 的图形绑定了 click 事件监听:

graph.on('circle-shape:click', (ev) => {
  const shape = ev.target; // 被点击的图形
  // ... do sth
});
  • 时机事件

时机事件指渲染、视口变换、元素增删改、数据变换等时机。所有时机事件详见
G6 的时机事件列表
。如:
beforeadditem

afteradditem
等:

  • 节点/边/Combo 状态改变时的事件:
    beforerefreshitem

    afterrefreshitem
  • 布局时机:
    beforelayout

    afterlayout

下面例子为 graph 绑定了渲染完成时机的监听。时机事件中,afterrender、afterlayout 一类事件必须在
graph.render()

graph.read()
之前绑定,方可监听到首次渲染、布局完成后的相关事件。

graph.on('afterrender', (ev) => {
  // ... do sth
});
  • 自定义事件

G6 允许用户自定义任意事件,可在任意位置通过
graph.emit(customEventName: string, event: IG6GraphEvent)
触发一个事件,第一个参数为自定义事件名称。在触发前,通过
graph.on(customEventName: string, callback: Function)
进行监听。例如:

graph.on('some-custom-event-name', (ev) => {
  // ... do sth
});
graph.emit('some-custom-event-name', {
  // some params
})

ps:当数据多样化的时候,可以对不同的数据设置不同的节点或者边的样式

data.nodes.forEach((node) => {
  node.style = {
    fill: node.type === 'company'
      ? '#EE6666665'
      : node.type === 'director'
        ? '#0F8CFF'
        : '#FFC510',
    stroke: node.type === 'company'
      ? '#EE6666665'
      : node.type === 'director'
        ? '#0F8CFF'
        : '#FFC510'
  }
})
data.edges.forEach((edge) => {
  edge.style = {
    stroke: edge.typest === 0
      ? '#EE6666665'
      : edge.typest === 1
        ? '#0F8CFF'
        : '#FFC510'
  }
  edge.label = edge.typest === 0
    ? '董事'
    : edge.typest === 1
      ? '经理'
      : edge.typest === 2
        ? '职员'
        : null
  edge.labelCfg = {
    style: {
      fill: edge.typest === 0
        ? '#EE6666665'
        : edge.typest === 1
          ? '#0F8CFF'
          : '#FFC510'
    }
  }
})

简介

在SQL Server数据库系统中,管理用户权限和角色对于确保数据安全、完整性和可访问性至关重要。在本文中,我们将探讨在SQL Server数据库中创建用户、分配权限和管理角色的过程。我们将涵盖基本概念,并提供带有SQL代码片段的实际示例。

引言

用户管理涉及创建用户帐户、分配适当的权限以及将用户组织到角色中。角色通过将具有相似访问级别的用户分组来简化权限管理。很多人在实际的项目实践中,不会新建用户或者分配角色权限有问题,本文采用SQL代码操作,提供简便的操作方法,新建用户并为其分配角色和权限。

创建服务器登录名

当创建服务器登录名以后,该账号将可以登录服务器,初始化状态下的登录名是没有任何权限的,仅可进入服务器


--创建服务器
USEmasterCREATE LOGIN USER1 WITH PASSWORD = '123456';

View Code

将用户映射到指定数据库

当用户登录数据库服务器以后,仅可看见已映射的数据库


--将用户映射到数据库
USEStudent;CREATE USER USER1 FOR LOGIN USER1 ;

View Code

创建数据库角色

创建你要要的角色名,并选择dbo架构,为分配角色给用户做准备


--创建数据库角色
CREATE ROLE UserViewRole AUTHORIZATION dbo;

View Code

分配权限

权限控制用户可以在特定数据库对象上执行的操作。权限可以在数据库级别或在诸如表、视图或存储过程等单个对象上授予。本文简单示例查询权限,将权限添加到角色。


--添加对目标表的 SELECT 权限
GRANT SELECT ON [dbo].[Student] TOUserViewRole;GRANT SELECT ON [dbo].[Interface] TO UserViewRole;

View Code

分配角色

将用户添加到角色:


--将用户添加到角色
EXEC sp_addrolemember N'UserViewRole', N'Use1';

View Code

完成以上操作,将完成用户新建与角色、权限的分配操作,一下附加不添加角色,直接赋予权限的操作:

--数据库级别权限--在数据库级别授予权限:

---- 在数据库上授予SELECT权限--GRANT SELECT TO User1;

---- 在数据库中的所有存储过程上授予EXECUTE权限--GRANT EXECUTE TO User1;--对象级别权限--在特定对象上授予权限:

---- 在表上授予SELECT权限--GRANT SELECT ON TableName TO User1;

---- 在存储过程上授予EXECUTE权限--GRANT EXECUTE ON StoredProcedureName TO User1;

结论

管理用户权限和角色对于维护数据库安全性并确保用户对数据具有适当访问权限至关重要。通过遵循最佳实践并利用SQL Server的强大功能来管理用户,您可以有效地控制对数据库资源的访问。

在本文中,我们介绍了在SQL Server中创建用户、分配权限和管理角色的基础知识。请记住定期审查和更新用户权限,以符合您组织的安全政策和数据访问要求。

需求背景:

mysql存储的一个字段,需要同步到elasticsearch,并存储为数组,以便于查询。

如下例子,就是查询预期。

PUT /t_test_1/_doc/1{"name":"苹果","code":[1,2,3]
}

PUT
/t_test_1/_doc/2{"name":"香蕉","code":[1,2]
}

PUT
/t_test_1/_doc/3{"name":"橙子","code":[1,3,5]
}

# 查询code包含1或者3中任意一个或者所有coe都包含的数据
POST
/t_test_1/_search
{
"query": {"bool": {"filter": [
{
"terms": {"code": ["1","3"]
}
}
]
}
}
}

数据同步背景介绍:

1、当前数据同步使用的是kafka + confluent
2、各种数据产品中间件版本:

MySQL5.7.x
es7.15.1
kafka3.2.0
Debezium MySQL connector plug-in 1.9.4.Final
confluentinc-kafka-connect-elasticsearch:13.1.0

详细内容可参考之前的博文

经过调研,找到两种方法实现:

1、第一种使用elasticsearch自带的ingest pipeline处理。话不多少,直接上code。

(1)首先mysql字段num_array设计成varchar,存储格式为'a,b,c,d,e,f';

(2)同步到es变成数组,es对应的字段num_array设计成keyword,便于精确查询,提高性能。

# 创建一个pipeline
PUT _ingest/pipeline/string_to_array_pipeline
{
  "description": "Transfer the string which is concat with a separtor  to array.",
  "processors": [
    {
      "split": {
        "field": "num_array",
        "separator": ","
      }
    },
    {
      "set": {
        "field": "update_user",
        "value": "system"
      }
    },
    {
      "set": {
        "field": "name",
        "value": "华山"
      }
    }
  ]
}
##t_mountain 设置字段num_array的mapping为keyword,设置default_pipeline =string_to_array_pipeline
PUT /t_mountain
{
  "settings": {
    "default_pipeline": "string_to_array_pipeline"
  }, 
   "mappings" : {
      "date_detection" : false,
      "properties" : {
        "altitude" : {
          "type" : "float"
        },
        "create_time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis"
        },
        "create_user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "desc" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "long"
        },
        "latitude" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "location" : {
          "type" : "geo_point"
        },
        "logtitude" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "num_array" : {
          "type" : "keyword"
        },
        "ticket" : {
          "type" : "float"
        },
        "update_time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis"
        },
        "update_user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
}
#查询
POST t_mountain/_search { "query": { "bool": { "filter": [ { "terms": { "num_array": [ "a", "b" ] } } ] } } }

2、使用debezium自定义转换实现json字符串转数组

详细内容参考gitee:
debezium-custom-converter

或参考github:
debezium-custom-converter


简单来说就是mysql存储成varchar格式:比如  'a,b,c,d,e'  或者   '["a","b","c","d"]'   经过自定义converter处理转成list,同步到es就是数组格式,es的所有设置和方法1相同。

------------------------数据同步过程中出现了一个很让人费解的现象---------------------

1、 数据同步过程中出现了一个很让人费解的现象:mysql的insert语句可以正常同步的es,且pipeline能正常应用,字段被处理成了数组格式。
但是,在mysql中update刚才插入的数据,则数据正常同步到了es,但是也只的default_pipeline没有生效。

2、排查发现是因为同步sink脚本中设置了"write.method":"upsert"导致mysql执行update语句时,目标索引设置的pipeline失效了。

现象令人费解!!!

3、
查看官方文档
,没有看出来这个解释,怎么就影响到索引设置的pipeline了

Method used for writing data to Elasticsearch, and one of INSERT or UPSERT. The default method is INSERT in which the connector constructs a document from the record value and inserts that document into Elasticsearch, completely replacing any existing document with the same ID; this matches previous behavior. The UPSERT method will create a new document if one with the specified ID does not yet exist, or will update an existing document with the same ID by adding or replacing only those fields present in the record value. The UPSERT method may require additional time and resources of Elasticsearch, so consider increasing the read.timeout.ms and decreasing the batch.size configuration properties.

用于向 Elasticsearch 中写入数据的方法,以及 INSERT 或 UPSERT 中的一种。默认方法是 INSERT,连接器会根据记录值构建一个文档,并将该文档插入 Elasticsearch,同时完全替换具有相同 ID 的现有文档;这与以前的行为一致。如果指定 ID 的文档还不存在,UPSERT 方法将创建一个新文档,或更新具有相同 ID 的现有文档,只添加或替换记录值中存在的字段。UPSERT 方法可能需要额外的时间和 Elasticsearch 资源,因此请考虑增加 read.timeout.ms 和减少 batch.size 配置属性。

4、遂在本地模拟同步数据,复现了上述现象。然后打开es详细日志追踪:

 bin/elasticsearch -E logger.org.elasticsearch.action=trace

先贴出来同步脚本:

source脚本

{
"name": "goods-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "127.0.0.1",
"database.port": "3306",
"database.user": "debezium_user",
"database.password": "@debezium2022",
"database.server.id": "12358",
"snapshot.mode": "when_needed",
"database.server.name": "goods",
"database.include.list": "goods",
"table.include.list": "goods.t_mountain,goods.t_sku,goods.t_spu",
"database.history.kafka.bootstrap.servers": "127.0.0.1:9092",
"database.history.kafka.topic": "dbhistory.goods",
"include.schema.changes": "true"
}
}

sink脚本:

{
"name": "elasticsearch-sink",
"config": {
"connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
"tasks.max": "1",
"topics": "goods.goods.t_sku,goods.goods.t_spu,goods.goods.t_mountain,goods001.goods.t_format_date",
"key.ignore": "false",
"connection.url": "http://127.0.0.1:9200",
"name": "elasticsearch-sink",
"type.name": "_doc",
"decimal.handling.mode": "string",
"transforms": "unwrap,key",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
"transforms.unwrap.drop.tombstones": "true",
"transforms.unwrap.delete.handling.mode": "drop",
"transforms.key.type": "org.apache.kafka.connect.transforms.ExtractField$Key",
"transforms.key.field": "id"
}
}

可以发现,此时的sink脚本未设置
"write.method":"upsert",那么该设置的默认值为insert

日志如下:

mysql insert语句,同步到es日志,操作是
index
(下面日志中我也做了标记)
,这是
已经经过索引设置的pipeline处理后的数据

[2024-03-25T01:22:16,232][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] send action [indices:data/write/bulk[s][p]] to local primary [[goods.goods.t_mountain][0]] for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]] with cluster state version [2312] to [NYz8ptioSBGMQoSB94VGew] 
[2024-03-25T01:22:16,233][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] [[goods.goods.t_mountain][0]] op [indices:data/write/bulk[s]] completed on primary for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]
[2024-03-25T01:22:16,235][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] operation succeeded. action [indices:data/write/bulk[s]],request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]

查看elasticsearch源码,从IndexRequest.java的toString方法可以看出,日志输出的是哪些内容。

从以上日志可以
模拟出es的插入DSL
,这是
已经经过索引设置的pipeline处理后的数据


POST _bulk
{"
index
":{"_id":"154","_index":"t_mountain"}}

{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}

mysql update语句,同步到es日志,操作是
index
(下面日志中我也做了标记)
,这是
已经经过索引设置的pipeline处理后的数据

[2024-03-25T01:23:18,419][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] send action [indices:data/write/bulk[s][p]] to local primary [[goods.goods.t_mountain][0]] for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711329798000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]] with cluster state version [2312] to [NYz8ptioSBGMQoSB94VGew] 
[2024-03-25T01:23:18,421][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] [[goods.goods.t_mountain][0]] op [indices:data/write/bulk[s]] completed on primary for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711329798000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]
[2024-03-25T01:23:18,422][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] operation succeeded. action [indices:data/write/bulk[s]],request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][154], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711329798000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]

从以上日志可以模拟出es的插入DSL,和上面的类似。都是插入数据。

POST _bulk
{"index":{"_id":"154","_index":"t_mountain"}}
{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":154,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}

5、修改sink脚本,添加一行配置
"write.method":"upsert",重启kafka-connect,继续数据同步

日志如下:

5.1、mysql insert语句,同步到es日志,操作是
update(下面日志中我也做了标记)
,可以看到数据分别存储在
doc对象
(未经索引设置的pipeline处理的原始数据)和
upsert对象
(经过索引设置的pipeline处理的原始数据)

中:

[2024-03-22T05:44:42,500][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] send action [indices:data/write/bulk[s][p]] to local primary [[goods.goods.t_mountain][0]] for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [update {[goods.goods.t_mountain][_doc][151], doc_as_upsert[false], doc[index {[null][_doc][null], source[{"id":151,"name":"少华山","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200.0,"create_user":"666666666666","create_time":1710419563000,"update_user":null,"update_time":1710420074000,"ticket":0.0,"desc":"少华山在陕西渭南华州区","num_array":"aaaaaa,b,ccccccc"}]}], upsert[index {[null][_doc][null], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}], scripted_upsert[false], detect_noop[true]}]] with cluster state version [1858] to [NYz8ptioSBGMQoSB94VGew] 
[2024-03-22T05:44:42,498][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] [[goods.goods.t_mountain][0]] op [indices:data/write/bulk[s]] completed on primary for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][151], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]
[2024-03-22T05:44:42,500][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] operation succeeded. action [indices:data/write/bulk[s]],request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][151], source[{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]

从elasticsearch源码的UpdateRequest.java类tostring方法中,可以看出日志输出的是哪些内容:

从以上日志可以模拟出es的插入DSL,可以正常同步数据,且经过索引设置的pipeline处理过的数据,正确同步到了es中。

----下面upsert中内容正常保存到了es中,符合预期。

POST _bulk
{"
update
":{"_id":"151","_index":"t_mountain"}}
{"doc":{"id":151,"name":"少华山","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200.0,"create_user":"666666666666","create_time":1710419563000,"update_user":null,"update_time":1710420074000,"ticket":0.0,"desc":"少华山在陕西渭南华州区","num_array":"aaaaaa,b,ccccccc"},"
upsert
":{"altitude":1200.0,"num_array":["aaaaaa","b","ccccccc"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1710420074000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"},"doc_as_upsert":false,"scripted_upsert":false, "detect_noop":true}

5.2、mysql update语句,同步到es日志,操作是
update(下面日志中我也做了标记)
,可以看到
数据分别存储在
doc对象
(未经索引设置的pipeline处理的原始数据)和
upsert对象
(经过索引设置的pipeline处理的原始数据)

中:

[2024-03-22T05:49:56,606][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] send action [indices:data/write/bulk[s][p]] to local primary [[goods.goods.t_mountain][0]] for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [update {[goods.goods.t_mountain][_doc][151], doc_as_upsert[false], doc[index {[null][_doc][null], source[{"id":151,"name":"泰山","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200.0,"create_user":"666666666666","create_time":1710419563000,"update_user":"666666","update_time":1711086596000,"ticket":0.0,"desc":"少华山在陕西渭南华州区","num_array":"a,b,c,d,e,f,g,h"}]}], upsert[index {[null][_doc][null], source[{"altitude":1200.0,"num_array":["a","b","c","d","e","f","g","h"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711086596000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}], scripted_upsert[false], detect_noop[true]}]] with cluster state version [1927] to [NYz8ptioSBGMQoSB94VGew] 
[2024-03-22T05:49:56,610][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] [[goods.goods.t_mountain][0]] op [indices:data/write/bulk[s]] completed on primary for request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][151], source[{"altitude":1200.0,"num_array":"a,b,c,d,e,f,g,h","create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711086596000,"update_user":"666666","name":"泰山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]
[2024-03-22T05:49:56,613][TRACE][o.e.a.b.TransportShardBulkAction] [esserver001-9200] operation succeeded. action [indices:data/write/bulk[s]],request [BulkShardRequest [[goods.goods.t_mountain][0]] containing [index {[goods.goods.t_mountain][_doc][151], source[{"altitude":1200.0,"num_array":"a,b,c,d,e,f,g,h","create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711086596000,"update_user":"666666","name":"泰山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"}]}]]

从以上日志可以模拟出es的插入DSL:

----下面doc中内容正常保存到了es中,upsert中的内容未更新到es中,问题就出在这里!!!!!!

POST _bulk
{"
update
":{"_id":"151","_index":"t_mountain"}}
{"
doc
":{"id":151,"name":"泰山","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200.0,"create_user":"666666666666","create_time":1710419563000,"update_user":"666666","update_time":1711086596000,"ticket":0.0,"desc":"少华山在陕西渭南华州区","num_array":"a,b,c,d,e,f,g,h"},"
upsert
":{"altitude":1200.0,"num_array":["a","b","c","d","e","f","g","h"],"create_time":1710419563000,"ticket":0.0,"latitude":"34.497647","logtitude":"110.073028","update_time":1711086596000,"update_user":"system","name":"华山","location":"34.497647,110.073028","id":151,"create_user":"666666666666","desc":"少华山在陕西渭南华州区"},"doc_as_upsert":false,"scripted_upsert":false, "detect_noop":true}

elasticsearch更新,doc里面的内容是部分更新,设置了几个字段就更新几个字段,不会覆盖doc未设置的字段。doc和upsert都存在的情况下,如果指定更新的文档不存在,则“If the document does not already exist, the contents of the upsert element are inserted as a new document.”,所以本例中5.1同步数据符合预期。如果指定的文档存在,
doc和upsert都存在的情况下,则doc里面的内容更新到es中,所以本例中5.2同步数据不符合预期,upsert中经过索引设置的pipeline处理过的数据为正确同步更新到es中。这就是bug出现的原因。

总结:

1、合理设置参数,"write.method":"upsert" 、"write.method":"insert"

设置了参数"write.method":"upsert"。mysql的update语句执行到es就是走的bulk部分更新,就会导致索引设置的pipeline失效,官方文档没看到特殊说明,这一块描述不是很清晰,但仔细查看,能略显端倪。

confluent这个设置看github上说的是为了避免数据被覆盖。所以部分更新的情况就设置为upsert。如果同步的时候不管是新增还是更新,source读取的数据都是包含完整字段的,就不存在数据被覆盖的问题,就可以设置成insert。

因为是通过debezium+kafka cnnect+confluent同步数据,无法设置elasticsearch批量更新的参数,因此在设置上述参数时要慎重,通常mysql同步数据到es设置成insert就可以了,不存在更新数据时旧数据被覆盖的情况。

如果不能修改sink脚本配置,那么可以创建一个单独的sink脚本只处理这一张设置了指定pipeline的索引,同时配合es索引设置的alias,亦可轻松解决问题。

2、如果是直接操作elasticsearch的更新,可以直接设置doc或者,upsert等对象。结合"doc_as_upsert"参数也可以完成数据的正确更新。

# 创建一个pipeline
PUT _ingest/pipeline/string_to_array_pipeline
{
  "description": "Transfer the string which is concat with a separtor  to array.",
  "processors": [
    {
      "split": {
        "field": "num_array",
        "separator": ","
      }
    },
    {
      "set": {
        "field": "update_user",
        "value": "system"
      }
    },
    {
      "set": {
        "field": "name",
        "value": "华山"
      }
    }
  ]
}


##t_mountain
PUT /t_mountain
{
  "settings": {
    "default_pipeline": "string_to_array_pipeline"
  }, 
   "mappings" : {
      "date_detection" : false,
      "properties" : {
        "altitude" : {
          "type" : "float"
        },
        "create_time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis"
        },
        "create_user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "desc" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "long"
        },
        "latitude" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "location" : {
          "type" : "geo_point"
        },
        "logtitude" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "num_array" : {
          "type" : "keyword"
        },
        "ticket" : {
          "type" : "float"
        },
        "update_time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis"
        },
        "update_user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
}
#  #A 部分buck更新t_mountain
POST _bulk
{"update":{"_id":"155","_index":"t_mountain"}}
{"doc":{"id":155,"name":"泰山4","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200,"create_user":"666666666666","create_time":1710419563000,"update_user":"777777","update_time":1711086596000,"ticket":0,"desc":"少华山在陕西渭南华州区","num_array":"a,b,c,d"}}
#  #B  buck插入新数据t_mountain
POST _bulk
{"index":{"_id":"155","_index":"t_mountain"}}
{"id":155,"name":"泰山3","location":"34.497647,110.073028","latitude":"34.497647","logtitude":"110.073028","altitude":1200,"create_user":"666666666666","create_time":1710419563000,"update_user":"777777","update_time":1711086596000,"ticket":0,"desc":"少华山在陕西渭南华州区","num_array":"a,b,c,d"}
GET t_mountain/_search?version=true { "query": { "term": { "id": { "value": "155" } } } }

--上面例子说明:
1、先执行#B,正常写入,索引设置的pipeline生效,再执行#A,正常更新,但是索引设置的pipeline没有生效。
(完美复现上面问题)

2、先执行#A,此时报错: "reason" : "[_doc][155]: document missing",因为默认的"doc_as_upsert":false设置是false,将这个修改为true,正常写入数据。且索引设置的pipeline正常执行。

3、先执行#B,正常写入,索引设置的pipeline生效,再执行#A,此时设置"doc_as_upsert":true,正常更新,索引设置的pipeline正常执行。

----在这个请求中,"doc_as_upsert":true指示Elasticsearch,如果指定的文档不存在,就将doc部分的内容作为新文档插入。这样,你就不会因为文档不存在而收到错误,而是会创建一个新的文档。

好了,"doc_as_upsert":true这个参数就可以帮助我们灵活处理elasticsearch更新相关的需求.

参考文档:
elasticsearch官方文档update

elasticsearch官方文档bulk
kafka-connect-elasticsearch

confluent文档

写在开头

最近发现自己记忆力严重下滑,很多sql命令,linux命令都记不住,特别是linux命令,很多命令参数很多,一段时间不用,再去使用就需要从网上重查了,很烦人,为此花了一些时间把之前笔记中的Linux命令给整理了一下,汇总出30个常用的分享出来,下次再想不起来直接看这篇文章就行了。

1、Linux指令-ls

list 的缩写,通过 ls 命令不仅可以查看 linux 文件夹包含的文件,而且可以查看文件权限(包括目录、文件夹、文件权限)、查看目录信息等等。

ls -a 列出目录下所有文件,包含以.开始的隐藏文件
ls -A 列出除.及..的其他文件
ls -r 文件以反序排列
ls -t 文件以修改时间排序
ls -S 文件以大小排序
ls -h 以易读大小显示
ls -l 除了文件名外,还将文件的权限、所有者、文件大小等详细信息显示出来
ls -lhrt 按照易读方式按时间反序排序,并显示文件详细信息
ls -lrS 按大小反序显示文件详细信息
ls -l t* 列出当前目录中所有以“t”开头的目录的详细内容

2、Linux指令-cd

切换目录,是changedirectory的缩写

cd / 进入指定的目录
cd ~ 进入home目录
cd - 进入上一次工作路径
cd .. 切换到上一级目录

3、Linux指令-pwd

该命令用于查看当前工作目录的路径, print work directory的缩写

pwd 查看当前目录路径
pwd -P 查看软连接的实际路径

4、Linux指令-mkdir

该指令用于创建目录,是make directory的缩写

mkdir t 当前工作目录下创建名为t的文件夹
mkdir -m 对新建的目录设置权限,当然也可以用chmod命令赋权
mkdir -p 若路径中的某些目录尚不存在,系统会自动创建
mkdir -p /tmp/test/t 在tmp目录下创建路径为test的目录,
	     在test目录下创建t目录

5、Linux指令-rm

rm是remove的缩写,指删除一个目录中的一个或多个文件或目录。如果没有使用 -r 选项,则 rm 不会删除目录。如果使用 rm 来删除文件,通常仍可以将该文件恢复原状。

命令语法:rm [选项] 文件..
rm -f 强制删除,忽略不存在的文件,不给出提示
rm -i 交互式删除,删除前给提示
rm -r 递归删除目录下文件,以及子目录下的文件
rm -i *.log 删除任何.log文件,删除前逐一询问确认
rm -rf test 强制删除test目录或文件,无需确认

6、Linux指令-rmdir

rmdir是remove directory的缩写,指删除空目录

rmdir -v 删除时有提示
rmdir -p 如果一个目录和子目录皆为空,则在删除子目录时,父目录一并删除
rmdir -p a/b/c 等同于rmdir a/b/c a/b a

7、Linux指令-mv

mv是move的缩写,指移动文件、目录,移动时可修改文件或目录名

命令语法:mv [选项] 源文件或目录 目标文件或目录
mv test.txt test2.txt 将文件test.txt重命名为test2.txt
mv log.txt log2.txt /log 将log.txt、log2.txt文件移动到/log目录中
mv -i log.txt log2.txt 将文件log.txt更名为log2.txt,如果log2.txt已经存在,则询问是否覆盖。
mv -f log.txt log2.txt 将文件log.txt更名为log2.txt,如果log2.txt已经存在,直接覆盖。
mv * ../ 移动当前文件夹下的所有文件到上一级目录

8、Linux指令-cp

复制,将多文件或目录复制至目标目录(shell 脚本中不加 -i 参数会直接覆盖不会提示)

常用指令:
-i 提示
-r 复制目录以及目录内所有的文件
-a 复制的文件与原文件时间一样
cp -ai a.txt test 复制a.txt文件到test目录下,保持原文件时间,如果test目录中已经存在a.txt文件,提示是否覆盖。
cp -s a.txt a_link.txt 为a.txt文件创建一个链接

9、Linux指令-cat

文本输出命令

cat filename 一次显示整个文件
cat > filename 从键盘创建一个文件
cat file1 file2 > file 将几个文件合并为一个文件
-b 对非空输出行号
-n 输出所有行号
cat -n log1.log log2.log 把log1.log的文件内容加上行号后输入到log2.log文件里
cat -b log1.log log2.log log.log 把log1.log和log2.log的文件内容加上行号(空白行不加)附加到log.log里。

10、Linux指令-more

阅读命令,与 cat 类似, more 会以一页一页的显示方便逐页阅读,按空格键(space)就往下一页显示,按 b 键就会往回(back)一页显示。

命令参数:
+n 从第n行开始显示
-n 定义屏幕大小为n行
+/pattern 在每个档案显示前搜寻该(pattern)字符串,然后从该字串前两行之后开始显示
-c 从顶部清屏,然后显示
-s 把连续的多个空行显示为一行
-u 把文件中的下划线去掉

常用的操作命令:
Enter 向下n行,需要定义。默认为1行
Ctrl+F 向下滚动一屏
空格键 向下滚动一屏
Ctrl+B 返回上一屏
= 输出当前行的行号
:f 输出文件名和当前行的行号
!命令 调用shell,并执行命令
q	退出more
more +3 text.txt 显示文件中从第三行起的内容
ls -l | more -5 以每次显示5行数据的量,列出当前目录下的所有文件详细信息

11、Linux指令-less

浏览文件命令,less 可以随意浏览文件,less 在查看之前不会加载整个文件

常用参数:
-i 忽略搜索时的大小写
-N 显示每行行号
-o <文件名> 将less输出的内容在指定的文件中保存起来
-s 显示连续空行为一行
/字符串 向下搜索“字符串”的功能
?字符串 向上搜索“字符串”的功能
-x <数字> 将tab键显示为规定的数字空格
空格键 滚动一行
回车键 滚动一页
[pagedown] 向下发动一页
[pageup] 向上翻动一页

ps -aux | less -N  ps查看进程信息并通过less分页显示,显示行号
less a.log b.log 查看多个文件,可以使用n查看下一个,使用怕p查看前一个

12、Linux指令-tail

用于显示指定文件末尾内容,不指定文件时,作为输入信息进行处理,常用来查看日志文件

常用参数:
-f 循环读取(常用于查看程序执行后递增的日志文件)
-n<行数> 显示行数(从后向前)
tail -n 100 显示后100行日志文件
tail -f log.log 循环读取日志文件逐渐增加的内容
tail -200f log.log 循环读取日志文件后200行的内容

13、Linux指令-head

显示开头或结尾命令
head 用来显示档案的开头至标准输出中,默认 head 命令打印文件的开头 10 行

常用参数:
-n <行数> 显示的行数(行数为复数表示从后向前数)

head a.log -n 20 显示a.log文件中前20行数据
head -c 20 a.log 显示a.log文件前20字节
head -n -10 a.log 显示a.log最后10行数据

14、Linux指令-which

这个指令平时用的比较少,主要功能是PATH 中搜索某个系统命令的位置,并返回第一个搜索结果
which 命令,可以看到某个系统命令是否存在,执行命令的位置。

which ls  查看 ls 命令的执行文件位置

15、Linux指令-locate

搜索文档数据库命令
locate 通过搜寻系统内建文档数据库达到快速找到档案,数据库由 updatedb 程序来更新,updatedb 由 cron daemon 周期性调用
locate 命令在搜寻较快,但最近才建立或刚更名的,可能会找不到
locate 与 find 命令相似,可以使用正则匹配查找

常用参数:
-l num 要显示的行数
-f 将特定的档案系统排除在外
-r 使用正则运算符作为寻找条件

locate pwd 查找文件名中包换pwd的所有文件
locate /etc/sh 搜索etc目录下的所有以sh开头的文件
locate -r '^/var.*txt$' 查找/var目录下,以txt结尾的文件

16、Linux指令-find

查找文件树命令,用于在文件树中查找文件,并作出相应的处理。

命令格式:find pathname -options [-print -exex -ok ...]
命令参数:
pathname:查找的目录路径
		 ~表示home目录
		 .表示当前目录
		 /表示根目录
-print:匹配的文件输出到标准输出
-exec:对匹配的文件执行该参数所给出的shell命令
-ok:和-exec作用相同,不过是以一种更安全的模式来执行该参数所给出的shell命令,在执行每一个命令前,都会给出提示,让用户来确定是都执行。

-options:表示查找方式,如下是其具体选项
-name 按照文件名查找文件
-perm 按照文件权限查找文件
-user 按照文件属主查找文件
-group 按照文件所属的组来查找文件
-type 查找某一类型的文件
		b 块设备文件
		d 目录
		c 字符设备文件
		l 符号链接文件
		p 管道文件
		f 普通文件
-size n :[c] 查找文件长度为n块文件,带有c时表示文件字节大小
-amin n 查找系统中最后n分钟访问的文件
-atime n 查找系统中最后n*24小时访问的文件
-cmin n 查找系统中最后n分钟被改变文件状态的文件
-ctime n 查找系统中最后n*24小时被改变文件状态的文件
-mmin n 查找系统中最后n分钟被改变文件数据的文件
-mtime n 查找系统中最后n*24小时被改变文件数据的文件
-maxdepth n 最大查找目录深度
-prune 选项来指出需要忽略的目录
-newer 查找更改时间比某个文件新,但比另外一个文件旧的所有文件
find . -name '[A-Z]*.txt' -print 在当前目录及子目录中,查找大写字母开头的txt文件 
find . -mtime -2 -type f -print  查找两天内被更该过的文件
find . -name 'del.txt' -ok rm {} \; 查找名为del.txt的文件并删除,删除前提示确认
find logs -type f -mtime +5 -exec -ok rm {} \;在 /logs目录中查找更改时间在5日以前的文件并删除它们

17、Linux指令-chmod

用于改变 linux 系统文件或目录的访问权限
该命令有两种用法:一种是包含字母和操作符表达式的文字设定法;另一种是包含数字的数字设定法
每一文件或目录的访问权限都有三组,每组用三位代号表示:

文件属主的读、写和执行权限
与属主同组的用户的读、写和执行权限
系统中其他用户的读、写和执行权限
常用参数:
-c 当发生改变时,报告处理信息
-R 处理指定目录以及其子目录下的所有文件
权限范围:
u:目录或者文件的当前的用户
g:目录或者文件的当前的群组
o:除了目录或者文件的当前用户或者群组之外的用户
a:所有的用户及群组

权限代号:
r :读权限,用数字4表示
w :写权限,用数字2表示
x :执行权限,用数字1表示
- :删除权限,用数字0表示
s :特殊权限
chmod -R 755 目录名称

18、Linux指令-tar

压缩和解压文件
tar 本身不具有压缩功能,只具有打包功能,有关压缩及解压是调用其它的功能来完成

命令参数:
-c 建立新的压缩文件
-f 指定压缩文件
-r 添加文件到已经压缩文件包中
-u 添加改了和现有的文件到压缩包中
-x 从压缩包中抽取文件
-t 显示压缩文件中的内容
-z 支持gzip压缩
-j 支持bzip2压缩
-Z 支持compress解压文件
-v 显示操作过程

示列:
tar -cvf log.tar 1.log,2.log 将1.log和2.log文件全部打成tar包
tar -zcvf /temp/log.tar.gz /log 将/log下所有文件及目录打包到指定的目录,并使用gz压缩
tar -ztvf /temp/log.tar.gz 查看刚打包的文件内容
tar -zxvf log.tar.gz 解压这个log.tar.gz文件
tar --exclude /log/mylog/ -zcvf /tmp/loglog.tar.gz /log   压缩打包/log,排除/log/mylog

19、Linux指令-chown

将指定文件的拥有者改为指定的用户或组,操作权限为root用户

用户可以是用户名或者用户 ID
组可以是组名或者组 ID
文件是以空格分开的要改变权限的文件列表,支持通配符
常用参数:
-c 显示更改部分的信息
-R 处理指定的目录及子目录

chown -c log:log a.txt 改变文件a.txt的拥有者和群组都为log,并显示改变信息
chown -c :log a.txt 改变文件a.txt的群组为log,并显示改变信息
chown -c log a.txt  改变文件a.txt的拥有者为log,并显示改变信息
chown -cR log: log/ 改变文件夹log及子文件、目录属主为log

20、Linux指令-df

显示磁盘空间使用情况

获取硬盘被占用空间,剩余空间等信息。默认所有当前被挂载的文件系统的可用空间都会显示
默认情况下,磁盘空间以 1KB 为单位进行显示
常用参数:
-a 全部文件系统列表
-h 以方便阅读的方式显示信息
-i 显示inode信息
-k 区块为1024字节
-l 只显示本地磁盘
-T 列出文件系统类型

21、Linux指令-du

是查看目录使用空间情况,与 df 命令不同的是 du 命令是对文件和目录磁盘使用的空间的查看

命令格式:du [选项] [文件]
常用参数:
-a 显示目录中所有文件大小
-k 以kb为单位显示文件大小
-m 以MB为单位显示文件大小
-g 以GB为单位显示文件大小
-h 以易读方式显示文件大小
-s 仅显示总计
-c 或 --total 除了显示个别目录或文件大小,同时也显示所有的目录或文件的大小总和

du -ah --max-depth=1  显示各个文件夹大小

22、Linux指令-ln

为文件在另外一个位置建立一个同步的链接

链接分为:
1、软链接

软链接,以路径的形式存在。类似于 Windows 操作系统中的快捷方式
软链接可以跨文件系统 ,硬链接不可以
软链接可以对一个不存在的文件名进行链接
软链接可以对目录进行链接

2、硬链接

硬链接,以文件副本的形式存在。但不占用实际空间。
不允许给目录创建硬链接
硬链接只有在同一个文件系统中才能创建

需要注意:

ln 命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化
ln 的链接又分软链接和硬链接两种,软链接就是ln –s 源文件 目标文件,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间;硬链接 ln 源文件 目标文件,没有参数 -s, 在指定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化
ln 指令用在链接文件或目录,如同时指定两个以上的文件或目录,且目标目录已经,则会把前面指定的所有文件或目录复制到该目录中。若同时指定多个文件或目录,且目标目录不存在,则会出现错误信息
常用参数:
-b 删除,覆盖之前建立的链接
-s 软连接
-v 显示详细处理过程
ln -s /usr/local/mysql/bin/mysql /usr/bin 建立一个软链接

23、Linux指令-date

显示或设定系统的日期与时间

命令参数:
-d<字符串>  显示字符串所指的日期与时间,字符串前后必须加上双引号
-s<字符串>  根据字符串来设置日期与时间,字符串前后必须加上双引号
-u  显示GMT
%H 小时(00-23)
%I 小时(00-12)
%M 分钟(以00-59来表示)
%s 总秒数起算时间为1970-01-01 00:00:00 UTC
%S 秒(以本地的惯用法来表示)
%a 星期的缩写
%A 星期的完整名称
%d 日期(以01-31来表示)
%D 日期(含年月日)
%m 月份(以01-12来表示)
%y 年份(以00-99来表示)
%Y 年份(以四位数来表示)

实例:
date +回车 查看系统当前时间
date -s "2018-05-24 16:36:00" 修改当天系统时间为所设置时间
date +%Y%m%d --date="+1 day"  //显示下一天的日期
date -d "nov 22"   显示今年的 11 月 22 日
date -d "2 weeks"  显示2周后的日期
date -d "next monday"  显示下周一的日期
date -d next-day +%Y%m%d 或 date -d tomorrow +%Y%m%d  显示明天的日期
date -d last-day +%Y%m%d 或 date -d yesterday +%Y%m%d  显示昨天的日期
date -d last-month +%Y%m  显示上个月的月份
date -d next-month +%Y%m  显示下个月的月份

24、Linux指令-cal

显示公历日历
指令后只有一个参数,表示年份,1-9999
指令后有两个参数,表示月份和年份

常用参数:
-3 显示前一个月,当前月,后一个月三个月的日历
-m 显示星期一为第一列
-j 显示当前年第几天
-y [year]显示[year]年份的日历

cal 11 2021 显示2021年11月的日历

25、Linux指令-grep

文本搜索命令,grep 是 Global Regular Expression Print 的缩写,全局正则表达式搜索
grep 在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须使用引号,模板后的所有字符串被看作文件名,搜索的结果被送到标准输出,不影响原文件内容。

命令格式:grep [option] pattern file|dir
常用参数:
-A n --after-context显示匹配字符后n行
-B n --before-context显示匹配字符前n行
-C n --context 显示匹配字符前后n行
-c --count 计算符合样式的列数
-i 忽略大小写
-l 只列出文件内容符合指定的样式的文件名称
-f 从文件中读取关键词
-n 显示匹配内容的所在文件中行数
-R 递归查找文件夹

grep 的规则表达式:
^        锚定行的开始 如:'^log'匹配所有以 log 开头的行。 
$        锚定行的结束 如:'log$'匹配所有以 log 结尾的行。 
.        匹配一个非换行符的字符,'l.g' 匹配 l+非换行字符+g,如:log
*        匹配零个或多个先前字符 如:'*log' 匹配所有一个或多个空格后紧跟 log 的行
.*       一起用代表任意字符  
[]       匹配一个指定范围内的字符,如:'[Ll]og' 匹配 Log 和 log 
[^]      匹配一个不在指定范围内的字符,如:'[^A-FH-Z]og' 匹配不包含 A-F 和 H-Z 的一个字母开头,紧跟 log 的行
\(..\)   标记匹配字符,如:'\(log\)',log 被标记为 1
\<      锚定单词的开始,如:'\<log' 匹配包含以 log 开头的单词的行
\>       锚定单词的结束,如:'log\>' 匹配包含以 log 结尾的单词的行
x\{m\}   重复字符 x,m 次,如:'a\{5\}' 匹配包含 5 个 a 的行
x\{m,\}  重复字符 x,至少 m 次,如:'a\{5,\}' 匹配至少有 5 个 a 的行
x\{m,n\} 重复字符 x,至少 m 次,不多于 n 次,如:'a\{5,10\}' 匹配 5 到 10 个 a 的行
\w       匹配文字和数字字符,也就是[A-Za-z0-9],如:'l\w*g'匹配 l 后跟零个或多个字母或数字字符加上字符 p  
\W       \w 的取反,匹配一个或多个非单词字符,如 , . ' "
\b       单词锁定符,如: '\blog\b' 只匹配 log

grep "text" . -R 在当前目录下递归查找含有“text”内容的文件

26、Linux指令-wc

wc(word count),统计指定的文件中字节数、字数、行数,并将统计结果输出

命令参数:
-c 统计字节数
-l 统计行数
-m 统计字符数
-w 统计词数,一个字被定义为由空白、跳格或换行字符分隔的字符串

27、Linux指令-ps

ps(process status),用来查看当前运行的进程状态,一次性查看,如果需要动态连续结果使用 top 指令

linux 系统中进程有5种状态:

运行(正在运行或在运行队列中等待)
中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
僵死(进程已终止, 但进程描述符存在, 直到父进程调用 wait4() 系统调用后释放)
停止(进程收到 SIGSTOP, SIGSTP, SIGTIN, SIGTOU 信号后停止运行)

ps 工具标识进程的5种状态码:

R 运行 runnable
S 中断 sleeping
D 不可中断 uninterruptible sleep
Z 僵死 a defunct process
T 停止 traced or stopped
常用参数:
-A 显示所有进程
-a 显示同一终端下所有进程
-f: full 展示进程详细信息
-e: every 展示所有进程信息
-ax: all 与 -e 同,展示所有进程信息
-o: 设置输出格式, 可以指定需要输出的进程信息列
-L: 展示线程信息
-C: 获取指定命令名的进程信息
-t: tty 展示关联指定 tty 的进程
--forest: 展示进程数
--sort: 按照某个或者某些进程信息列排序展示
a 显示所有进程
c 显示进程真实名称
e 显示环境变量
f 显示进程间的关系
r 显示当前终端运行的进程

-aux 显示所有包含其它使用的进程
-ef  显示所有当前进程信息
ps -C bash  显示指定名称的进程信息
ps -eLf  显示当前系统中的线程信息
ps -ef --forest  显示进程树

28、Linux指令-top

显示当前系统正在执行的进程的 ID、内存占用率、CPU 占用率等相关信息

常用参数:
-c 显示完整的进程命令
-s 保密模式
-p <进程号> 指定进程显示
-n <次数> 循环显示次数

实例:
top - 00:05:02 up 204 days,  9:56,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:  68 total,   1 running,  67 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.7 us,  0.7 sy,  0.0 ni, 98.3 id,  0.3 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1016168 total,    65948 free,   335736 used,   614484 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   517700 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     
 7110 root      10 -10  130476   9416   6116 S  1.3  0.9 141:26.59 AliYunDun   
15845 root      20   0   47064   4320   2180 S  0.3  0.4   2:51.16 nginx   

前五行是当前系统情况整体的统计信息区

第一行,任务队列信息,同 uptime 命令的执行结果:
00:05:02 — 当前系统时间
up 204 days,  9:56 — 系统已经连续运行了 204 天 9 小时 56 分钟未重启
2 users — 当前有 2 个用户登录系统
load average: 0.00, 0.01, 0.05 — load average 后面的三个数分别是 0 分钟、1 分钟、5分钟的负载情况,load average 数据是每隔 5 秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了

第二行,Tasks — 任务(进程):
系统现在共有 68 个进程,其中处于运行中的有 1 个,休眠中 67 个,停止 0 个,僵死 0个

第三行,cpu状态信息:
0.7 us — 用户空间占用 CPU 的百分比
0.7 sy — 内核空间占用 CPU 的百分比
0.0 ni — 改变过优先级的进程占用 CPU 的百分比
98.3 id — 空闲CPU百分比
0.3 wa — IO 等待占用 CPU 的百分比
0.0 hi — 硬中断(Hardware IRQ)占用 CPU 的百分比
0.0 si — 软中断(Software Interrupts)占用 CPU 的百分比
0.0 st - 虚拟机占用百分比


第四行,内存状态:
1016168 total — 物理内存总量
65948 free — 空闲内存总量
335736 used — 使用中的内存总量
614484 buff/cache — 缓存的内存量

第五行,swap交换分区信息,具体信息说明如下:
0 total — 交换区总量
0 free — 空闲交换区总量
0 used — 使用的交换区总量
517700 avail Mem - 可用内存

第七行以下:各进程(任务)的状态监控,项目列信息说明如下:
PID — 进程id
USER — 进程所有者
PR — 进程优先级
NI — nice值。负值表示高优先级,正值表示低优先级
VIRT — 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES — 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
SHR — 共享内存大小,单位kb
S — 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU — 上次更新到现在的CPU时间占用百分比
%MEM — 进程使用的物理内存百分比
TIME+ — 进程使用的CPU时间总计,单位1/100秒
COMMAND — 进程名称(命令名/命令行)

top 交互命令
h 显示top交互命令帮助信息
c 切换显示命令名称和完整命令行
m 以内存使用率排序
P 根据CPU使用百分比大小进行排序
T 根据时间/累计时间进行排序
W 将当前设置写入~/.toprc文件中
o或者O 改变显示项目的顺序

29、Linux指令-kill

删除执行中的程序或者工作,发送指定的信号到相应的进程,如果不指定信号的话,将发送sigterm(15)终止指定进程,用‘-KILL’参数,发送信号SIGKILL(9)强制结束进程

常用参数:
-l 信号,若不加信号的编号参数,会全部列出信号的名称。
-a 当处理当前进程时,不限制命令名和进程号的对应关系
-p 指定kill命令只打印相关进程的进程号,而不发送任何信号
-s 指定发送信号
-u 指定用户

示例:
kill -l 显示信号
kill -KILL 8878 强制杀死进程 8878
kill -9 8878 彻底杀死进程8878
kill -u tomcat 杀死tomcat用户的进程

30、Linux指令-free

显示系统内存使用情况,包括物理内存、swap内存和内核cache内存

命令参数:
-b 以Byte显示内存使用情况
-k 以kb为单位显示内存使用情况
-m 以mb为单位显示内存使用情况
-g 以gb 为单位显示内存使用情况
-s<间隔秒数>持续显示内存
-t 显示内存使用总合

总结

以上是基于在工作中所遇到的一些命令进行了汇总,并不完全,后续再遇到会继续补充滴,大家像build哥一样实在记不住Linux命令的,就别硬记啦,收藏起来,后面忘记了直接查吧。

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得
留言+点赞+收藏
呀。原创不易,转载请联系Build哥!

image

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

image

大模型落地实战指南:从选择到训练,深度解析显卡选型、模型训练技、模型选择巧及AI未来展望---打造AI应用新篇章

0.前言大模型发展史

  • 早期阶段(1950s~1980s)

在1950年代初期,人们开始尝试使用计算机处理自然语言文本。然而,由于当时的计算机处理能力非常有限,很难处理自然语言中的复杂语法和语义。随着技术的发展,自然语言处理领域在20世纪60年代和70年代取得了一些重要的进展。例如,1970年,美国宾夕法尼亚大学的Adele Goldberg和David Robson创建了一个名为Lunenfeld Project的系统,它可以进行自动翻译。同时,中国科学院自动化研究所也在20世纪70年代开始研究自然语言处理技术,主要集中在机器翻译领域。

  • 中期阶段(1980s~2010s)

进入20世纪80年代和90年代,自然语言处理领域的研究更加深入。例如,1981年,Xerox PARC的研究人员Ron Kaplan和Martin Kay开发了一个名为Lexical Functional Grammar(LFG)的语法框架,这为后续的NLP研究提供了重要的理论基础。在这个阶段,NLP技术开始逐渐应用于实际场景中,如机器翻译、语音识别和文本分类等。

  • 现代阶段(2010s~至今)

进入21世纪后,尤其是近年来,NLP大模型的发展迎来了革命性的突破。这主要得益于深度学习技术的快速发展和计算能力的提升。在这一阶段,预训练模型成为NLP领域的主流方法。

其中,2018年是一个重要的时间节点。在这一年,BERT模型的出现标志着NLP大模型时代的开始。BERT是一个基于Transformer结构的双向编码器模型,通过在大量文本数据上进行预训练,学习到了丰富的语言知识和上下文信息。随后,GPT系列模型也相继问世,这些模型在预训练的基础上,通过微调可以适应各种NLP任务,取得了显著的性能提升。

此外,随着计算资源的不断丰富和模型结构的优化,NLP大模型的规模也在不断扩大。从最初的几百万参数到现在的几十亿甚至上百亿参数,这些大模型在性能上不断刷新记录,推动了NLP技术的快速发展。

NLP领域主要模型的发展历程可以大致分为如下几个阶段:

  1. 早期研究阶段:侧重于设计人工编写的规则和语法,如基于规则和知识的方法等;
  2. 统计方法崛起:引入数学和统计方法,侧重于从大规模语料库中自动学习语言规律,如隐马尔可夫模型(HMM)、条件随机场(CRF)等;
  3. 深度学习革命:基于神经网络模型的方法,强调自动提取特征和端到端的训练,如循环神经网络(RNN)、长短时记忆网络(LSTM)、卷积神经网络(CNN)等;
  4. 预训练模型兴起:基于大规模数据和深度学习模型的预训练方法,提升了NLP任务的性能,如BERT、GPT、T5等。

可以发现,NLP领域的主要模型,从深度学习阶段开始,经过预训练模型兴起,直到如今的各种聊天大模型的爆发,NLP模型一直在向着参数量更多、通用性更强的方向发展。

  1. 左图介绍:语言模型(LM)是一种利用自然文本来预测词(Token)顺序的机器学习方法。大语言模型(LLM)则通常指参数数量达到亿级别的神经网络语言模型,例如:GPT-3、GPT-4、PaLM2等,仅有左下方的灰色分支为非Transformer模型,其余颜色的分支均为基于Transformer的模型

  2. 自然语言处理是计算机科学、人工智能和语言学的交叉领域,研究如何让计算机处理、理解和生成人类语言。目标是:能够实现人机交互、自动翻译、信息检索、情感分析等任务。应用领域包括:搜索引擎、社交媒体监测、智能客服、新闻生成等。

1.显卡选择篇-硬件配置

先说结论,大模型的训练用 4090 是不行的,但推理(inference/serving)用 4090 不仅可行,在性价比上还能比 H100 稍高。4090 如果极致优化,性价比甚至可以达到 H100 的 2 倍。

  • 看链接相关文章超详细讲解

英伟达系列显卡大解析B100、H200、L40S、A100、A800、H100、H800、V100如何选择,含架构技术和性能对比带你解决疑惑

如何选择GPU显卡,带你对比A100/H100/4090性价比、训练/推理该使用谁?

2.大模型训练流程

训练一个大模型,到底需要投入多少块,需要多少数据,训练多长时间能达到一个不错的效果? 本文引用靠谱的数据,来回答这些问题。

  • 全流程训练

大模型的训练,简单来说,分为
Pretraining

Finetuning
微调,
Pretraining
需要非常多的数据和算力,
Finetuning
相对来说对算力的要求比较低。

  • LoRA
    :基本原理是冻结预训练好的模型权重参数,在冻结原模型参数的情况下,通过往模型中加入额外的网络层,并只训练这些新增的网络层参数
  • P-tuning v1
    微调方法是将 Prompt 加入到微调过程中,只对 Prompt 部分的参数进行训练,而语言模型的参数固定不变
  • Freeze
    :即参数冻结,对原始模型部分参数进行冻结操作,仅训练部分参数
  • RLHF(OpenAI)2022.12 (Reinforcement Learning fromHuman Feedback,人类反馈强化学习)起到的作用是,通过将人类的反馈纳入训练过程,为机器提供了一种自然的、人性化的互动学习过程。
    • RRHF(阿里巴巴)2023.4
    • RLTF(腾讯)2023.7
    • RRTF(华为)2023.7
    • RLAIF(谷歌)2023.9

百度千帆大模型训练全流程Mass:

2.1 SFT监督微调&RLHF讲解

2.2 Prompt工程原理篇

2.3 Prompt工程实践篇

3.大模型如何选择

3.1 大模型能力对比

国内外依然有一定差距。GPT4-Turbo总分92.71分领先,高于其他国内大模型及国外大模型。其中国内最好模型文心一言4.0总分87.75分,距离GPT4-Turbo有4.96分,距离GPT4(网页)有2.61分的差距。本次最新上榜的Google开源模型的Gemma-7b-it表现不佳,可能的原因之一是训练数据中中文语料占比较少。

  • 国内大模型历月前三甲

SuperCLUE月榜首位的大模型有5个。分别是文心一言、BlueLM、SenseChat3.0、Baichuan2-13B-Chat、360智脑。其中,百度的文心一言登顶SuperCLUE月榜的次数最多,分别在7月、11月、12月、24年2月取得了SuperCLUE最好成绩。

3.2 开源模型对比推荐

总体上大版本(如34B)的模型优于中小版本(13B、6B)的开源模型,更大的版本(如72B)的模型表现要更好。但也有小模型好于大模型的情况,如qwen1.5的70亿参数版本好于一些130亿参数的大模型,面壁智能的MiniCPM-2b好于智谱AI的ChatGLM3-6B

大厂中开源的主导力量是阿里云,在各个参数量级中国都有模型开源。但众多的创业公司是开源模型的主力,如智谱AI、百川智能、零一万物、元象科技、面壁智能。

3.2 Qwen 不同大小模型的训练、推理配置

  • 通义千问开源模型配置要求

测量了使用BF16、Int8和Int4中的模型生成序列长度(Sequence Length)2048的平均推理速度和GPU内存使用情况。

  • 训练所需要内存列表

  • 推理所需要内存列表

  • Qwen在各个评测任务表现-整体效果不错

  • 技术创新:通义千问720亿参数模型(Qwen-72B)代表了当时业界开源模型的顶级技术水平,大规模参数量意味着模型具有更强的学习能力和泛化性能,能够处理复杂多样的自然语言任务。

  • 性能表现:在多个权威基准评测中取得开源模型最优成绩,证明了其卓越的技术实力和广泛的适用性,不仅超越了部分知名开源模型如Llama 2-70B,而且在与商用闭源模型的竞争中也展现出了竞争力。

  • 全模态能力:开源的模型包括文本和音频等多种模态,实现了“全尺寸全模态”的开源,表明通义千问支持跨模态的应用场景,增强了其在多领域应用的潜力。

  • 行业影响:阿里云推动了AI技术普惠化进程,使得学术界、企业和个人开发者可以更便捷地利用这些先进的模型进行研究和开发,降低了准入门槛,促进了AI生态的繁荣与发展。

4. 对AI看法

  1. 提高效率:
    • 在多个业务领域中,AI大模型通过自动化和优化流程,显著提高了工作效率;
    • 在数据分析领域,AI大模型可以自动处理和分析海量数据,提供有价值的洞察。
  2. 提高决策准确性
    • 基于大量数据的训练,AI大模型可以提供更为准确的分析和预测,可以做出更明智的决策。
  3. 推动创新
    • AI大模型的应用为企业带来了创新的可能性,推动了产品和服务的升级。

更多优质内容请关注公号:汀丶人工智能;会提供一些相关的资源和优质文章,免费获取阅读。

原文链接:
大模型落地实战指南:从选择到训练,深度解析显卡选型、模型训练技、模型选择巧及AI未来展望---打造AI应用新篇章

参考链接:

  1. https://github.com/QwenLM/Qwen?tab=readme-ov-file#profiling-of-memory-and-speed
  2. https://github.com/Lightning-AI/lit-llama/blob/main/howto/train_redpajama.md