特性速覽| Apache Hudi 0.5.3版本正式發布

1. 下載連接

  • 源代碼下載:Apache Hudi 0.5.3 Source Release (asc, sha512)
  • 0.5.3版本相關jar包地址:https://repository.apache.org/#nexus-search;quick~hudi

2. 遷移指南

  • 這是一個bugfix版本,從0.5.2升級時不需要任何特殊的遷移步驟。如果要從早期版本”X”升級,請閱讀”X”和0.5.3之間的每個後續版本的遷移指南。
  • 0.5.3是Hudi畢業后的第一個版本,因此所有hudi jar的版本名稱中不再帶有”-incubating”。在所有提及hudi版本的地方,請確保不再存在”-incubating”。

例如,hudi-spark-bundle pom依賴如下所示:

<dependency>
	<groupId>org.apache.hudi</groupId>
	<artifactId>hudi-spark-bundle_2.12</artifactId>
	<version>0.5.3</version>
</dependency>

3. 關鍵特性

  • Hudi內置支持 aliyun OSS 對象存儲。

  • 默認情況下將為delta-streamer和spark datasource寫入啟用Embedded Timeline Server。在此版本之前,此功能處於實驗模式,embeddedTimeline Server在Spark Driver中緩存文件列表,並提供Restful接口給Spark Writer任務調用來減少了每次寫入時的list文件列表的操作,此優化對雲上對象存儲非常友好。

  • 默認情況下為delta-streamer和Spark datasource寫入均啟用”增量清理(incremental cleaning)”。在此版本之前,此功能還處於實驗模式,在穩定狀態下,增量清理避免了掃描所有分區的昂貴步驟,而是使用Hudi元數據來查找要清理的文件,此優化也對雲上對象存儲非常友好。

  • 支持將Delta-Streamer配置文件放置在與實際數據不同的文件系統中。

  • Hudi Hive Sync現在支持按日期類型列分區的表。

  • Hudi Hive Sync現在支持直接通過Hive MetaStore進行同步。您只需要設置hoodie.datasource.hive_sync.use_jdbc = false。Hive Metastore Uri將從environment中隱式讀取。例如當通過Spark datasource寫入時,

     spark.write.format(“hudi”)
     .option(…)
     .option(“hoodie.datasource.hive_sync.username”, “<user>”)
     .option(“hoodie.datasource.hive_sync.password”, “<password>”)
     .option(“hoodie.datasource.hive_sync.partition_fields”, “<partition_fields>”)
     .option(“hoodie.datasource.hive_sync.database”, “<db_name>”)
     .option(“hoodie.datasource.hive_sync.table”, “<table_name>”)
     .option(“hoodie.datasource.hive_sync.use_jdbc”, “false”)
     .mode(APPEND)
     .save(“/path/to/dataset”)
    
  • 支持Presto查詢MoR表時Hudi側的改造。

  • 其他與Writer Performance相關的缺陷修復。

    • 現在DataSource Writer避免了寫入后不必要的數據加載。
    • Hudi Writer現在利用spark的併發來加速小文件查找。

4. 感謝

感謝如下貢獻者(排名不分先後): @bhasudha,@yanghua ,@ddong ,@smarthi ,@afilipchik,@zhedoubushishi,@umehrot2,@varadar,@ffcchi,@bschell,@vinothchandar ,@shenh062326,@lamber-ken,@zhaomin1423,@EdwinGuo,@prashantwason ,@pratyakshsharma,@dengziming ,@AakashPradeep,@Jecarm ,@xushiyan ,@cxzl25,@garyli1019 ,@rolandjohann ,@nsivabalan,@leesf ,@jfrazee

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

聚甘新

一起玩轉微服務(5)——分層架構

領域驅動設計DDD(Domain Driven Design)提出了從業務設計到代碼實現一致性的要求,不再對分析模型和實現模型進行區分。也就是說從代碼的結構中我們可以直接理解業務的設計,命名得當的話,非程序人員也可以“讀”代碼。這與微服務設計中的約定優於配置不謀而合,如果你熟悉英文,那麼直接根據包名和類名就可以直接解讀出程序開發者所構建的業務的大概意圖。

領域模型包含一些明確定義的類型:

  • 實體是一個對象,它有固定的身份,具有明確定義的”連續性線索”或生命周期。通常列舉的示例是一個 Person(一個實體)。大多數系統都需要唯一地跟蹤一個 Person,無論姓名、地址或其他屬性是否更改。
  • l值對象沒有明確定義的身份,而僅由它們的屬性定義。它們通常不可變,所以兩個相等的值對象始終保持相等。地址可以是與 Person 關聯的值對象。
  • l集合是一個相關對象集群,這些對象被看作一個整體。它擁有一個特定實體作為它的根,並定義了明確的封裝邊界。它不只是一個列表。
  • l服務用於表示不是實體或值對象的自然部分的操作或活動。

領域模型在實現時可大可小,在業務的早期,在系統比較小的情況下,它有可能是一個類。當系統做大了以後,它可能是個庫。再做更大一點的時候,它可能是一個服務,給不同的應用去調用。

要將領域元素轉換為服務,可按照以下一般準則來完成此操作:

  • 使用值對象的表示作為參數和返回值,將集合和實體轉換為獨立的微服務。
  • 將領域服務(未附加到集合或實體的服務)與獨立的微服務相匹配。
  • 每個微服務應處理一個完整的業務功能。

領域模型又可以分為失血、貧血和充血3種。

  • 失血模型:基於數據庫的領域設計方式就是典型的失血模型,只關注數據的增刪改查。
  • 貧血模型:就是在domain object包含了不依賴於持久化的領域邏輯,而那些依賴持久化的領域邏輯被分離到server層。
  • 充血模型:充血模型跟貧血模型差不多,不同的是如何劃分業務邏輯,就是說,約大部分業務應該放到domain object裏面,而service應該是很薄的一層。

設計原則之分層架構

同一公司使用統一應用分層,以減少開發維護學習成本。應用分層這件事情看起來很簡單,但每個程序員都有自己的一套,哪怕是初學者,所以想實施起來並非那麼容易。

最早接觸分層架構的應該是我們最熟悉的MVC(Model-View-Controller)架構,將應用分成了模型、視圖和控制層,可以說引導了絕大多數開發者,而我們現在的應用中非常多的包括框架,架構設計都使用此模式。這后又演化出了MVP(Model-View-Presenter)和MVVM(Model-View-ViewModel)。這些可以說都是隨着技術的不斷髮展,為了應對不同場景所演化出來的模型。而微服務的每個架構都可以再細分成領域模型,下面看一下經典的領域模型架構。

它包括了Domain,Service Layer和Repositories。核心實體(Entity)和值對象(Value Object)應該在Domain層,定義的領域服務(Domain Service)在Service Layer,而針對實體和值對象的存儲和查詢邏輯都應該在Repositories層。值得注意的是,不要把Entity的屬性和行為分離到Domain和Service兩層中去實現,即所謂的貧血模型,事實證明這樣的實現方式會造成很大的維護問題。基於這種設計,工程的結構可以構造為:

– MicroService-Sample/src/

    domain

    gateways

    interface

    repositories

    services

當然,在微服務的架構中,每個微服務不必嚴格遵照這樣的規定,切忌死搬硬套,最重要的是理解,在不同的業務場合,架構的設計可以適當的做調整,畢竟適合的架構一定要具有靈活性。

分層的原則包括:

  • 文件夾分層法

應用分層採用文件夾方式的優點是可大可小、簡單易用、統一規範,可以包括 5 個項目,也可以包括 50 個項目,以滿足所有業務應用的多種不同場景。

  • 調用規約

在開發過程中,需要遵循分層架構的約束,禁止跨層次的調用。

  • 下層為上層服務

以用戶為中心,以目標為導向。上層(業務邏輯層)需要什麼,下層(數據訪問層)提供什麼,而不是下層(數據訪問層)有什麼,就向上層(業務邏輯層)提供什麼。

  • 實體層規約

Entity是數據表對象,不是數據訪問層對象;DTO 是網絡傳輸對象,不是表現層對象;BO 是內存計算邏輯對象,不是業務邏輯層對象,不是只能給業務邏輯層使用 。如果僅限定在本層訪問,則導致單個應用內大量沒有價值的對象轉換。以用戶為中心來設計實體類,可以減少無價值重複對象和無用轉換。

  • U 型訪問

下行時表現層是 Input,業務邏輯層是 Process,數據訪問層是 Output。上行時數據訪問層是 Input,業務邏輯層是 Process,  表現層就 Output。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

Mybatis Generator逆向工程的使用

