Kudu-客户端API编程、生态整合(Spark、Flink、Impala)

2023-11-12

Kudu客户端API编程

客户端API核心类

Kudu提供了主流语言客户端API,核心类、方法是一致的,我们首先简要盘点下核心的这些类以便于我们写代码。

(1)Kudu client

  • AsyncKuduClient:完全异步且线程安全的Kudu客户端。该类应该只实例化一次,同时访问很多表。只有操纵多个不同集群才需要实例化多次。不会阻塞操作,可以关联回调函数用于操作完成时的动作。Builder模式创建。
  • KuduClient:对AsyncKuduClient的封装,同步执行、线程安全的Kudu客户端,Builder模式创建。

(2)Schema

表示表结构,主要是column的集合。该类提供了一些工具方法用于查询操作。

(3)ColumnSchema

表示一column,使用builder构建。

(4)CreateTableOptions

Builder模式类,用于创建表。

(5)KuduTable

表示集群上的一张表。含有当前表结构信息,隶属于特定AsyncKuduClient。

(6)Session

  • AsyncKuduSession:隶属于特定KuduClient,代表一个上下文环境,所有写操作都会在该上下文中进行。在一个session中,可以对多个操作按批处理方式执行,以获得较好的性能。每个session都可以设置超时、优先级以及跟踪id等信息。 session和KuduClient是独立的,主要是在多线程环境下,不同线程需要并发执行事务,事务的边界是基于每 个session的BeginTransaction和commit之间的过程。 来自于不同session的写操作不会组织到一个RPC请求batch中,意味着延迟敏感的客户端(低延迟)和面向吞吐量的客户端(高延迟)使用同一KuduClient,每个Session中可以设置特定的超时和优先级。
  • KuduSession:对AsyncKuduSession封装,同步执行,但非线程安全

(7)Insert/Update/Delete/Upsert

表示插入/更新/删除/插入或者更新操作,对象不可复用。

(8)PartialRow

表示一行的部分列。

(9)KuduScanner

扫描对象,用于条件查询及迭代获取结果集。

Java编程接口

环境准备

接下来我们只需要在pom.xml中导入相关依赖即可:

<properties> 
    <kudu.version>1.10.0</kudu.version> 
    <junit.version>4.12</junit.version> 
</properties> 

<dependencies> 
    <!-- Kudu client --> 
    <dependency> 
        <groupId>org.apache.kudu</groupId> 
        <artifactId>kudu-client</artifactId> 
        <version>${kudu.version}</version> 
    </dependency> 
    <!-- Log --> 
    <dependency> 
        <groupId>org.slf4j</groupId> 
        <artifactId>slf4j-simple</artifactId> 
        <version>1.7.12</version> 
    </dependency> 
    <!-- Unit test --> 
    <dependency> 
        <groupId>junit</groupId> 
        <artifactId>junit</artifactId> 
        <version>${junit.version}</version> 
        <scope>provided</scope> 
    </dependency> 
</dependencies> 

<!-- 指定具体仓库 --> 
<repositories> 
    <repository> 
        <id>cdh.repo</id> 
        <name>Cloudera Repositories</name>
        <url>https://repository.cloudera.com/content/repositories/releases</url> 
        <snapshots> 
            <enabled>false</enabled> 
        </snapshots> 
    </repository> 
</repositories>

代码骨架:

public class TestKudu { 
    public static void main(String[] args) { 
        System.out.println("hello kudu!");
    }
}

定义一个常量KUDU_MASTERS存放Kudu master的连接信息(根据自己的实际情况改为你的IP或者主机名):

private static final String KUDU_MASTERS = "node01:7051,node02:7051,node03:7051";
创建表

执行结果:

@Test 
public void testCreateTable() throws KuduException { 
    //1、创建Schema 
    List<ColumnSchema> columns = new ArrayList<>(2); 
    columns.add(new ColumnSchema.ColumnSchemaBuilder("uid", Type.INT8) 
                .key(true) 
                .build()); 
    columns.add(new ColumnSchema.ColumnSchemaBuilder("name", 
                                                     Type.STRING).nullable(true) 
                .build()); 
    columns.add(new ColumnSchema.ColumnSchemaBuilder("age", Type.INT8).build()); 
    Schema schema = new Schema(columns); 
    //2、指定表选项 
    //2.1 建表选项 
    CreateTableOptions tableOptions = new CreateTableOptions(); 
    //2.2 创建分区字段列表(必须是主键列) 
    List<String> hashCls = new ArrayList<String>(); 
    hashCls.add("uid"); 
    int numBuckets = 6; 
    //2.3 分区策略 
    tableOptions.addHashPartitions(hashCls,numBuckets) 
        .setNumReplicas(1); 
    //3、创建KuduClient 
    KuduClient client=null; 
    try { 
        client=new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        //4、创建表 
        if(!client.tableExists("users")){ 
            client.createTable("users", schema, tableOptions); 
            System.out.println(".........create table success........."); 
        }else{ 
            System.out.println(".........the table already exists ........."); 
        } 
    }finally { 
        //5、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
} 
插入数据
@Test 
public void testInsert() throws KuduException { 
    //1、获得kudu客户端 
    KuduClient client = null; 
    //2、打开表 
    KuduTable table = null; 
    //3、创建会话 
    KuduSession session = null; 
    try{
        client = new KuduClient 
            .KuduClientBuilder(KUDU_MASTERS) 
            .build(); 
        table = client.openTable("users") ; 
        session = client.newSession(); 
        session.setFlushMode(SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND); 
        session.setFlushInterval(2000); 
        //4、循环插入10行记录 
        for(int i = 0 ; i < 100 ; i ++){ 
            //新建Insert对象 
            Insert insert = table.newInsert() ; 
            PartialRow row = insert.getRow() ; 
            row.addByte("uid" , Byte.parseByte(i+"")); 
            //i是偶数 
            if(i % 2 == 0){ 
                row.setNull("name"); 
            }
            else{
                row.addString("name" , "name " + i); 
            }
            row.addByte("age",Byte.parseByte(i+"")); 
            //加入session 
            session.apply(insert) ; 
        }
        //5、关闭session 
        session.close() ; 
        //判断错误数 
        if(session.countPendingErrors() != 0){ 
            //获得操作结果 
            RowErrorsAndOverflowStatus result = session.getPendingErrors() ; 
            if(result.isOverflowed()){ 
                System.out.println("............buffer溢出!................."); 
            }
            RowError[] errs = result.getRowErrors() ; 
            for(RowError er : errs){ 
                System.out.println(er); 
            } 
        } 
    }finally { 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}
查询数据
@Test 
public void testSelect() throws KuduException { 
    //1、获得kudu客户端 
    KuduClient client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
    //2、打开表 
    KuduTable table = client.openTable("users") ; 
    //3、扫描器 
    KuduScanner scanner = null; 
    try {
        //4、获取表结构 
        Schema schema = table.getSchema() ; 
        //5、指定查询条件 
        List<String> projectColumns = new ArrayList<String>(2); 
        projectColumns.add("uid"); 
        projectColumns.add("name"); 
        projectColumns.add("age"); 
        //age >= 0 
        int lowerBound = 0; 
        KuduPredicate lowerPred = 
            KuduPredicate.newComparisonPredicate(schema.getColumn("age"), 
                                                 KuduPredicate.ComparisonOp.GREATER_EQUAL, lowerBound); 
        //age < 10 
        int upperBound = 10; 
        KuduPredicate upperPred = 
            KuduPredicate.newComparisonPredicate(schema.getColumn("age"), 
                                                 KuduPredicate.ComparisonOp.LESS, upperBound); 
        scanner = client.newScannerBuilder(table) 
            .setProjectedColumnNames(projectColumns) 
            .addPredicate(lowerPred) 
            .addPredicate(upperPred) 
            .build(); 
        int resultCount = 0; 
        while (scanner.hasMoreRows()) { 
            RowResultIterator results = scanner.nextRows(); 
            while (results.hasNext()) { 
                RowResult result = results.next(); 
                byte uid = result.getByte("uid"); 
                String name = null ; 
                if (result.isNull("name")) { 
                    name = "不存在" ; 
                }
                else{
                    name = result.getString("name") ; 
                }
                byte age = result.getByte("age"); 
                System.out.printf("uid=%d, name=%s, age=%drn" , uid 
                                  ,name,age); 
                resultCount++; 
            } 
        }
        System.out.println("-----------------------"+resultCount); 
        scanner.close() ; 
    }finally { 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}
修改表结构
@Test 
public void testAlterTable() throws Exception { 
    //1、获得kudu客户端 
    KuduClient client = null; 
    try {
        //2、修改表选项 
        AlterTableOptions ato = new AlterTableOptions() ; 
        ato.addColumn("wage" , Type.DOUBLE , 10000.000) ; 
        //3、修改表结构 
        client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        if(client.tableExists("users")){ 
            client.alterTable("users" , ato) ; 
            System.out.println("........alterTable success.........."); 
        } 
    }finally { 
        //4、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
} 

修改完再次查询:

@Test 
public void testSelect2() throws KuduException { 
    //1、获得kudu客户端 
    KuduClient client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
    //2、打开表 
    KuduTable table = client.openTable("users") ; 
    //3、扫描器 
    KuduScanner scanner = null; 
    try {
        //4、获取表结构 
        Schema schema = table.getSchema() ; 
        //5、指定查询条件 
        List<String> projectColumns = new ArrayList<String>(2); 
        projectColumns.add("uid"); 
        projectColumns.add("name"); 
        projectColumns.add("age"); 
        projectColumns.add("wage"); 
        //age >= 0 
        int lowerBound = 0; 
        KuduPredicate lowerPred = 
            KuduPredicate.newComparisonPredicate(schema.getColumn("age"), 
                                                 KuduPredicate.ComparisonOp.GREATER_EQUAL, lowerBound); 
        //age < 10 
        int upperBound = 10; 
        KuduPredicate upperPred = 
            KuduPredicate.newComparisonPredicate(schema.getColumn("age"), 
                                                 KuduPredicate.ComparisonOp.LESS, upperBound); 
        scanner = client.newScannerBuilder(table) 
            .setProjectedColumnNames(projectColumns) 
            .addPredicate(lowerPred) 
            .addPredicate(upperPred) 
            .build(); 
        int resultCount = 0; 
        while (scanner.hasMoreRows()) { 
            RowResultIterator results = scanner.nextRows(); 
            while (results.hasNext()) { 
                RowResult result = results.next(); 
                byte uid = result.getByte("uid"); 
                String name = null ; 
                if (result.isNull("name")) { 
                    name = "不存在" ; 
                }
                else{
                    name = result.getString("name") ; 
                }
                byte age = result.getByte("age"); 
                double wage = result.getDouble("wage"); 
                System.out.printf("uid=%d, name=%s, age=%d, wage=%frn" , uid 
                                  ,name,age,wage); 
                resultCount++; 
            } 
        }
        System.out.println("-----------------------"+resultCount); 
        scanner.close() ; 
    }finally { 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}
更新数据
@Test 
public void testUpdate() throws Exception { 
    //1、获得kudu客户端 
    KuduClient client = null; 
    //2、打开表 
    KuduTable table = null; 
    //3、会话 
    KuduSession session = null; 
    try {
        client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        table = client.openTable("users"); 
        session = client.newSession(); 
        //4、创建并执行update操作 
        Update update = table.newUpdate(); 
        PartialRow row = update.getRow(); 
        row.addByte("uid", Byte.parseByte("1"+"")); 
        row.addDouble("wage", 20000.000); 
        session.apply(update); 
        session.close(); 
    }finally { 
        //5、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}
删除数据
@Test 
public void testDelete() throws Exception { 
    //1、获得kudu客户端 
    KuduClient client = null; 
    //2、打开表 
    KuduTable table = null; 
    //3、会话 
    KuduSession session = null; 
    try {
        client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        table = client.openTable("users"); 
        session = client.newSession(); 
        //4、新建并执行Delete操作 
        Delete delete = table.newDelete(); 
        //得到row 
        PartialRow row = delete.getRow(); 
        //where key = 0 
        row.addByte("uid", Byte.parseByte(3+"")); 
        session.apply(delete); 
        session.close(); 
    }finally { 
        //5、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}
更新和插入
@Test 
public void testUpsert() throws Exception { 
    //1、获得kudu客户端 
    KuduClient client = null; 
    //2、打开表 
    KuduTable table = null; 
    //3、会话 
    KuduSession session = null; 
    try {
        client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        table = client.openTable("users"); 
        session = client.newSession(); 
        //4、upsert 
        Upsert upsert = table.newUpsert(); 
        PartialRow row = upsert.getRow(); 
        row.addByte("uid", Byte.parseByte(3+"")); 
        row.addString("name", "tomasLee"); 
        row.addByte("age", Byte.parseByte(35+"")); 
        row.addDouble("wage", 18000.000); 
        session.apply(upsert); 
        Upsert upsert1 = table.newUpsert(); 
        PartialRow row1 = upsert1.getRow(); 
        row1.addByte("uid", Byte.parseByte(1+"")); 
        row1.addByte("age", Byte.parseByte(8+"")); 
        row1.addDouble("wage", 15000.000); 
        session.apply(upsert1); 
        session.close(); 
    }finally { 
        //5、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
}

注意:Upsert主键一样则更新,否则为新增,不能为空的字段必须提供值,否则不执行。

删除表
@Test 
public void testDeleteTable() throws KuduException { 
    //1、创建KuduClient 
    KuduClient client = null; 
    try { 
        client = new KuduClient.KuduClientBuilder(KUDU_MASTERS).build(); 
        //2、删除表 
        if(client.tableExists("users")){ 
            client.deleteTable("users"); 
            System.out.println("........delete table success.........."); 
        } 
    }finally { 
        //3、关闭资源 
        if(null!=client){ 
            client.shutdown(); 
        } 
    } 
} 

其他语言编程接口

请参看官方exaples:https://github.com/apache/kudu/tree/master/examples

Hadoop生态整合

整合概述

Kudu除了支持高吞吐离线分析(类似HDFS)和高并发随机读写(类似HBase),还可以整合主流分布式计算框架进行离线运算和即系查询,常见整合方案如下:

MMSIZE

根据上表的总结,大家可以结合自己业务实际情况去选择整合方式,没有好坏之分。

集成Spark

Spark shell中操作Kudu

在Spark shell中可以轻松操作Kudu,不过这种方式不常用,参考链接如下:

https://kudu.apache.org/releases/1.10.0/docs/developing.html#_kudu_integration_with_spark

代码整合Kudu+Spark-项目准备

(1)修改pom.xml,最中内容如下:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>com.djt</groupId> 
    <artifactId>kudu_tutorial</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <properties> 
        <kudu.version>1.10.0</kudu.version> 
        <junit.version>4.12</junit.version> 
        <scala.version>2.11.8</scala.version> 
        <spark.version>2.4.3</spark.version> 
    </properties> 
    <dependencies> 
        <!-- Scala --> 
        <dependency> 
            <groupId>org.scala-lang</groupId> 
            <artifactId>scala-library</artifactId> 
            <version>${scala.version}</version> 
        </dependency> 
        <!-- Spark --> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-core_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-sql_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-hive_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <!-- kudu-spark --> 
        <dependency> 
            <groupId>org.apache.kudu</groupId> 
            <artifactId>kudu-spark2_2.11</artifactId> 
            <version>${kudu.version}</version> 
        </dependency> 
        <!-- Kudu client --> 
        <dependency> 
            <groupId>org.apache.kudu</groupId> 
            <artifactId>kudu-client</artifactId> 
            <version>${kudu.version}</version> 
        </dependency> 
        <!-- Log --> 
        <dependency> 
            <groupId>org.slf4j</groupId> 
            <artifactId>slf4j-simple</artifactId> 
            <version>1.7.12</version> 
        </dependency> 
        <!-- Unit test --> 
        <dependency> 
            <groupId>junit</groupId> 
            <artifactId>junit</artifactId> 
            <version>${junit.version}</version> 
            <scope>provided</scope> 
        </dependency> 
    </dependencies> 
    <build> 
        <plugins> 
            <plugin> 
                <groupId>org.apache.maven.plugins</groupId> 
                <artifactId>maven-compiler-plugin</artifactId> 
                <version>3.5.1</version> 
                <configuration> 
                    <source>1.8</source> 
                    <target>1.8</target> 
                </configuration> 
            </plugin> 
            <plugin> 
                <groupId>net.alchim31.maven</groupId> 
                <artifactId>scala-maven-plugin</artifactId> 
                <version>3.2.0</version> 
                <executions> 
                    <execution> 
                        <goals> 
                            <goal>compile</goal> 
                            <goal>testCompile</goal> 
                        </goals> 
                        <configuration> 
                            <args>
                                <arg>-dependencyfile</arg> 
                                <arg>${project.build.directory}/.scala_dependencies</arg> 
                            </args> 
                        </configuration> 
                    </execution> 
                </executions> 
            </plugin> 
            <plugin> 
                <groupId>org.apache.maven.plugins</groupId> 
                <artifactId>maven-shade-plugin</artifactId> 
                <version>2.4</version> 
                <executions> 
                    <execution> 
                        <phase>package</phase> 
                        <goals> 
                            <goal>shade</goal> 
                        </goals> 
                    </execution> 
                </executions> 
            </plugin> 
        </plugins> 
    </build> 
    <!-- 指定具体仓库 --> 
    <repositories> 
        <repository> 
            <id>cdh.repo</id> 
            <name>Cloudera Repositories</name> 
            <url>https://repository.cloudera.com/content/repositories/releases</url> 
            <snapshots> 
                <enabled>false</enabled> 
            </snapshots> 
        </repository> 
    </repositories> 
</project> 

(2)准备数据文件

在项目根目录创建目录dataset,并把数据文件students100k放进去:

MSIZE

(3)准备代码骨架

跟java目录平行创建一个目录scala:

MSIZE

将scala目录设置为sources root:

MSIZE

创建Scala类com.djt.kudu.spark.KuduSparkDemo:

package com.djt.kudu.spark 
import org.junit.Test 
class KuduSparkDemo { 
    @Test 
    def test(): Unit = { 
        println("hello kudu spark!") 
    } 
}

直接运行能正常输出,项目就准备好了。

DDL

首先,我们定义一个常量KUDU_MASTERS存放Kudu master的连接信息:

//master连接信息 
val KUDU_MASTERS = "node01:7051,node02:7051,node03:7051" 

接下来,我们创建case class Student,它的字段信息必须跟我们前面数据文件students100k相匹配:

case class Student(sid: Int, name: String, gender: String, age: Int, height: Float, weight: Float) 
@Test 
def ddl(): Unit = { 
    // 1. SparkSession 
    val spark = SparkSession.builder() 
    .master("local[6]") 
    .appName("KuduSparkDemo") 
    .getOrCreate() 
    //2 创建 KuduContext 
    val kuduContext = new KuduContext(KUDU_MASTERS, spark.sparkContext) 
    //3、判断表是否存在, 如果存在则删除表 
    val TABLE_NAME = "students" 
    if (kuduContext.tableExists(TABLE_NAME)) { 
        kuduContext.deleteTable(TABLE_NAME) 
    }
    //4. 定义一张Kudu表:students
    //4.1 定义字段信息
    val schema = StructType( 
        List(
            StructField("sid", IntegerType, nullable = false), 
            StructField("name", StringType, nullable = false), 
            StructField("gender", StringType, nullable = false), 
            StructField("age", IntegerType, nullable = false), 
            StructField("height", FloatType, nullable = false), 
            StructField("weight", FloatType, nullable = false) 
        )
    )
    //4.2 定义主键(rowkey) 
    val keys = Seq("sid") 
    //4.3 定义分区信息 
    import scala.collection.JavaConverters._ 
    val numBuckets = 6 
    val options = new CreateTableOptions() 
    .addHashPartitions(List("sid").asJava,numBuckets) 
    .setNumReplicas(1) 
    //5. 创建一张Kudu表:students
    kuduContext.createTable(tableName = TABLE_NAME, 
                            schema = schema, 
                            keys = keys, 
                            options = options) 
    //6、关闭资源 
    spark.close() 
}
CUD
@Test 
def cud(): Unit = { 
    // 1、SparkSession 
    val spark = SparkSession.builder() 
    .master("local[6]") 
    .appName("KuduSparkDemo") 
    .getOrCreate() 
    //2、创建 KuduContext 
    val kuduContext = new KuduContext(KUDU_MASTERS, spark.sparkContext) 
    // 3. 增加 
    import spark.implicits._ 
    val df = Seq(Student(8, "王荣", "F", 19, 164.4f, 116.5f), Student(9, "李晓", 
                                                                    "F", 18, 174.4f, 126.5f)).toDF() 
    val TABLE_NAME = "students" 
    kuduContext.insertRows(df, TABLE_NAME) 
    // 4. 删除 
    kuduContext.deleteRows(df.select($"sid"), TABLE_NAME) 
    // 5. 增或改 
    kuduContext.upsertRows(df, TABLE_NAME) 
    // 6. 修改 
    kuduContext.updateRows(df, TABLE_NAME) 
    //7、关闭资源 
    spark.close() 
}

用如下命令以验证结果(换成自己的主机名或者IP):

kudu table scan node01:7051,node02:7051,node03:7051 students 
批处理读写Kudu

1)批量写

@Test 
def batchWrite(): Unit = { 
    // 1.SparkSession 
    val spark = SparkSession.builder() 
    .master("local[6]") 
    .appName("KuduSparkDemo") 
    .getOrCreate() 
    // 2.定义数据schema 
    val schema = StructType( 
        List(
            StructField("sid", IntegerType, nullable = false), 
            StructField("name", StringType, nullable = false), 
            StructField("gender", StringType, nullable = false), 
            StructField("age", IntegerType, nullable = false), 
            StructField("height", FloatType, nullable = false), 
            StructField("weight", FloatType, nullable = false) 
        ) 
    )
    // 3.从csv读取数据 
    val studentsDF = spark.read 
    .option("header", value = true) 
    .option("delimiter", value = "t") 
    .schema(schema) 
    .csv("dataset/students100k") 
    // 4.写入Kudu 
    val TABLE_NAME = "students" 
    studentsDF.write 
    .option("kudu.table", TABLE_NAME) 
    .option("kudu.master", KUDU_MASTERS) 
    .mode(SaveMode.Append) 
    .format("kudu") 
    .save() 
    //5.回收资源 
    spark.close() 
}

2)SQL分析(批量读)

@Test 
def batchRead(): Unit = { 
    // 1.SparkSession 
    val spark = SparkSession.builder() 
    .master("local[6]") 
    .appName("KuduSparkDemo") 
    .getOrCreate() 
    // 2.从kudu表读取数据到DataFrame 
    val TABLE_NAME = "students" 
    val studentsDF = spark.read 
    .option("kudu.table", TABLE_NAME) 
    .option("kudu.master", KUDU_MASTERS) 
    .format("kudu") 
    .load() 
    // 3.直接使用Spark API查询 
    //studentsDF.select("sid","name", "gender", "age").filter("sid >= 5 and sid<=10").show() 
    // 3.基于DataFrame创建临时视图(临时表) 
    studentsDF.createOrReplaceTempView("students") 
    // 4.执行sql查询 
    //val projectDF = spark.sql("select sid, name, gender, age from students where age <= 19 and height > 180") 

    val projectDF = spark.sql("select gender, count(), max(height) ,min(height), avg(height) from students where age <= 19 and height > 180 group by gender") 
    //5.打印结果 
    projectDF.show() 
    //6.关闭资源 
    spark.close() 
} 

特别注意
  • 每个集群避免多KuduClient

    常见错误就是创建了多个KuduClient对象。在kudu-spark中,KuduClient对象由KuduContext所持有。对于同一kudu集群,不应该创建多个KuduClient对象,而是应该通过KuduContext访问KuduClient,方法为KuduContext.syncClient。

  • 存在问题和限制

    • Spark2.2+需要Java8支持,尽管Kudu Spark2.x兼容Java 7。Spark 2.2 默认依赖Kudu1.5.0。
    • Kudu表如果包含大写或非ascii字符的话,注册临时表时需要指定其他的名字。
    • 列名如果含有大写或非ascii字符的,不能在Spark SQL中使用,否则必须重命名。
    • <>或or操作不会推送到kudu执行,而是最终由Spark的task来计算。只有以通配结尾的like运算才会推送到kudu执行,比如like foo%,但是like foo%bar则不会推送给Kudu。
    • 并不是Spark SQL中的每种类型Kudu都支持,Date和Complex类型就不支持。
    • Kudu表在Spark SQL中只能注册成临时表,不能使用HiveContext访问。

集成Flink

集成说明

在Spark和Flink先后崛起之后,开始与Hadoop生态中的各个组件整合(官方或者第三方)。Apache Bahir就是一个第三方项目,它对Spark和Flink进行扩展以便于它们整合其他组件(主要针对流处理)。

以下是Apache Bahir的官网:

http://bahir.apache.org/

MMSIZE

Apache Bahir对Flink的支持以子项目bahir-flflink的方式提供,以下是它的github主页:

https://github.com/apache/bahir-flflink

我们就基于bahir-flflink来整合Kudu+Flink,目前支持:

  • 批处理读和写
  • 流处理写(流处理读一般只针对消息队列,对于存储流处理读意义不大)
编译bahir-flflink

bahir-flflink目前1.0版还没正式发布,刚到1.0-rc5,且不支持Kudu。1.1版开始支持Kudu,目前还在1.1-SNAPSHOT版(Kudu1.10.0和Flink1.9.0),因此我们需要自己编译,且一定要在Linux或者MacOS下编译:

编译并安装到maven本地仓库:

git clone https://github.com/apache/bahir-flink.git 
cd bahir-flink/ 
mvn -DskipTests -Drat.skip=true clean install 

执行完之后,我们去maven本地仓库查看:

MSIZE

如果有些依赖包实在下载不下来导致编译不过的话,可以使用老师提供的编译好的包直接安装到maven本地仓库即可:

mvn install:install-file -DgroupId=org.apache.bahir -DartifactId=flink-connector-kudu_2.11 -Dversion=1.1-SNAPSHOT -Dpackaging=jar -Dfile=./flink-connector-kudu_2.11-1.1-SNAPSHOT.jar 
项目准备

修改pom.xml,最终内容如下:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>com.djt</groupId> 
    <artifactId>kudu-tutorial</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <properties> 
        <kudu.version>1.10.0</kudu.version> 
        <junit.version>4.12</junit.version> 
        <scala.version>2.11.8</scala.version> 
        <spark.version>2.4.3</spark.version> 
        <flink.version>1.9.0</flink.version> 
        <flink-connector.version>1.1-SNAPSHOT</flink-connector.version> 
    </properties> 
    <dependencies> 
        <!-- bahir-flink --> 
        <dependency> 
            <groupId>org.apache.bahir</groupId> 
            <artifactId>flink-connector-kudu_2.11</artifactId> 
            <version>${flink-connector.version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.flink</groupId> 
            <artifactId>flink-streaming-java_2.11</artifactId> 
            <version>${flink.version}</version> 
            <scope>provided</scope> 
        </dependency> 
        <!-- Scala --> 
        <dependency> 
            <groupId>org.scala-lang</groupId> 
            <artifactId>scala-library</artifactId> 
            <version>${scala.version}</version> 
        </dependency> 
        <!-- Spark --> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-core_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-sql_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.spark</groupId> 
            <artifactId>spark-hive_2.11</artifactId> 
            <version>${spark.version}</version> 
        </dependency> 
        <!-- kudu-spark --> 
        <dependency> 
            <groupId>org.apache.kudu</groupId> 
            <artifactId>kudu-spark2_2.11</artifactId> 
            <version>${kudu.version}</version> 
        </dependency> 
        <!-- Kudu client --> 
        <dependency> 
            <groupId>org.apache.kudu</groupId> 
            <artifactId>kudu-client</artifactId> 
            <version>${kudu.version}</version> 
        </dependency> 
        <!-- Log --> 
        <dependency> 
            <groupId>org.slf4j</groupId> 
            <artifactId>slf4j-simple</artifactId> 
            <version>1.7.12</version> 
        </dependency> 
        <!-- Unit test --> 
        <dependency> 
            <groupId>junit</groupId> 
            <artifactId>junit</artifactId> 
            <version>${junit.version}</version> 
            <scope>provided</scope> 
        </dependency> 
    </dependencies> 
    <build> 
        <plugins> 
            <plugin> 
                <groupId>org.apache.maven.plugins</groupId> 
                <artifactId>maven-compiler-plugin</artifactId> 
                <version>3.5.1</version> 
                <configuration> 
                    <source>1.8</source> 
                    <target>1.8</target> 
                </configuration> 
            </plugin> 
            <plugin> 
                <groupId>net.alchim31.maven</groupId> 
                <artifactId>scala-maven-plugin</artifactId> 
                <version>3.2.0</version> 
                <executions> 
                    <execution> 
                        <goals> 
                            <goal>compile</goal> 
                            <goal>testCompile</goal> 
                        </goals> 
                        <configuration> 
                            <args>
                                <arg>-dependencyfile</arg> 
                                <arg>${project.build.directory}/.scala_dependencies</arg> 
                            </args> 
                        </configuration> 
                    </execution> 
                </executions> 
            </plugin> 
            <plugin> 
                <groupId>org.apache.maven.plugins</groupId> 
                <artifactId>maven-shade-plugin</artifactId> 
                <version>2.4</version> 
                <executions> 
                    <execution> 
                        <phase>package</phase> 
                        <goals> 
                            <goal>shade</goal> 
                        </goals> 
                    </execution> 
                </executions> 
            </plugin> 
        </plugins> 
    </build> 
    <!-- 指定具体仓库 --> 
    <repositories> 
        <repository> 
            <id>cdh.repo</id> 
            <name>Cloudera Repositories</name> 
            <url>https://repository.cloudera.com/content/repositories/releases</url> 
            <snapshots> 
                <enabled>false</enabled> 
            </snapshots> 
        </repository> 
    </repositories> 
</project> 

骨架代码:

package com.djt.kudu.flink; 
public class KuduFlinkDemo { 
    public static void main(String[] args) { 
        System.out.println("hello kudu flink!"); 
    } 
}

运行能正常输出就OK了。

批处理读写

(1)批处理读

批处理读需要开启Kudu安全,这里就不做演示了。

批处理读使用KuduInputFormat,代码如下:

@Test 
public void testBatchRead() throws Exception { 
    //1、初始化执行环境 
    ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); 
    env.setParallelism(3); 
    //2、构建数据处理逻辑(输入-->处理--输出) 
    //2.1 输入(读取kudu的students表) 
    //a、创建KuduReaderConfig 
    KuduReaderConfig kuduReaderConfig = KuduReaderConfig.Builder 
    .setMasters(KUDU_MASTERS) 
    .build(); 
    //b、创建KuduTableInfo 
    KuduTableInfo tableInfo = KuduTableInfo.Builder 
    .create("students") 
    .replicas(1) 
    .addColumn(KuduColumnInfo.Builder.create("sid", 
                                             Type.INT32).key(true).hashKey(true).build()) 
    .addColumn(KuduColumnInfo.Builder.create("name", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("gender", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("age", Type.INT32).build()) 
    .addColumn(KuduColumnInfo.Builder.create("height", Type.FLOAT).build()) 
    .addColumn(KuduColumnInfo.Builder.create("weight", Type.FLOAT).build()) 
    .build(); 
    //c、创建反序列化器KuduDeserialization 
    KuduDeserialization serDe = new PojoSerDe(Student.class); 
    //d、组装过滤条件 
    List<KuduFilterInfo> tableFilters = new ArrayList<>(); 
    tableFilters.add(KuduFilterInfo.Builder.create("age").greaterThan(18).build()); 
    tableFilters.add(KuduFilterInfo.Builder.create("age").lessThan(20).build()); 
    //e、指定要返回的列 
    List<String> tableProjections = Arrays.asList("sid", "age"); 
    //f、组装KuduInputFormat 
    DataSet<Student> result = env.createInput(new KuduInputFormat(kuduReaderConfig, tableInfo, serDe, new ArrayList<>(), tableProjections), TypeInformation.of(Student.class)); 
    //2.2 处理(包含输出) 
    result.count(); 
    //3、执行job(延迟执行) 
    env.execute(); 
}

(2)批处理写

@Test 
public void testBatchWrite() throws Exception { 
    //1、初始化执行环境 
    ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); 
    env.setParallelism(3); 
    //2、构建数据处理逻辑(输入-->处理--输出) 
    //2.1 输入 
    DataSet<Student> originData= env.readCsvFile("dataset/students100k") 
    .fieldDelimiter("t") 
    .ignoreFirstLine() 
    .ignoreInvalidLines() 
    .pojoType(Student.class,"sid","name","gender","age","height","weight"); 
    //2.2 处理(咱们不处理) 
    //2.3 输出(kudu 表) 
    //a、创建KuduWriterConfig 
    KuduWriterConfig writerConfig=KuduWriterConfig.Builder 
    .setMasters(KUDU_MASTERS) 
    .setWriteMode(KuduWriterMode.UPSERT) 
    .setConsistency(SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND) 
    .build(); 
    //b、创建KuduTableInfo 
    KuduTableInfo tableInfo = KuduTableInfo.Builder 
    .create("students1") 
    .replicas(1) 
    .addColumn(KuduColumnInfo.Builder.create("sid", 
                                             Type.INT32).key(true).hashKey(true).build()) 
    .addColumn(KuduColumnInfo.Builder.create("name", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("gender", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("age", Type.INT32).build()) 
    .addColumn(KuduColumnInfo.Builder.create("height", Type.FLOAT).build()) 
    .addColumn(KuduColumnInfo.Builder.create("weight", Type.FLOAT).build()) 
    .build(); 
    //c、创建KuduSerialization 
    KuduSerialization serDe=new PojoSerDe(Student.class); 
    //d、装配KuduOutputFormat 
    originData.output(new KuduOutputFormat(writerConfig, tableInfo, serDe)); 
    //3、执行job(延迟执行) 
    env.execute(); 
}
流处理写

直接使用KuduSink:

@Test 
public void testKuduSink() throws Exception { 
    List<Student> list = Arrays.asList( 
        new Student(1, "张三", "F", 19, 176.3f, 134.4f), 
        new Student(2, "李四", "F", 20, 186.3f, 154.8f) 
    );
    //1、初始化执行环境 
    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); 
    env.setParallelism(3); 
    //2、构建数据处理逻辑(输入-->处理--输出) 
    //2.1 输入 
    DataStream<Student> originData = env.fromCollection(list); 
    //2.2 处理(咱们不处理) 
    //2.3 输出(kudu 表) 
    //a、创建KuduWriterConfig 
    KuduWriterConfig writerConfig=KuduWriterConfig.Builder 
    .setMasters(KUDU_MASTERS) 
    .setWriteMode(KuduWriterMode.UPSERT) 
    .setConsistency(SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND) 
    .build(); 
    //b、创建KuduTableInfo 
    KuduTableInfo tableInfo = KuduTableInfo.Builder 
    .create("students1") 
    .replicas(1) 
    .addColumn(KuduColumnInfo.Builder.create("sid", 
                                             Type.INT32).key(true).hashKey(true).build()) 
    .addColumn(KuduColumnInfo.Builder.create("name", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("gender", Type.STRING).build()) 
    .addColumn(KuduColumnInfo.Builder.create("age", Type.INT32).build()) 
    .addColumn(KuduColumnInfo.Builder.create("height", Type.FLOAT).build()) 
    .addColumn(KuduColumnInfo.Builder.create("weight", Type.FLOAT).build()) 
    .build(); 
    //c、创建KuduSerialization 
    KuduSerialization serDe=new PojoSerDe(Student.class); 
    //d、装配KuduSink 
    originData.addSink(new KuduSink(writerConfig,tableInfo,serDe)); 
    //3、执行job(延迟执行) 
    env.execute(); 
} 

集成Impala

各组件之间的关系

Kudu整合Impala依赖很多组件,这里有一张组件关系图如下:

MMSIZE

通过上图我们分析结论如下:

  • impala依赖Hive
  • Hive依赖Hadoop
  • Hive依赖MySQL存储元数据
  • Hadoop依赖Zookeeper
  • 基本都离不开JDK

因此,相关组件我们需要先安装。

环境准备

部署规划方案是:

MMSIZE

具体安装省略。

为什么整合Kudu+Impala

Kudu作为高性能的分布式存储同时兼具HDFS和HBase的能力确实能够解决很多业务问题,但是Kudu没有SQL语法支持限制它的使用门槛,因此 Cloudra官方专门把Impala和Kudu做了整合,因此他们的分工是:

  • Kudu负责存储
  • Impala负责计算(用SQL语法分析存储在Kudu表里的数据)

Impala对外支持两种场景:

  • 基于Impala替代Kudu API开发上层应用(可以但不推荐)
  • Impala作为中间层提供JDBC/ODBC跟上层BI或者其他框架整合(推荐)
怎么整合Kudu+Impala

何为整合?让Impala认识并能操作Kudu中的表(内部表/外部表均可):

MMSIZE

Kudu跟Impala的整合非常简单,可总结为两点:

  • Kudu这边什么配置都不用改,它就等着Impala来访问

  • Impala那边有两种方式来访问Kudu

    方式一:每次在Impala中建内部表/外部表时指定Kudu Master(不推荐)

    例如:

    CREATE EXTERNAL TABLE `users` STORED AS KUDU 
    TBLPROPERTIES( 
    'kudu.table_name' = 'users', 
    'kudu.master_addresses' = 'node01:7051,node02:7051,node03:7051') 
    

    方式二:在Impal的默认配置中指定Kudu Master+内部表/外部表:

    在/etc/default/impala中指定:

    --kudu_master_hosts=<master1>[:port],<master2>[:port],<master3>[:port]
    
整合Kudu+Impala

(1)配置Kudu Master地址

在Impala中配置Kudu Master地址(所有节点):

sudo vi /etc/default/impala

在 IMPALA_SERVER_ARGS 下添加如下配置:

--kudu_master_hosts=<master1>[:port],<master2>[:port],<master3>[:port] 

MMSIZE

(2)重启所有Impala服务

node01重启state-store和catalog:

sudo service impala-state-store restart 
sudo service impala-catalog restart 

node02和node03上重启impala-server:

sudo service impala-server restart
Impala Shell中操作Kudu

(1)登录Impala-shell

我们在node01上安装了impala-shell,因此在node01上执行如下命令:

impala-shell -i node02:21000

这时就进入了impala-shell:

MMSIZE

(2)表映射

Impala可以操作很多表:

  • Kudu表
  • HBase表
  • Hive表(各种存储格式:Text、ORC、Parquet等等)
  • 等等

Impala要想操作Kudu表,有两种方式:

  • 外部表

    所谓外部表是指Kudu那边已经建好表了,我们把Kudu表映射为一张Impal表即可,删除表时只删映射关系,Kudu那边的表还在。以前面创建的表students1为例,只需要在impala-shell中创建一个外部表即可:

    CREATE EXTERNAL TABLE `students1` STORED AS KUDU 
    TBLPROPERTIES( 
        'kudu.table_name' = 'students1'); 
    

    或者在指定数据库下创建外表:

    CREATE DATABASE IF NOT EXISTS test; 
    CREATE EXTERNAL TABLE test.users STORED AS KUDU 
    TBLPROPERTIES( 
        'kudu.table_name' = 'users'); 
    
  • 内部表

    所谓内部表,它跟外部表正好相反,是指在Impala中创建一张表存储为Kudu格式,例如:

    CREATE TABLE my_first_table 
    ( 
        id BIGINT, 
        name STRING, 
        PRIMARY KEY(id) 
    )
    PARTITION BY HASH PARTITIONS 16 
    STORED AS KUDU 
    TBLPROPERTIES ('kudu.num_tablet_replicas' = '1'); 
    

    这时他会自动在Kudu中也创建一个表:

    MMSIZE

    注意:删除内部表,Kudu中的表是会删除的。

(3)查询

查询就是SQL语法,大家可以自行尝试,例如:

select gender, count(), max(height) ,min(height), avg(height) from students1 where age <= 19 and height > 180 group by gender; 

查询结果:

MMSIZE

(4)DML

插入数据:

#单行插入 
INSERT INTO my_first_table VALUES (1, "zhangsan"); 
select  from my_first_table; 

MMSIZE

#多行插入 
INSERT INTO my_first_table VALUES (2, "lisi"), (3, "wangwu"), (4, "zhaoliu"); 
select  from my_first_table; 

MMSIZE

CREATE TABLE test2 
( 
    id BIGINT, 
    name STRING, 
    PRIMARY KEY(id) 
)
PARTITION BY HASH PARTITIONS 6
STORED AS KUDU 
TBLPROPERTIES ('kudu.num_tablet_replicas' = '1'); 

select  from test2; 

#从其它表批量导入 
INSERT INTO test2 SELECT  FROM my_first_table; 
select  from my_first_table; 

MMSIZE

更新数据:

#更新 
UPDATE my_first_table SET name="张三" where id =1 ; 
select  from my_first_table; 

MMSIZE

删除数据:

delete from my_first_table where id =3; 
select  from my_first_table; 

MMSIZE

(5)更改表属性

1)重命名impala内部表:

ALTER TABLE my_first_table RENAME TO person; 
show tables; 

MMSIZE

Kudu那边的表也跟着改名了:

MMSIZE

2)重命名impala外部表:

ALTER TABLE students1 RENAME TO stus; 
show tables;

MMSIZE

Kudu那边的表名不会跟着改变(只是改了映射):

MMSIZE

3)将外部表重新映射kudu表

ALTER TABLE external_table 
SET TBLPROPERTIES('kudu.table_name' = 'xxx') 

4)将内部表改为外部表

ALTER TABLE my_table SET TBLPROPERTIES('EXTERNAL' = 'TRUE'); 
谓词下推

所谓谓词简单理解就是SQL的where字句中的条件判断,Impala的原理就是读取Kudu表的数据然后进行计算,如果谓词能够下推到Kudu中去执行则返回给Impala的数据将会很小,性能将大幅提升。目前:

  • 支持下推的谓词: = , <= , < , > , >= , BETWEEN , IN
  • 不支持下推的谓词: != , LIKE , 或者Impala中的其他谓词
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kudu-客户端API编程、生态整合(Spark、Flink、Impala) 的相关文章

  • 人工智能与机器学习:未来的编程范式

    1 背景介绍 人工智能 Artificial Intelligence AI 和机器学习 Machine Learning ML 是现代计算机科学的重要领域之一 它们旨在让计算机能够自主地学习 理解和进化 以解决复杂的问题 随着数据量的增加
  • spark相关

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • 技术管理者的核心能力在哪?

    作为管理者我曾经被下属当面问过 你为什么不写代码 诚然 我最近两年 代码越写越少 会越开越多 但 存在真的合理吗 我的核心能力应该是什么 看了一篇文章 它提出一个观点 技术管理者的核心能力在于技术判断力 通过在技术领域和非技术领域的长期积累
  • 用CHAT如何写大学生会计综合模拟实训报告

    CHAT回复 标题 大学生会计综合模拟实训报告 一 前言 随着信息化时代的发展 现代会计工作不再只依赖手动运算和记录 而是更加倚重电脑软件系统的配合运用 因此 对我们大学生来说 把握会计理论知识的同时 积极掌握相关的实践应用技能变得非常重要
  • Navicat 16 for MySQL:打造高效数据库开发管理工具

    随着数据的快速增长和复杂性的提升 数据库成为了现代应用开发中不可或缺的一部分 而在MySQL数据库领域 Navicat 16 for MySQL作为一款强大的数据库开发管理工具 正受到越来越多开发者的青睐 Navicat 16 for My
  • 38条Web测试经验分享

    1 页面链接检查 每一个链接是否都有对应的页面 并且页面之间切换正确 可以使用一些工具 如LinkBotPro File AIDCS HTML Link Validater Xenu等工具 LinkBotPro不支持中文 中文字符显示为乱码
  • 基于java的饮食分享平台系统设计与实现

    基于java的饮食分享平台系统设计与实现 I 引言 A 研究背景和动机 近年来 随着人们生活水平的提高和健康意识的增强 饮食健康已经成为越来越多人的关注焦点 因此 一个方便快捷的饮食分享平台就显得尤为重要 基于Java的饮食分享平台系统设计
  • 【计算机毕业设计】电商个性化推荐系统

    伴随着我国社会的发展 人民生活质量日益提高 于是对电商个性化推荐进行规范而严格是十分有必要的 所以许许多多的信息管理系统应运而生 此时单靠人力应对这些事务就显得有些力不从心了 所以本论文将设计一套电商个性化推荐系统 帮助商家进行商品信息 在
  • 【计算机毕业设计】趵突泉景区的智慧导游小程序_5ztvv

    当今社会已经步入了科学技术进步和经济社会快速发展的新时期 国际信息和学术交流也不断加强 计算机技术对经济社会发展和人民生活改善的影响也日益突出 人类的生存和思考方式也产生了变化 传统趵突泉景区的智慧导游采取了人工的管理方法 但这种管理方法存
  • 【计算机毕业设计】二手家电管理平台

    时代在飞速进步 每个行业都在努力发展现在先进技术 通过这些先进的技术来提高自己的水平和优势 二手家电管理平台当然不能排除在外 二手家电管理平台是在实际应用和软件工程的开发原理之上 运用java语言以及前台VUE框架 后台SpringBoot
  • 2024 人工智能与大数据专业毕业设计(论文)选题指导

    目录 前言 毕设选题 选题迷茫 选题的重要性 更多选题指导 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生
  • 做测试不会 SQL?超详细的 SQL 查询语法教程来啦!

    前言 作为一名测试工程师 工作中在对测试结果进行数据比对的时候 或多或少要和数据库打交道的 要和数据库打交道 那么一些常用的sql查询语法必须要掌握 最近有部分做测试小伙伴表示sql查询不太会 问我有没有sql查询语法这一块的文档可以学习
  • AI在保护环境、应对气候变化中的作用

    对于AI生命周期数据领域的全球领导者而言 暂时搁置我们惯常的AI见解和AI生命周期数据内容产出 来认识诸如世界地球日这样的自然环境类活动日 似乎是个奇怪的事情 我们想要知道 数据是否真的会影响我们的地球环境 简而言之 是 确实如此 但作为一
  • Redis分布式锁--java实现

    文章目录 Redis分布式锁 方案 SETNX EXPIRE 基本原理 比较好的实现 会产生四个问题 几种解决原子性的方案
  • 毕业设计:基于python人脸识别系统 LBPH算法 sqlite数据库 (源码)✅

    博主介绍 全网粉丝10W 前互联网大厂软件研发 集结硕博英豪成立工作室 专注于计算机相关专业 毕业设计 项目实战6年之久 选择我们就是选择放心 选择安心毕业 感兴趣的可以先收藏起来 点赞 关注不迷路 毕业设计 2023 2024年计算机毕业
  • 温室气体排放更敏感的模型(即更高的平衡气候敏感性(ECS))在数年到数十年时间尺度上也具有更高的温度变化(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据
  • 每日变更的最佳实践

    在优维公司内部 我们采用发布单的方式进行每天的应用变更管理 这里给各位介绍优维的最佳实践 变更是需要多角色合作的 而且他是整体研发流程的一部分 在优维内部 我们坚持每日变更 打通开发环节到最终发布上线的全过程 在保证质量的前提下 尽可能提升
  • 实力认证!鼎捷软件荣膺“领军企业”和“创新产品”两大奖项

    近日 由中国科学院软件研究所 中科软科技股份有限公司联合主办的 2023中国软件技术大会 于北京成功举办 本届大会以 大模型驱动下的软件变革 为主题 数十位来自知名互联网公司和软件巨头企业的技术大咖 不同领域行业专家 畅销书作者等分享嘉宾
  • 执行 powershell 时 Kudu REST API 命令端点错误

    当尝试根据 api command 执行 POST 时这个描述 https github com projectkudu kudu wiki REST API command出现以下错误 PS C gt Result Error remov
  • Azure linux上的Git推送节点js失败,kudu想要运行dotnet命令

    从本地 git 推送到 Azure 应用服务 git 失败 直到昨天为止 这一直运作良好 我真的不明白为什么dotnet当App在Linux中运行时 参与Azure中的推送执行 我在本地从 Windows Powershell 运行 git

随机推荐

  • Halide学习笔记---Halide语言设计的初衷

    Halide语言设计初衷 我们正处于一个数据密集的时代 4D广场相机 图形渲染 3D打印 图像传感器 高质量医学图像等 每天生产大量的图像数据 面对这样一个图像时代 急需要针对图像处理算法设计的高性能图像处理编程语言 在这样的需求下 Hal
  • 第五届蓝桥杯——java c组 1/a 的分数称为单位分数

    形如 1 a 的分数称为单位分数 可以把1分解为若干个互不相同的单位分数之和 例如 1 1 2 1 3 1 9 1 18 1 1 2 1 3 1 10 1 15 1 1 3 1 5 1 7 1 9 1 11 1 15 1 35 1 45 1
  • 一文读懂 React16.0-16.6 新特性(实践 思考)

    首先 把我知道从16 0开始到现在为止新特性全部列举出来 v16 0 render 支持返回数组和字符串 createPortal 支持自定义 DOM 属性 Error Boundary Fiber SSR优化 减少文件体积v16 1 re
  • 对区块链钱包的简单认识

    钱包是存储和使用数字货币的工具 在区块链领域有举足轻重的地位 在对钱包分类之前 需要先理解几个概念 钱包地址 它类似于银行卡号 一个人可以拥有多张银行卡 所以他也可以拥有多个钱包地址 一个钱包地址只能对应一个私钥 在一个钱包中 可以拥有多个
  • VMware导入别人的虚拟机

    根据老师发的文件 将分卷压缩文件下载到一个文件夹中 选择解压第一个文件 即可得到所有资源 可以看出得到如下文件 这里需要导入Vmware中 我查询了几种导入的方法 1 导入OVF 首先选择导入ovf文件 直接点击打开虚拟机 选择导入Ubun
  • Go 每日一库之 cobra

    简介 cobra是一个命令行程序库 可以用来编写命令行程序 同时 它也提供了一个脚手架 用于生成基于 cobra 的应用程序框架 非常多知名的开源项目使用了 cobra 库构建命令行 如Kubernetes Hugo etcd等等等等 本文
  • $('#dg').datagrid('getSelections');", 得到的rows.length在多选情况下总为1, 不选择时为0.

    dg datagrid getSelections 得到的rows length在多选情况下总为1 不选择时为0 方案 title ID field dataId width 80 hidden true 的field一定要与idField
  • 订单系统设计 —— 订单管理

    文章目录 一 方案背景 1 1 考虑因素 1 2 数据特点 二 方案演进 三 MySQL架构 3 1 单库单表 3 2 读写分离 3 3 垂直拆分 3 4 数据归档 冷热分离 3 5 分库分表 方案1 路由表 方案2 哈希 四 MySQL
  • windows linux环境搭建

    1 windows中linux环境wsl 2 powershell scoop https zhuanlan zhihu com p 463284082 powershell通过scoop可以安装各种软件 安装git就是 scoop ins
  • 提升UE5写实效果的项目设置

    随着虚幻引擎5 Unreal Engine 5 简称UE5 的发布 游戏开发者和数字艺术家们迎来了一个全新的机会 可以在其强大的渲染引擎下创建更加逼真和令人惊叹的游戏和虚拟场景 然而 要实现出色的写实效果 需要合理设置项目并运用一些技巧和策
  • 使用IDEA2023创建Servlet模板,使其右键显示Servlet选项

    使用IDEA2023创建Servlet模板 使其右键显示Servlet选项 之前在IDEA2022及以前可以通过一些方法创建Servlet模板 但我不知道为什么2023版本没有作用 下面提供另一种方式实现Servlet的创建 直接参考IDE
  • 系统化程序分析

    左志强 南京大学计算机系副研究员 研究领域包括程序分析 编译技术 系统软件等 本文以技术文章的方式回顾左老师在 SIG 程序分析 技术沙龙上的分享 回顾视频也已经上传 B 站 欢迎小伙伴们点开观看 SIG 程序分析技术沙龙回顾 面向千万行代
  • 单电源运放和双电源运放及其供电方式选择与转换的注意事项

    文章目录 前言 一 运放之双电源供电和单电源供电 1 如何区分单电源运放和双电源运放 2 单电源供电运放特性 3 运放的两种供电模式转换 4 单端偏置的缺陷 二 仿真验证 1 两阶高通滤波放大电路 两端偏置 2 两阶高通滤波放大电路 单端偏
  • 20-10-026-安装-KyLin-2.6.0-单机版安装(MAC官网下载)-spark引擎

    文章目录 1 视界 1 官网 2 安装要求 2 1 软件要求 2 2 硬件要求 2 3 Hadoop 环境 3 本次环境 4 HBASE 1 2 0安装 5 kylin 安装 6 检查zk jar 7 启动Hbase 8 添加依赖 9 添加
  • VS附加到进程调试

    操作 要附加到进程中调试外部可执行文件 您需要使用Visual Studio的 调试附加 功能 以下是附加到进程中调试外部可执行文件的步骤 打开您要调试的源代码文件或可执行文件 打开Visual Studio 选择 调试 菜单 然后选择 附
  • ffmpeg读取rtsp并保存到mp4文件

    本文章只讲述mp4文件的录像 至于音频录入 会在下个文章中介绍 总体思路为 初始化 连接相机获取码流 读取码流中的视频 创建输出mp4上下文 写mp4头 循环读取码流 写入mp4 写文件尾 关闭文件 第一步 初始化网络环境 环境注册 av
  • 信号、signal 函数、sigaction 函数

    文章目录 1 信号的基本概念 2 利用 kill 命令发送信号 3 信号处理的相关动作 4 信号与 signal 函数 4 1 signal 函数示例一 4 2 signal 函数示例二 5 利用 sigaction 函数进行信号处理 6
  • mysql对表的操作

    mysql对表的操作 表的概念 表是包含数据库中所有数据的数据库对象 表中的数据库对象包含列 索引 触发器 其中触发器是指用户定义的事务命令集合 当对一个表中的数据进行插入 更新或者删除时 这组命令就会自动执行 可以确保数据的安全性和完整性
  • npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本

    npm 无法加载文件 D Nodejs node global npm ps1 因为在此系统上禁止运行脚本 1 问题详情 2 解决方法 1 问题详情 npm 无法加载文件 D Nodejs node global npm ps1 因为在此系
  • Kudu-客户端API编程、生态整合(Spark、Flink、Impala)

    文章目录 Kudu客户端API编程 客户端API核心类 Java编程接口 环境准备 创建表 插入数据 查询数据 修改表结构 更新数据 删除数据 更新和插入 删除表 Hadoop生态整合 整合概述 集成Spark Spark shell中操作