一、MyBatis Generator簡介

    MyBatis Generator(MBG)是MyBatis和iBATIS的代碼生成器。它將為所有版本的MyBatis以及版本2.2.0之後的iBATIS版本生成代碼。它將審查數據庫表(或許多表),並將生成可用於訪問表的構件。這減少了設置對象和配置文件以與數據庫表交互的初始麻煩。MBG尋求對簡單CRUD(創建,檢索,更新,刪除)的大部分數據庫操作產生重大影響。您仍然需要為連接查詢或存儲過程手動編寫SQL和對象代碼。MyBatis Generator將生成:

  • 與表結構匹配的Java POJO。這可能包括:

    • 一個匹配表的主鍵的類(如果有主鍵)

    • 一個匹配表的非主鍵字段的類(BLOB字段除外)

    • 包含表的BLOB字段的類(如果表具有BLOB字段)

    • 用於啟用動態選擇,更新和刪除的類

    這些類之間存在適當的繼承關係。請注意,生成器可以配置為生成不同類型的POJO層次結構 – 例如,如果您願意,可以選擇為每個表生成單個域對象。

  • MyBatis/iBATIS兼容的SQL Map XML文件。MBG為配置中的每個表上的簡單CRUD函數生成SQL。生成的SQL語句包括:

    • insert 插入

    • update by primary key  按主鍵更新

    • update by example (using a dynamic where clause)  通過條件更新(使用動態where子句)

    • delete by primary key  按主鍵刪除

    • delete by example (using a dynamic where clause)  按條件刪除(使用動態where子句)

    • select by primary key  按主鍵查詢

    • select by example (using a dynamic where clause)  按條件查詢(使用動態where子句)

    • count by example  按條件查詢記錄總數

    根據表結構的不同,這些語句有不同的變體(例如,如果表沒有主鍵,則MBG不會通過主鍵功能生成更新)。

    • 適當使用上述對象的Java客戶端類。Java客戶端類的生成是可選的。MBG將為MyBatis 3.x生成以下類型的Java客戶端:

      • 適用於MyBatis 3.x映射器基礎結構的映射器接口

MBG將為iBATIS 2.x生成以下類型的Java客戶端:

    • 符合Spring框架的DAO

    • 僅使用iBATIS SQL映射API的DAO。這些DAO可以生成兩種:通過構造函數或setter注入提供SqlMapClient。

    • 符合iBATIS DAO框架的DAO(iBATIS的可選部分,現在不推薦使用此框架,我們建議您使用Spring框架)

    MyBatis生成器設計為在迭代開發環境中運行良好,並且可以作為Ant任務或Maven插件包含在連續構建環境中。迭代運行MBG時需要注意的重要事項包括:

  1. 如果存在與新生成的XML文件同名的現有文件,MBG將自動合併XML文件。MBG不會覆蓋您對其生成的XML文件所做的任何自定義更改。您可以反覆運行它,而不必擔心會丟失對XML的自定義更改。MBG將替換先前運行中生成的任何XML元素。

  2. MBG不會合併Java文件,它可以覆蓋現有文件或使用不同的唯一名稱保存新生成的文件。如果對生成的Java文件進行更改並以迭代方式運行MBG,則必須手動合併更改。當作為Eclipse插件運行時 ,MBG可以自動合併Java文件。

二、MyBatis Generator使用

1、新建MBG的配置文件generatorConfig.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
 
 
    <!--導入屬性配置-->
    <properties resource="generator.properties"></properties>
    <!--指定特定數據庫的jdbc驅動jar包的位置-->
    <!--<classPathEntry location="${jdbc.driverLocation}"/>-->
 
    <context id="default" targetRuntime="MyBatis3">
 
        <!-- optional,旨在創建class時,對註釋進行控制,false生成註釋,true無註釋 -->
        <commentGenerator>
            <property name="suppressDate" value="false"/>
            <property name="suppressAllComments" value="false"/>
        </commentGenerator>
 
        <!--jdbc的數據庫連接 -->
        <jdbcConnection
                driverClass="${jdbc.driverClass}"
                connectionURL="${jdbc.connectionURL}"
                userId="${jdbc.userId}"
                password="${jdbc.password}">
        </jdbcConnection>
 
 
        <!--
         默認false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,
         為 true時把JDBC DECIMAL 和 NUMERIC 類型解析為java.math.BigDecimal
        -->
        <!-- 非必需,類型處理器,在數據庫類型和java類型之間的轉換控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
 
 
        <!-- Model模型生成器,用來生成含有主鍵key的類,記錄類 以及查詢Example類
            targetPackage     指定生成的model生成所在的包名
            targetProject     指定在該項目下所在的路徑|指定生成到的工程名稱
        -->
        <javaModelGenerator targetPackage="com.test.model"
                            targetProject=".\src\main\java">
            <!-- 是否允許子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否對model添加 構造函數 true添加,false不添加-->
            <property name="constructorBased" value="false"/>
            <!-- 是否對類CHAR類型的列的數據進行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model對象是否 不可改變  即生成的Model對象不會有 setter方法,只有構造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>
 
        <!--Mapper映射文件生成所在的目錄 為每一個數據庫的表生成對應的SqlMapper文件 -->
        <sqlMapGenerator targetPackage="com.test.mapper"
                         targetProject=".\src\main\java">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
 
        <!-- 客戶端代碼,生成易於使用的針對Model對象和XML配置文件 的代碼
                type="ANNOTATEDMAPPER",生成Java Model 和基於註解的Mapper對象
                type="MIXEDMAPPER",生成基於註解的Java Model 和相應的Mapper對象
                type="XMLMAPPER",生成SQLMapper XML文件和獨立的Mapper接口
        -->
        <javaClientGenerator targetPackage="com.test.mapper"
                             targetProject=".\src\main\java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
 
        <!--需要映射的數據庫的表名-->
        <table tableName="t_userinfo" domainObjectName="UserInfo"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
    </context>
</generatorConfiguration>

2、新建generator.properties文件

jdbc.driverLocation=C:\\mysql-connector-java-5.1.43.jar
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/mybatis
jdbc.userId=root
jdbc.password=tiger

3、配置執行mybatis generator操作,這裡有兩種方式

第1種方式:如果使用maven項目就可以省去編寫Java啟動類,使用maven插件和配置文件pom.xml即可,插件啟動maven-generator,在pom.xml中添加maven-generator插件

<plugins>
    <!--myBatis逆向工程插件-->
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.2</version>
        <configuration>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
            <configurationFile>${project.basedir}/src/main/resources/generatorConfig.xml</configurationFile>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.35</version>
            </dependency>
        </dependencies>
    </plugin>
</plugins>

點擊mybatis-generator:generate就能執行mybatis generator了

第2種方式:

1、如果不是maven項目添加該mybatis-generator-core-1.3.2.jar,編寫main方法指向mybatis逆向工程,我給依賴粘貼到下面了

<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

2、修改generatorConfig.xml文件,放開註釋的該配置

<classPathEntry location="${jdbc.driverLocation}"/>

3、然後編寫測試類執行

/**
 * 如果不是maven項目可以這樣生成
 */
public class MybatisGeneratorTest {
    public static void main(String[] args) throws InterruptedException, SQLException, IOException, InvalidConfigurationException, XMLParserException {
        List<String> warnings = new ArrayList<String>();
        //生成的java文件是否覆蓋
        boolean overwrite = true;
        //指定逆向工程配置文件
        //File configFile = new File("E:\\project\\mybatis-generator\\src\\main\\resources\\generatorConfig.xml");
        InputStream resourceAsStream = MybatisGeneratorTest.class.getClassLoader().getResourceAsStream("generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(resourceAsStream);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings);
        myBatisGenerator.generate(null);
    }
}

介紹完這兩種方式,查看數據表:

查看生成的實體類:


TIP:可以看出如果實體類想要遵循駝峰命名規範,數據庫表字段名設計需要用”_”來劃分

查看生成的文件信息:


TIP1:必須在<plugin></plugin>標籤里添加數據庫驅動,在其他地方添加無效,如果不添加會報找不到驅動錯誤,如過不在該插件添加數據庫依賴的話可以使用 <classPathEntry location=”${jdbc.driverLocation}”/> 來指定數據庫驅動位置。

TIP2:如果你在使用mybatis generator插件執行的時候報[ERROR] Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.2:generate (default-cli) on project mybatis-generator: <properties> resource generator.properties does not exist -> [Help 1]


儘管你的 <properties resource=”generator.properties”></properties>配置的沒有問題,但是還是找不到generator.properties,查看該配置,註釋掉


該配置會改變generatorConfig.xml中讀取generator.properties文件的默認路徑

TIP3:Mybatis Generator反向工程默認不會覆蓋生成的*.java文件。也可以設置覆蓋生成的*.java文件,在反向工程插件mybatis-generator-maven-plugin添加該配置<overwrite>true</overwrite>則會覆蓋生成的*.java文件,如圖


    Mybatis Generator不會覆蓋你的mapper.xml文件,MBG會合併追加到mapper.xml和你自定義的存在一起,但是如果你修改MBG第一次默認生成的SQL(MBG生成的CRUD),MBG會重新把自己生成的CRUD恢復默認,說白了,MBG只會覆蓋他自己生成的SQL,不會覆蓋你自定義的,你自定義的不變。。。如圖,他不會動你的自定義SQL,只會覆蓋Mybatis反向工程自己生成的SQL,前提MBG自動生成SQL語句的註釋要存在。


在最常見的用例中,MyBatis Generator(MBG)由XML配置文件驅動。配置文件告訴MBG

  • 如何連接到數據庫

  • 生成什麼對象,以及如何生成它們

  • 應使用哪些表生成對象

官方MBG配置文件詳解地址:http://mybatis.org/generator/configreference/xmlconfig.html

附帶一個MBG的中文配置文件詳解:https://www.jianshu.com/p/e09d2370b796

 

更多Mybatis逆向工程的使用參考:http://www.mybatis.org/generator/index.html


● XStream學習手冊

● 別在 Java 代碼里亂打日誌了,這才是正確的打日誌姿勢!

● 高可用Redis服務架構分析與搭建

● 8 種方案,幫你解決重複提交問題!請拿走

● IDEA 解決 Maven 依賴衝突的高能神器,這一篇夠不夠?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

機器學習——打開集成方法的大門,手把手帶你實現AdaBoost模型

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是機器學習專題的第25篇文章,我們一起來聊聊AdaBoost。

我們目前為止已經學過了好幾個模型,光決策樹的生成算法就有三種。但是我們每次進行分類的時候,每次都是採用一個模型進行訓練和預測。我們日常在做一個決策的時候,往往會諮詢好幾個人,綜合採納他們的意見。那麼有沒有可能把這個思路照搬到機器學習領域當中,創建多個模型來綜合得出結果呢?

這當然是可以的,這樣的思路就叫做集成方法(ensemble method)。

集成方法

集成方法本身並不是某種具體的方法或者是算法,只是一種訓練機器學習模型的思路。它的含義只有一點,就是訓練多個模型,然後將它們的結果匯聚在一起。

根據這個思路,業內又衍生出了三種特定的方法,分別是Bagging、Boosting和Stacking。

Bagging

Bagging是bootstrap aggregating的縮寫,我們從字面上很難理解它的含義。我們記住這個名字即可,在Bagging方法當中,我們會通過有放回隨機採樣的方式創建K個數據集。對於每一個數據集來說,可能有一些單個的樣本重複出現,也可能有一些樣本從沒有出現過,但整體而言,每個樣本出現的概率是相同的。

之後,我們用抽樣出來的K個數據集訓練K個模型,這裏的模型沒有做限制,我們可以使用任何機器學習方模型。K個模型自然會得到K個結果,那麼我們採取民主投票的方式對這K個模型進行聚合。

舉個例子說,假設K=25,在一個二分類問題當中。有10個模型預測結果是0,15個模型預測結果是1。那麼最終整個模型的預測結果就是1,相當於K個模型民主投票,每個模型投票權一樣。大名鼎鼎的隨機森林就是採取的這種方式。

Boosting

Boosting的思路和Bagging非常相似,它們對於樣本的採樣邏輯是一致的。不同的是,在Boosting當中,這K個模型並不是同時訓練的,而是串行訓練的。每一個模型在訓練的時候都會基於之前模型的結果,更加關注於被之前模型判斷錯誤的樣本。同樣,樣本也會有一個權值,錯誤判斷率越大的樣本擁有越大的權值。

並且每一個模型根據它能力的不同,會被賦予不同的權重,最後會對所有模型進行加權求和,而不是公平投票。由於這個機制,使得模型在訓練的時候的效率也有差異。因為Bagging所有模型之間是完全獨立的,我們是可以採取分佈式訓練的。而Boosting中每一個模型會依賴之前模型的效果,所以只能串行訓練。

Stacking

Stacking是Kaggle比賽當中經常使用的方法,它的思路也非常簡單。我們選擇K種不同的模型,然後通過交叉驗證的方式,在訓練集上進行訓練和預測。保證每個模型都對所有的訓練樣本產出一個預測結果。那麼對於每一條訓練樣本,我們都能得到K個結果。

之後,我們再創建一個第二層的模型,它的訓練特徵就是這K個結果。也就是說Stacking方法當中會用到多層模型的結構,最後一層模型的訓練特徵是上層模型預測的結果。由模型自己去訓練究竟哪一個模型的結果更值得採納,以及如何組合模型之間的特長。

我們今天介紹的AdaBoost顧名思義,是一個經典的Boosting算法。

模型思路

AdaBoost的核心思路是通過使用Boosting的方法,通過一些弱分類器構建出強分類器來。

強分類器我們都很好理解,就是性能很強的模型,那麼弱分類器應該怎麼理解呢?模型的強弱其實是相對於隨機結果來定義的,比隨機結果越好的模型,它的性能越強。從這點出發,弱分類器也就是只比隨機結果略強的分類器。我們的目的是通過設計樣本和模型的權重,使得可以做出最佳決策,將這些弱分類器的結果綜合出強分類器的效果來。

首先我們會給訓練樣本賦予一個權重,一開始的時候,每一條樣本的權重均相等。根據訓練樣本訓練出一個弱分類器並計算這個分類器的錯誤率。然後在同一個數據集上再次訓練弱分類器,在第二次的訓練當中,我們將會調整每個樣本的權重。其中正確的樣本權重會降低,錯誤的樣本權重會升高

同樣每一個分類器也會分配到一個權重值,權重越高說明它的話語權越大。這些是根據模型的錯誤率來計算的。錯誤率定義為:

這裏的D表示數據集表示分類錯誤的集合,它也就等於錯誤分類的樣本數除以總樣本數。

有了錯誤率之後,我們可以根據下面這個公式得到

得到了之後,我們利用它對樣本的權重進行更新,其中分類正確的權重更改為:

分類錯誤的樣本權重更改為:

這樣,我們所有的權重都更新完了,這也就完成了一輪迭代。AdaBoost會反覆進行迭代和調整權重,直到訓練錯誤率為0或者是弱分類器的數量達到閾值。

代碼實現

首先,我們來獲取數據,這裏我們選擇了sklearn數據集中的乳腺癌預測數據。和之前的例子一樣,我們可以直接import進來使用,非常方便:

import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer

breast = load_breast_cancer()
X, y = breast.data, breast.target
# reshape,將一維向量轉成二維
y = y.reshape((-1, 1))

接着,我們將數據拆分成訓練數據和測試數據,這個也是常規做法了,沒有難度:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=23)

在AdaBoost模型當中,我們選擇的弱分類器是決策樹的樹樁。所謂的樹樁就是樹深為1的決策樹。樹深為1顯然不論我們怎麼選擇閾值,都不會得到特別好的結果,但是由於我們依然會選擇閾值和特徵,所以結果也不會太差,至少要比隨機選擇要好。所以這就保證了,我們可以得到一個比隨機選擇效果略好一些的弱分類器,並且它的實現非常簡單。

在我們實現模型之前,我們先來實現幾個輔助函數。

def loss_error(y_pred, y, weight):
    return weight.T.dot((y_pred != y_train))

def stump_classify(X, idx, threshold, comparator):
    if comparator == 'lt':
        return X[:, idx] <= threshold
    else:
        return X[:, idx] > threshold
    
def get_thresholds(X, i):
    min_val, max_val = X[:, i].min(), X[:, i].max()
    return np.linspace(min_val, max_val, 10)

這三個函數應該都不難理解,第一個函數當中我們計算了模型的誤差。由於我們每一個樣本擁有一個自身的權重,所以我們對誤差進行加權求和。第二個函數是樹樁分類器的預測函數,邏輯非常簡單,根據閾值比較大小。這裡有兩種情況,有可能小於閾值的樣本是正例,也有可能大於閾值的樣本是正例,所以我們還需要第三個參數記錄這個信息。第三個函數是生成閾值的函數,由於我們並不需要樹樁的性能特別好,所以我們也沒有必要去遍歷閾值的所有取值,簡單地把特徵的範圍劃分成10段即可。

接下來是單個樹樁的生成函數,它等價於決策樹當中選擇特徵進行數據拆分的函數,邏輯大同小異,只需要稍作修改即可。

def build_stump(X, y, weight):
    m, n = X.shape
    ret_stump, ret_pred = None, []
    best_error = float('inf')

    # 枚舉特徵
    for i in range(n):
        # 枚舉閾值
        for j in get_thresholds(X, i):
            # 枚舉正例兩種情況
            for c in ['lt', 'gt']:
                # 預測並且求誤差
                pred = stump_classify(X, i, j, c).reshape((-1, 1))
                err = loss_error(pred, y, weight)
                # 記錄下最好的樹樁
                if err < best_error:
                    best_error, ret_pred = err, pred.copy()
                    ret_stump = {'idx': i, 'threshold': j, 'comparator': c} 
    return ret_stump, best_error, ret_pred

接下來要做的就是重複生成樹樁的操作,計算,並且更新每一條樣本的權重。整個過程也沒有太多的難點,基本上就是照着實現公式:

def adaboost_train(X, y, num_stump):
    stumps = []
    m = X.shape[0]
    # 樣本權重初始化,一開始全部相等
    weight = np.ones((y_train.shape[0], 1)) / y_train.shape[0]
    # 生成num_stump個樹樁
    for i in range(num_stump):
        best_stump, err, pred = build_stump(X, y, weight)
        # 計算alpha
        alpha = 0.5 * np.log((1.0 - err) / max(err, 1e-10))
        best_stump['alpha'] = alpha
        stumps.append(best_stump)

        # 更新每一條樣本的權重
        for j in range(m):
            weight[j] = weight[j] * (np.exp(-alpha) if pred[j] == y[j] else np.exp(alpha))
        weight = weight / weight.sum()
        # 如果當前的準確率已經非常高,則退出
        if err < 1e-8:
            break
    return stumps

樹樁生成結束之後,最後就是預測的部分了。整個預測過程依然非常簡單,就是一個加權求和的過程。這裏要注意一下,我們在訓練的時候為了突出錯誤預測的樣本,讓模型擁有更好的能力,維護了樣本的權重。然而在預測的時候,我們是不知道預測樣本的權重的,所以我們只需要對模型的結果進行加權即可。

def adaboost_classify(X, stumps):
    m = X.shape[0]
    pred = np.ones((m, 1))
    alphs = 0.0
    for i, stump in enumerate(stumps):
        y_pred = stump_classify(X, stump['idx'], stump['threshold'], stump['comparator'])
        # 根據alpha加權求和
        pred = y_pred * stump['alpha']
        alphs += stump['alpha']
    pred /= alphs
    # 根據0.5劃分0和1類別
    return np.sign(pred).reshape((-1, 1))

到這裏,我們整個模型就實現完了,我們先來看下單個樹樁在訓練集上的表現:

可以看到準確率只有0.54,只是比隨機預測略好一點點而已。

然而當我們綜合了20個樹樁的結果之後,在訓練集上我們可以得到0.9的準確率。在預測集上,它的表現更好,準確率有接近0.95!

這是因為AdaBoost當中,每一個分類器都是弱分類器,它根本沒有過擬合的能力,畢竟在訓練集的表現都很差,這就保證了分類器學到的都是實在的泛化能力,在訓練集上適用,在測試集上很大概率也適用。這也是集成方法最大的優點之一。

總結

集成方法可以說是機器學習領域一個非常重要的飛躍,集成方法的出現,讓設計出一個強分類器這件事的難度大大降低,並且還保證了模型的效果。

因為在一些領域當中,設計一個強分類器可能非常困難,然而設計一個弱一些的分類器則簡單得多,再加上模型本身性能很好,不容易陷入過擬合。使得在深度學習模型流行之前,集成方法廣泛使用,幾乎所有機器學習領域的比賽的冠軍,都使用了集成學習。

集成學習當中具體的思想或許各有不同,但是核心的思路是一致的。我們理解了AdaBoost之後,再去學習其他的集成模型就要容易多了。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

聚甘新

Java 從入門到進階之路(二十四)

在之前的文章我們介紹了一下 Java 中的  集合框架中的Collection 的泛型,本章我們來看一下 Java 集合框架中的Collection 的子接口 List。

Collection 接口有 3 種子類型,List、Set 和 Queue,其中 List 和 Set 的區別是 Set 中不能存放相同的元素,而 List 中可以,本章我們就來介紹一下 List。

 

 

 從上圖我們可以知道 List 有兩個實例類,ArrayList 和 LinkedList,

ArrayList 是數組實現,查找快,增上慢,由於是數組實現,在增和刪的時候牽扯到數組的增容,以及靠背元素,所以慢,數組是可以直接按索引查找,所以查找時較快。

LinkedList 是鏈表實現,增刪快,查找慢,由於鏈表實現,增加時只要讓前一個元素記住自己就可以了,刪除時讓前一個元素記住后一個元素,后一個元素記住前一個元素,這樣的增刪效率高但查詢時需要一個一個遍歷,所以效率低。

LinkedList 我們可以形象的比作老式手錶的鏈子,一節扣一節,增刪時只需要打開兩個之間的節扣即可,不需要牽扯到其他節扣。

ArrayList 和 LinkedList 都有各自的優缺點,在用的時候可以根據需求自行選擇,避免性能消耗。在現在計算機計算能力越來越強,做的也不是大型項目的時候,這兩個之間的性能差異我們其實是可以忽略的。

接下來我們就來看一下 List 接口的一些基礎用法,如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 /**
 5  * java.util.List
 6  * 可重複集合,並且有序
 7  * 特點是可以根據下錶操作元素
 8  * ArrayList:使用數組實現,查詢更快
 9  * LinkedList:使用鏈表實現,增刪更快(收尾增刪效果更明顯)
10  */
11 
12 public class Main {
13     public static void main(String[] args) {
14         List<String> list = new ArrayList<String>();
15         list.add("one");
16         list.add("two");
17         list.add("three");
18         list.add("four");
19         /**
20          * E set(int index, E e)
21          * 將給定元素設置到制定位置上,返回原位置的元素
22          * 所以是替換元素操作
23          * 如果超出了元素的長度,則使用 add 添加,否則編譯錯誤
24          * */
25         String old = list.set(1, "2"); // 將下標為 1 的元素改為 2,返回值則是被替換的元素
26         System.out.println(old); // two
27         System.out.println(list); // [one, 2, three, four]
28 
29         /**
30          * E get(int index)
31          * 獲取給定下標對應的元素
32          * */
33         String two = list.get(1); // 獲取第二個元素
34         System.out.println(two); // 2
35 
36         /**
37          * 可以通過傳統的循環遍歷 List 集合
38          * */
39         for (int i = 0; i < list.size(); i++) {
40             System.out.println(list.get(i)); // one 2 three four
41         }
42     }
43 }

在上面的代碼中,我們通過 set 和 get 方法來設置和獲取我們想要的下標的元素,當然還有其他方法,如下:

 1 /**
 2  * List 集合提供了一對重載的 add,remove 方法
 3  * void add(int index, E e)
 4  * 將給定元素插入到指定位置,
 5  * 如果不指定下標,則插入到末尾
 6  * <p>
 7  * E remove(int index)
 8  * 從集合中刪除指定位置的元素,並將其返回
 9  */
10 
11 public class Main {
12     public static void main(String[] args) {
13         List<String> list = new ArrayList<String>();
14         list.add("one");
15         list.add("two");
16         list.add("three");
17         list.add("four");
18 
19         list.add(1, "2"); // 將下標為 1 的元素插入 2
20         System.out.println(list); // [one, 2, two, three, four]
21 
22         String string = list.remove(1); // 將下標為 1 的元素刪除,返回值為該元素
23         System.out.println(string); // 2
24         System.out.println(list); // [one, two, three, four]
25     }
26 }

我們在將 Collection 的時候講過 add 和 remove 方法,在 List 中這兩個方法被重載了,可以根據需求插入和刪除想要刪除的下標的元素,那如果我們想要獲取兩個下標之間的元素和刪除兩個下標之間的元素該怎麼辦呢,如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class Main {
 5     public static void main(String[] args) {
 6         List<Integer> list = new ArrayList<Integer>();
 7         for (int i = 0; i < 10; i++) {
 8             list.add(i);
 9         }
10         System.out.println(list); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
11         List<Integer> subList = list.subList(2, 5); // 獲取下標從 2 到 5 的元素,含 2 不含 5
12         System.out.println(subList); // [2, 3, 4]
13         // 將 subList 中每個元素擴大 10 倍
14         for (int i = 0; i < subList.size(); i++) {
15             subList.set(i, subList.get(i) * 10);
16         }
17         System.out.println(subList); // [20, 30, 40]
18         /**
19          * 對子集的修改,就是修改原集合相應內容
20          * */
21         System.out.println(list); // [0, 1, 20, 30, 40, 5, 6, 7, 8, 9]
22         /**
23          * 刪除集合中 2-5 的元素
24          * */
25         list.subList(2, 5).clear();
26         System.out.println(list); // [0, 1, 5, 6, 7, 8, 9]
27     }
28 }

我們說集合和數組有很多相似的地方,那課可以進行相互轉換呢,當然是可以的,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 
 4 public class Main {
 5     public static void main(String[] args) {
 6         Collection<String> collection = new ArrayList<String>();
 7         collection.add("one");
 8         collection.add("two");
 9         collection.add("three");
10         collection.add("four");
11         System.out.println(collection); // [one, two, three, four]
12         /**
13          * 集合提供了一個 toArray,可以將當前集合轉換為數組
14          * */
15         // Object[] array = collection.toArray(); // 不常用
16         /**
17          * collection.size() 表示要轉換的數組的 length
18          * 如果大於給定的 collection 的 size,則自動填充完整 array
19          * 如果小於給定的 collection 的 size,則自動創建給你一樣長度的 size
20          * */
21         String[] array = collection.toArray(new String[collection.size()]);
22         System.out.println(array.length); // 4
23         for (String string : array) {
24             System.out.println(string); // one two three four
25         }
26 
27         String[] array1 = collection.toArray(new String[6]);
28         System.out.println(array.length); // 4
29         for (String string : array1) {
30             System.out.println(string); // one two three four null null
31         }
32 
33         String[] array2 = collection.toArray(new String[1]);
34         System.out.println(array.length); // 4
35         for (String string : array2) {
36             System.out.println(string); // one two three four
37         }
38     }
39 }

在上面的代碼中我們實現了集合轉換為數組的方法,接下來我們再看一下數組轉換為集合的方法:

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 /**
 6  * 數組轉換為集合
 7  * 需要注意,轉換隻能轉換為 List 集合
 8  * 使用的是數組的工具類 Arrays 的靜態方法 asList
 9  * 只能轉換為 List 集合的主要原因是:Set 不能存放重複元素
10  * 所以若轉換為 Set 集合可能會出現丟失元素的情況
11  */
12 public class Main {
13     public static void main(String[] args) {
14         String[] array = {"one", "two", "three", "four"};
15         List<String> list = Arrays.asList(array);
16         System.out.println(list); // [one, two, three, four]
17 
18         /**
19          * 向集合中添加元素,會出現編譯錯誤
20          * 相當於在原數組添加元素
21          * 該集合時由數組轉換過來的,那麼該集合就表示原來的數組
22          * 所以對集合的操作就是對數組的操作
23          * 那麼添加元素會導致原數組擴容
24          * 那麼久不能表示原來的數組了
25          * 所以不允許向該集合添加元素
26          */
27         // list.add("five"); // 編譯錯誤 Exception in thread "main" java.lang.UnsupportedOperationException
28 
29         /**
30          * 若希望增刪元素,需要另外創建一個集合
31          * */
32         /**
33          * 所有的集合都提供了一個帶有 Collection 類型參數的構造方法
34          * 該構造方法稱為:複製構造器
35          * 作用是在創建當前集合的同時,
36          * 集合中包含給定集合中的所有元素
37          * */
38         // List<String> list1 = new ArrayList<String>(list); // 複製構造器,一步到位
39         List<String> list1 = new ArrayList<String>();
40         list1.addAll(list);
41         list1.add("five");
42         System.out.println(list1); // [one, 2, three, four, five]
43 
44         /**
45          * 修改集合元素,數組元素也會改變
46          * */
47         list.set(1, "2");
48         System.out.println(list); // [one, 2, three, four]
49         for (String string : array) {
50             System.out.println(string); // one 2 three four
51         }
52     }
53 }

    

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

Spring Boot 2 實戰:利用Redis的Geo功能實現查找附近的位置

1. 前言

老闆突然要上線一個需求,獲取當前位置方圓一公里的業務代理點。明天上線!當接到這個需求的時候我差點吐血,這時間也太緊張了。趕緊去查相關的技術選型。經過一番折騰,終於在晚上十點完成了這個需求。現在把大致實現的思路總結一下。

2. MySQL 不合適

遇到需求,首先要想到現有的東西能不能滿足,成本如何。

MySQL是我首先能夠想到的,畢竟大部分數據要持久化到MySQL。但是使用MySQL需要自行計算Geohash。需要使用大量數學幾何計算,並且需要學習地理相關知識,門檻較高,短時間內不可能完成需求,而且長期來看這也不是MySQL擅長的領域,所以沒有考慮它。

Geohash 參考 https://www.cnblogs.com/LBSer/p/3310455.html

2. Redis 中的GEO

Redis是我們最為熟悉的K-V數據庫,它常被拿來作為高性能的緩存數據庫來使用,大部分項目都會用到它。從3.2版本開始它開始提供了GEO能力,用來實現諸如附近位置、計算距離等這類依賴於地理位置信息的功能。GEO相關的命令如下:

Redis命令 描述
GEOHASH 返回一個或多個位置元素的 Geohash 表示
GEOPOS 從key里返回所有給定位置元素的位置(經度和緯度)
GEODIST 返回兩個給定位置之間的距離
GEORADIUS 以給定的經緯度為中心, 找出某一半徑內的元素
GEOADD 將指定的地理空間位置(緯度、經度、名稱)添加到指定的key中
GEORADIUSBYMEMBER 找出位於指定範圍內的元素,中心點是由給定的位置元素決定

Redis會假設地球為完美的球形, 所以可能有一些位置計算偏差,據說<=0.5%,對於有嚴格地理位置要求的需求來說要經過一些場景測試來檢驗是否能夠滿足需求。

2.1 寫入地理信息

那麼如何實現目標單位半徑內的所有元素呢?我們可以將所有的位置的經緯度通過上表中的GEOADD將這些地理信息轉換為52位的Geohash寫入Redis

該命令格式:

geoadd key longitude latitude member [longitude latitude member ...]

對應例子:

redis> geoadd cities:locs 117.12 39.08 tianjin 114.29 38.02  shijiazhuang 
(integer) 2

意思是將經度為117.12緯度為39.08的地點tianjin和經度為114.29緯度為38.02的地點shijiazhuang加入keycities:locssorted set集合中。可以添加一到多個位置。然後我們就可以藉助於其他命令來進行地理位置的計算了。

有效的經度從-180度到180度。有效的緯度從-85.05112878度到85.05112878度。當坐標位置超出上述指定範圍時,該命令將會返回一個錯誤。

2.2 統計單位半徑內的地區

我們可以藉助於GEORADIUS來找出以給定經緯度,某一半徑內的所有元素。

該命令格式:

georadius key longtitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] 

這個命令比GEOADD要複雜一些:

  • radius 半徑長度,必選項。後面的mkmftmi、是長度單位選項,四選一。
  • WITHCOORD 將位置元素的經度和維度也一併返回,非必選。
  • WITHDIST 在返回位置元素的同時, 將位置元素與中心點的距離也一併返回。 距離的單位和查詢單位一致,非必選。
  • WITHHASH 返回位置的52位精度的Geohash值,非必選。這個我反正很少用,可能其它一些偏向底層的LBS應用服務需要這個。
  • COUNT 返回符合條件的位置元素的數量,非必選。比如返回前10個,以避免出現符合的結果太多而出現性能問題。
  • ASC|DESC 排序方式,非必選。默認情況下返回未排序,但是大多數我們需要進行排序。參照中心位置,從近到遠使用ASC ,從遠到近使用DESC

例如,我們在 cities:locs 中查找以(115.03,38.44)為中心,方圓200km的城市,結果包含城市名稱、對應的坐標和距離中心點的距離(km),並按照從近到遠排列。命令如下:

redis> georadius cities:locs 115.03 38.44 200 km WITHCOORD WITHDIST ASC
1) 1) "shijiazhuang"
   2) "79.7653"
   3) 1) "114.29000169038772583"
      2) "38.01999994251037407"
2) 1) "tianjin"
   2) "186.6937"
   3) 1) "117.02000230550765991"
      2) "39.0800000535766543"

你可以加上 COUNT 1來查找最近的一個位置。

3. 基於Redis GEO實戰

大致的原理思路說完了,接下來就是實操了。結合Spring Boot應用我們應該如何做?

3.1 開發環境

需要具有GEO特性的Redis版本,這裏我使用的是Redis 4 。另外我們客戶端使用 spring-boot-starter-data-redis 。這裏我們會使用到 RedisTemplate對象。

3.2 批量添加位置信息

第一步,我們需要將位置數據初始化到Redis中。在Spring Data Redis中一個位置坐標(lng,lat) 可以封裝到org.springframework.data.geo.Point對象中。然後指定一個名稱,就組成了一個位置Geo信息。RedisTemplate提供了批量添加位置信息的方法。我們可以將章節2.1中的添加命令轉換為下面的代碼:

   Map<String, Point> points = new HashMap<>();
   points.put("tianjin", new Point(117.12, 39.08));
   points.put("shijiazhuang", new Point(114.29, 38.02));
   // RedisTemplate 批量添加 Geo
   redisTemplate.boundGeoOps("cities:locs").add(points);

可以結合Spring Boot 提供的ApplicationRunner接口來實現初始化。

@Bean
public ApplicationRunner cacheActiveAppRunner(RedisTemplate<String, String> redisTemplate) {

    return args -> {
        final String GEO_KEY = "cities:locs";

        // 清理緩存
        redisTemplate.delete(GEO_KEY);
        
        Map<String, Point> points = new HashMap<>();
        points.put("tianjin", new Point(117.12, 39.08));
        points.put("shijiazhuang", new Point(114.29, 38.02));
        // RedisTemplate 批量添加 GeoLocation
        BoundGeoOperations<String, String> geoOps = redisTemplate.boundGeoOps(GEO_KEY);
        geoOps.add(points);
    };
}

地理數據持久化到MySQL,然後同步到Redis中。

3.3 查詢附近的特定位置

RedisTemplate 針對GEORADIUS命令也有封裝:

GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args)

Circle對象是封裝覆蓋的面積(圖1),需要的要素為中心點坐標Point對象、半徑(radius)、計量單位(metric), 例如:

Point point = new Point(115.03, 38.44);

Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
Distance distance = new Distance(200, metric);

Circle circle = new Circle(point, distance);

GeoRadiusCommandArgs用來封裝GEORADIUS的一些可選命令參數,參見章節2.2中的WITHCOORDCOUNTASC等,例如我們需要在返回結果中包含坐標、中心距離、由近到遠排序的前5條數據:

RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands
        .GeoRadiusCommandArgs
        .newGeoRadiusArgs()
        .includeDistance()
        .includeCoordinates()
        .sortAscending()
        .limit(limit);

然後執行 radius方法就會拿到GeoResults<RedisGeoCommands.GeoLocation<String>>封裝的結果,我們對這個可迭代對象進行解析就可以拿到我們想要的數據:

GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo()
        .radius(GEO_STAGE, circle, args);

if (radius != null) {
    List<StageDTO> stageDTOS = new ArrayList<>();
    radius.forEach(geoLocationGeoResult -> {
        RedisGeoCommands.GeoLocation<String> content = geoLocationGeoResult.getContent();
        //member 名稱  如  tianjin 
        String name = content.getName();
        // 對應的經緯度坐標
        Point pos = content.getPoint();
        // 距離中心點的距離
        Distance dis = geoLocationGeoResult.getDistance();
    });
}

3.4 刪除元素

有時候我們可能需要刪除某個位置元素,但是RedisGeo並沒有刪除成員的命令。不過由於它的底層是zset,我們可以藉助zrem命令進行刪除,對應的Java代碼為:

redisTemplate.boundZSetOps(GEO_STAGE).remove("tianjin");

4. 總結

今天我們使用RedisGeo特性實現了常見的附近的地理信息查詢需求,簡單易上手。其實使用另一個Nosql數據庫MongoDB也可以實現。在數據量比較小的情況下Redis已經能很好的滿足需要。如果數據量大可使用MongoDB來實現。 文中涉及的DEMO可通過我個人博客獲取。

關注公眾號:Felordcn 獲取更多資訊

個人博客:https://felord.cn

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

聚甘新

跨雲廠商部署 k3s 集群

原文鏈接:https://fuckcloudnative.io/posts/deploy-k3s-cross-public-cloud/

最近一兩年各大雲服務商都出了各種福利活動,很多小夥伴薅了一波又一波羊毛,比如騰訊雲 1C2G 95/年 真香系列,華為雲和阿里雲也都有類似的活動,薅個兩三台就能搭建一個 Kubernetes 集群。但是跨雲服務商搭建 Kubernetes 集群並不像我們想象中的那麼容易,首先就是原生的 Kubernetes 組件本身對資源的消耗量很大,而雲服務器的資源非常有限,經不起這麼大傢伙的折騰,對此我們可以選擇使用輕量級 Kubernetes 發行版:k3s

k3s 將安裝 Kubernetes 所需的一切打包進僅有 60MB 大小的二進制文件中,並且完全實現了 Kubernetes API。為了減少運行 Kubernetes 所需的內存,k3s 刪除了很多不必要的驅動程序,並用附加組件對其進行替換。由於它只需要極低的資源就可以運行,因此它能夠在任何 512MB 內存以上的設備上運行集群。

其實 k3s 的安裝非常簡單,分分鐘就能搞定,但對於公有雲來說,還是有很多坑的,比如內網不通、公網 IP 不在服務器上該咋辦?本文就為你一一解決這些難題,讓天下的雲羊毛都成為 k3s 的後宮!

1. 下載二進制文件

首先來解決第一個難題:k3s 二進制文件的下載。國內下載 GitHub 速度基本都是以幾個 kb 為單位,不忍直視,如果下載內容都是代碼,有很多辦法可以解決,比如通過碼雲中轉啊、直接通過 CDN 下載啊,什麼?你不知道可以通過 CDN 下載?好吧沒關係,現在我告訴你了:https://cdn.con.sh/。

但是上面的 CDN 並不能下載 release 里的內容,要想下載 release 里的內容,可以使用這個網站:https://toolwa.com/github/。打開網站,輸入 release 裏面的文件下載鏈接,點擊起飛即可加速下載。

當然,如果你會魔法上網的話,上面的所有花里胡哨的方法都可以無視,直接下載就好啦(本文選擇使用版本 v1.17.6+k3s1):

$ wget https://github.com/rancher/k3s/releases/download/v1.17.6+k3s1/k3s -O /usr/local/bin/k3s
$ chmod +x /usr/local/bin/k3s

需要在所有節點中下載上述二進制文件。

2. 升級內核

k3s 的默認網絡插件是 flannel,默認模式是 vxlan 模式,建議使用 wireguard 模式,原因不解釋了,不知道 wireguard 是啥的自己去搜一下。

wireguard 對內核的要求比較高,而 CentOS 7.x 的默認內核是不滿足要求的,需要升級內核(如果你的操作系統是 CentOS 7.x 的話)。步驟如下:

① 載入公鑰

$ rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

② 升級安裝 elrepo

$ rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

③ 載入 elrepo-kernel 元數據

$ yum --disablerepo=\* --enablerepo=elrepo-kernel repolist

④ 安裝最新版本的內核

$ yum --disablerepo=\* --enablerepo=elrepo-kernel install  kernel-ml.x86_64  -y

⑤ 刪除舊版本工具包

$ yum remove kernel-tools-libs.x86_64 kernel-tools.x86_64  -y

⑥ 安裝新版本工具包

$ yum --disablerepo=\* --enablerepo=elrepo-kernel install kernel-ml-tools kernel-ml-devel kernel-ml-headers -y

⑦ 查看內核插入順序

$ grep "^menuentry" /boot/grub2/grub.cfg | cut -d "'" -f2

CentOS Linux (3.10.0-1127.10.1.el7.x86_64) 7 (Core)
CentOS Linux (5.7.2-1.el7.elrepo.x86_64) 7 (Core)
CentOS Linux (0-rescue-96820b9851c24560b5f942f2496b9aeb) 7 (Core)

默認新內核是從頭插入,默認啟動順序也是從 0 開始。

⑧ 查看當前實際啟動順序

$ grub2-editenv list

saved_entry=CentOS Linux (3.10.0-1127.10.1.el7.x86_64) 7 (Core)

⑨ 設置默認啟動

$ grub2-set-default 'CentOS Linux (5.7.2-1.el7.elrepo.x86_64) 7 (Core)'

最後重啟檢查:

$ reboot
$ uname -r

注意:集群中的所有節點都需要升級內核。

3. 安裝 wireguard

內核升級了之後,就可以安裝 wireguard 了,也很簡單,步驟如下:

$ yum install epel-release https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
$ yum install yum-plugin-elrepo
$ yum install kmod-wireguard wireguard-tools

注意:集群中的所有節點都需要安裝。

4. 部署控制平面

下面就可以在控制節點上啟動控制平面的組件了,這裏我們選擇手動部署,這樣比較方便修改參數。先創建一個 Service Unit 文件:

$ cat > /etc/systemd/system/k3s.service <<EOF
[Unit]
Description=Lightweight Kubernetes
Documentation=https://k3s.io
Wants=network-online.target

[Install]
WantedBy=multi-user.target

[Service]
Type=notify
EnvironmentFile=/etc/systemd/system/k3s.service.env
KillMode=process
Delegate=yes
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/k3s \
    server \
    --tls-san <public_ip> \
    --node-ip <public_ip> \
    --node-external-ip <public_ip> \
    --no-deploy servicelb \
    --flannel-backend wireguard \
    --kube-proxy-arg "proxy-mode=ipvs" "masquerade-all=true" \
    --kube-proxy-arg "metrics-bind-address=0.0.0.0"
EOF
  • <public_ip> 替換成控制節點的公網 IP。
  • flannel 使用 wireguard 協議來跨主機通信。
  • kube-proxy 使用 ipvs 模式。

啟動 k3s 控制平面並設置開機自啟:

$ systemctl enable k3s --now

查看集群組件健康狀況:

$ kubectl get cs

NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok

這裏的輸出沒有 etcd,因為 k3s 的默認數據存儲是 Sqlite,對於小型數據庫十分友好。Kubernetes 控制平面中發生的更改更多是與頻繁更新部署、調度 Pod 等有關,因此對於幾個節點的小型集群而言,數據庫不會造成太大負載,能省下不少資源,真香!

5. 加入計算節點

部署好控制平面之後,就可以加入計算節點了。首先在計算節點上創建 Service Unit 文件:

$ cat > /etc/systemd/system/k3s-agent.service <<EOF
[Unit]
Description=Lightweight Kubernetes
Documentation=https://k3s.io
Wants=network-online.target

[Install]
WantedBy=multi-user.target

[Service]
Type=exec
EnvironmentFile=/etc/systemd/system/k3s-agent.service.env
KillMode=process
Delegate=yes
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/k3s agent \
    --node-external-ip <public_ip> \
    --node-ip <public_ip> \
    --kube-proxy-arg "proxy-mode=ipvs" "masquerade-all=true" \
    --kube-proxy-arg "metrics-bind-address=0.0.0.0"
EOF

環境變量文件 /etc/systemd/system/k3s-agent.service.env 中需要加入兩個環境變量:

  • K3S_URL : API Server 的 URL,一般格式為:https://<master_ip>:6443。其中 <master_ip> 是控制節點的公網 IP。
  • K3S_TOKEN : 加入集群所需的 token,可以在控制節點上查看 /var/lib/rancher/k3s/server/node-token 文件。

/etc/systemd/system/k3s-agent.service.env 內容如下:

K3S_URL=https://<master_ip>:6443
K3S_TOKEN=xxxxxxxx

啟動 k3s-agent 並設置開啟自啟:

$ systemctl enable k3s-agent --now

查看節點狀態:

$ kubectl get node

NAME         STATUS   ROLES    AGE     VERSION
blog-k3s01   Ready    master   3d6h    v1.17.6+k3s1
blog-k3s02   Ready    <none>   3d3h    v1.17.6+k3s1

6. 內網不互通的解決辦法

這裡會遇到一個問題,不同節點的 flannel 使用的是內網 IP 來進行通信,而我們的雲服務器是內網不互通的,而且公網 IP 也不在服務器上。可以看一下 node 的 annotations

$ kubectl get node blog-k3s02 -o yaml

apiVersion: v1
kind: Node
metadata:
  annotations:
    flannel.alpha.coreos.com/backend-data: '"xxxxx"'
    flannel.alpha.coreos.com/backend-type: extension
    flannel.alpha.coreos.com/kube-subnet-manager: "true"
    flannel.alpha.coreos.com/public-ip: 192.168.0.11
    ...

可以看到 flannel 給節點打的註解中的節點 IP 是內網 IP。要想讓 flannel 使用公網 IP 進行通信,需要額外添加一個註解 public-ip-overwrite,然後 flannel 會基於這個 IP 配置網絡。按照官方文檔的說法,如果你的 node 設置了 ExternalIP,flannel 會自動給 node 添加一個註解 public-ip-overwrite,但我不知道該如何給 node 設置 ExternalIP,乾脆就直接手動加註解吧:

$ kubectl annotate nodes <master> flannel.alpha.coreos.com/public-ip-overwrite=<master_pub_ip>
$ kubectl annotate nodes <node> flannel.alpha.coreos.com/public-ip-overwrite=<node_pub_ip>

加了註解之後,flannel 的 public-ip 就會被修改為公網 IP。然後在各個節點上重啟各自的 k3s 服務,查看 wireguard 連接狀況:

$ wg show flannel.1

interface: flannel.1
  public key: ONDgJCwxxxxxxxJvdWpoOKTxQA=
  private key: (hidden)
  listening port: 51820
  
peer: MKKaanTxxxxxxxV8VpcHq4CSRISshw=
  endpoint: <pub_ip>:51820
  allowed ips: 10.42.4.0/24
  latest handshake: 26 seconds ago
  transfer: 133.17 KiB received, 387.44 KiB sent
  persistent keepalive: every 25 seconds

可以看到通信端點被改成了公網 IP,大功告成!

7. metrics-server 問題解決

還有一個問題就是 metrics-server 無法獲取 cpu、內存等利用率核心指標。需要修改 metrics-server 的 manifests,使用以下命令在線編輯 metrics-server 的 manifests:

$ kubectl -n kube-system edit deploy metrics-server

然後加入以下執行參數后保存退出:

      -command:
        - /metrics-server
        - --kubelet-preferred-address-types=ExternalIP
        - --kubelet-insecure-tls

這樣就可以讓 metrics-server 使用公網 IP 來和 node 通信了。修改成功后就可以看到核心指標了:

$ kubectl top nodes
NAME         CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
blog-k3s01   193m         9%     886Mi           22%
blog-k3s02   41m          2%     1292Mi          32%

$ kubectl top pod -n kube-system
NAME                                      CPU(cores)   MEMORY(bytes)
coredns-848b6cc76f-zq576                  8m           14Mi
local-path-provisioner-58fb86bdfd-bzdfl   2m           9Mi
metrics-server-bdfc79c97-djmzk            1m           12Mi

到這裏跨雲服務商部署 k3s 基本上就大功告成了,下一篇文章將會教你如何打通家裡到雲上 k3s 的網絡,讓你家中所有設備都可以直接訪問 Pod IP、svc IP,甚至可以直接訪問 svc 域名,敬請期待。

Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12離線安裝包發布地址http://store.lameleg.com ,歡迎體驗。 使用了最新的sealos v3.3.6版本。 作了主機名解析配置優化,lvscare 掛載/lib/module解決開機啟動ipvs加載問題, 修復lvscare社區netlink與3.10內核不兼容問題,sealos生成百年證書等特性。更多特性 https://github.com/fanux/sealos 。歡迎掃描下方的二維碼加入釘釘群 ,釘釘群已經集成sealos的機器人實時可以看到sealos的動態。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

Flutter學習筆記(35)–通知Notification,Flutter學習筆記(35)–通知Notification

如需轉載,請註明出處:Flutter學習筆記(35)–通知Notification

通知的NotificationListener和我們之前寫的事件的Listener一樣,都是功能性的組件,而且也都是從子節點順着widget樹向上冒泡,不同的是,事件的Listener不可以被終止,但是通知的NotificationListener是可以被終止的。

是否終止根據NotificationListener的返回值來決定。

說一下我個人的理解:

通知Notification的發送是通過disPatch進行分發的,就好像Android裏面的事件分發,當NotificationListener監聽到了通知事件,這時候會走到其onNotification回調中,根據回調中的返回值類型(true還是false)來決定是否還繼續向父親節點發送通知。

返回true就是繼續分發,返回false就是終止分發,返回false就意味着上層節點的NotificationListener就不會接收到通知事件了。

舉個例子就是:

兩層NotificationListener嵌套,子節點的NotificationListener返回true,那麼父親節點的NotificationListener可以接收到通知事件,反之如果返回false,那麼父親節點的NotificationListener就不會接收到通知事件了。

下面看一下demo示例:

demo就是簡單的發送通知,監聽到通知事件后改變text的內容。

1.創建一個事件通知類,要繼承Notification,它其實就是一個數據載體,在裏面定義通知數據的類型和內容。

import 'package:flutter/material.dart';

class MyNotification extends Notification{
  String notificationStr;

  MyNotification(this.notificationStr);
}

2.NotificationListener的使用和通知事件的分發

import 'package:flutter/material.dart';
import 'package:study_app/util/MyNotification.dart';

class NotificationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _NotificationDemoState();
  }
}

class _NotificationDemoState extends State {
  String _notificationData = 'default_data';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'NotificationDemo',
      home: new Scaffold(
          appBar: AppBar(
            title: Text('NotificationDemo'),
          ),
          body: NotificationListener<MyNotification>(
            onNotification: (notification) {
              setState(() {
                _notificationData = notification.notificationStr;
              });
              return true;
            },
            child: Column(
              children: <Widget>[
                Text(_notificationData),
                Builder(
                  builder: (context) {
                    return Container(
                      width: double.infinity,
                      child: RaisedButton(
                          child: Text('發送通知'),
                          onPressed: () {
                            MyNotification('notification_data')
                                .dispatch(context);
                          }),
                    );
                  },
                )
              ],
            ),
          )),
    );
  }
}

在看書的時候,作者強調了一種錯誤的寫法,如下圖註釋的部分:

原因是通知在分發的時候,需要一個context參數,這個參數指的是Notification監聽的子widget的context,如果按照註釋部分的寫法的話,context是根widget的,這樣會導致監聽不到子widget了。

所以需要我們通過Builder構建出我們子widget的context,這裏需要特別注意一下。

最後看一下效果截圖:

   

以上!有任何疑問歡迎留言!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

2020年最佳Java調試工具(翻譯)

調試是應用程序開發周期不可或缺的一部分。用Java或任何其他語言編寫程序時,每個開發人員應解決的首要問題之一是可靠的調試工具的可用性。

所使用的工具類型可能影響或破壞應用程序的調試過程,因此至關重要的是,要了解根據用例而定最佳選擇。

在這篇文章中,我們概述了2020年最好的7種Java調試工具。在開發,生產環境中查找,診斷和修復問題時,這些工具中的大多數將派上用場。

NetBeans

NetBeans是運行在Linux,Windows,MacOS和Solaris上的頂級,使用最廣泛的Java IDE之一。正如預期的那樣,它具有可視化調試器和代碼概要分析器,使開發人員可以調試可執行的Java類,單元測試和整個項目。

NetBeans調試器允許您在Java代碼中放置斷點,運行方法,添加字段監視,監視執行以及在調試會話期間拍攝快照。

Rookout

Rookout是一個很棒的Java調試選項,可以在開發和生產中很好地工作。它在包括無服務器和容器的各種環境中提供了強大的調試功能。

通過收集和流水線化關鍵數據,淘汰工作超越了標準調試功能。這使開發人員無需編寫代碼,重新部署或重新啟動應用程序即可了解軟件執行問題並解決錯誤。

藉助Rookout,開發人員可以消除冗長,複雜且資源密集的數據探索和錯誤查找過程。

Eclipse

Eclipse是帶有內置Java調試器的著名開源IDE。自成立以來,Eclipse一直保持其作為開發現代應用程序最強大的跨平台IDE之一的聲譽。

它提供了標準的調試功能,例如設置斷點,執行步驟執行,檢查變量和值,掛起和恢複線程等功能。

Eclipse平台還方便了遠程調試。儘管它主要是Java IDE,但Eclipse調試視圖還支持PHP,C,C ++和許多其他編程語言。

IntelliJ IDEA

IntelliJ IDEA是具有功能強大的調試器的高度流行的Java IDE。該工具使開發人員可以輕鬆調試簡單代碼以及多線程Java應用程序。

使用IntelliJ調試器,您可以設置斷點,單步執行代碼,評估表達式,檢查變量以及執行一系列其他調試過程。它可以更輕鬆地檢測意外的流量和狀態,死鎖,活動鎖等。

IntelliJ IDEA的核心旨在改善Java開發團隊的工作流程和生產力。

Java調試器(JDB)

Java調試器(JDB)是允許開發人員在命令行中調試Java代碼的工具。通過Java調試接口(JDI)(高級前端接口),開發人員可以檢測並修復程序中的錯誤。該工具還可用於檢查和調試遠程Java虛擬機中的代碼。

像大多數命令行調試器一樣,JDB具有學習曲線,因此新用戶需要花費一些時間來適應JDB。但是,一旦掌握了JDB命令,就可以輕鬆設置斷點,單步執行代碼並執行其他調試操作。

Fusion Reactor(聚變反應堆??)

Fusion Reactor是針對開發,測試和生產環境中的Java應用程序的創新性能監視解決方案。該工具配備了一組令人印象深刻的功能,這些功能可提供Java開發人員在APM工具中所需的一切。

Fusion Reactor開發版,您可以開發,測試,並在非生產環境分析應用。使用此工具,在將應用程序部署到生產環境之前,更容易發現問題並提高代碼質量。

另一個值得注意的功能是生產調試器,它使開發人員在與代碼交互並修復錯誤時獲得最大的控制權。Fusion Reactor還支持遠程調試。

JDeveloper

Oracle的JDeveloper是一種免費的IDE,可解決應用程序開發生命周期中從編碼到設計,性能分析,調試,優化和部署的每個步驟。

使用JDeveloper進行調試時,可以設置斷點和觀察點,分析調用堆棧,檢查和操作變量,並逐步研究代碼執行情況。除了Java,它還可以用於調試HTML,PHP,JavaScript,SQL和XML。

現在,您可以繼續使用上述工具,以更高的速度和效率來檢測,診斷和解決Java應用程序中的問題。

翻譯原文

Top Java Debugging Tools for 2020

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

聚甘新

Oracle SQL調優系列之SQL Monitor Report

@

目錄

  • 1、SQL Monitor簡介
  • 2、捕捉sql的前提
  • 3、SQL Monitor 參數設置
  • 4、SQL Monitor Report
    • 4.1、SQL_ID獲取
    • 4.2、Text文本格式
    • 4.3、Html格式
    • 4.4、ACTIVE格式
    • 4.5 SQL Monitoring list
  • 5、SQL Monitor Report查詢
    • 5.1、查看所有的sql monitor report
    • 5.2、查看某個sql的sql monitor report
    • 5.3、查看某個sql的整體性能
    • 5.4、查看整個系統的性能

1、SQL Monitor簡介

sql monitor是oracle官方提供的自動監控符合特定條件的SQL,用於收集執行時的細節信息的監控工具,常用於sql調優和系統性能監控

2、捕捉sql的前提

sql monitor 捕捉sql的前提:

  • 并行執行的sql語句
  • 單次執行消耗的CPU或IO超過5秒
  • statistics_level級別必須是TYPICAL 或者ALL
  • 使用/* +MONITOR*/ HINT的SQL語句

3、SQL Monitor 參數設置

  • STATISTICS_LEVEL必須設置為:’TYPICAL’(缺省)或者 ‘ALL’
  • CONTROL_MANAGEMENT_PACK_ACCESS設置為:’DIAGNOSTIC+TUNING’

查看statistics_level參數

show parameter statistics_level;

建議還是改變Session就可以

alter session set statistics_level=ALL;

查看參數CONTROL_MANAGEMENT_PACK_ACCESS

show parameter CONTROL_MANAGEMENT_PACK_ACCESS;

4、SQL Monitor Report

本博客採用DBMS_SQLTUNE包DBMS_SQLTUNE.report_sql_monitor的方式獲取,報告格式有:’TEXT’,’HTML’,’XML’ ,’ACTIVE’,其中’ACTIVE’只在11g R2以後才支持

4.1、SQL_ID獲取

sql monitor使用,必須在sql中使用/* +MONITOR*/ Hint,然後數據會存在v$sql_monitor表裡

隨意找條sql,注意要加/*+ moniotr*/


select /*+ moniotr*/ a.user_code, a.full_name, a.user_pwd, c.unit_code, c.unit_name
  from base_user a
  left join (select ur.user_code, ur.unit_code
               from t_user_role ur
              where ur.user_role < 10) b
    on a.user_code = b.user_code
  left join t_unit_info c
    on b.unit_code = c.unit_code
 where c.unit_code in
       (select uinfo.unit_code
          from t_unit_info uinfo
         start with uinfo.unit_code = '15803'
        connect by prior uinfo.unit_code = uinfo.para_unit_code);
     

提供sql查詢,獲取sql_id

select sql_id,sql_text from v$sql_monitor where sql_text like '%t_unit_info%

4.2、Text文本格式

將上面查詢到的sql_id改下,然後執行如下SQL:

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_text.txt
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => 'g9rtj389t0g66',
  TYPE => 'TEXT',
  REPORT_LEVEL => 'ALL') AS REPORT
FROM dual;
spool off

獲取到text格式的sql monitor

4.3、Html格式

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_html.html
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => 'g9rtj389t0g66',
  TYPE => 'HTML',
  REPORT_LEVEL => 'ALL') AS REPORT
FROM dual;
spool off

獲取到對應報告,可以看到執行計劃、Buffer Gets 等等信息

4.4、ACTIVE格式

ACTIVE格式需要下載相應的flash組件、腳本,詳細見SQL Monitor Report 使用詳解

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_active.html
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => '2rjh5d5k2yujz',
  TYPE => 'ACTIVE',
  REPORT_LEVEL => 'ALL',
  BASE_PATH => 'http://ip/script') AS REPORT
FROM dual;
spool off

4.5 SQL Monitoring list

如果要獲取所有sql monitor,就可以使用如下SQL:

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
SPOOL report_sql_monitor_list.html
SELECT dbms_sqltune.report_sql_monitor_list(
  type         => 'HTML',
  report_level => 'ALL') AS report
FROM dual;
SPOOL OFF

5、SQL Monitor Report查詢

提供sql monitor常用的查詢腳本

5.1、查看所有的sql monitor report

  • 查看所有的sql monitor report
   select dbms_sqltune.report_sql_monitor from dual;

5.2、查看某個sql的sql monitor report

  • 查看某個sql的sql monitor report
  SELECT DBMS_SQLTUNE.report_sql_monitor(sql_id => '2rjh5d5k2yujz', type => 'TEXT') from dual;

5.3、查看某個sql的整體性能

  • 查看某個sql的整體性能
   SELECT DBMS_SQLTUNE.report_sql_monitor_list(sql_id=>'2rjh5d5k2yujz',type =>'TEXT',report_level => 'ALL') AS report FROM dual;

5.4、查看整個系統的性能

  • 查看整個系統的性能
   SELECT DBMS_SQLTUNE.report_sql_monitor_list(type =>'TEXT',report_level => 'ALL') AS report FROM dual;

相關SQL腳本下載:sql download

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新