標籤: 網頁設計公司

  • “電動汽車就是大家電” 中國首次將電動汽車引入家電連鎖賣場

    中國首家電動汽車跨平臺運營商聯合電動21日在北京與蘇甯易購達成全面戰略合作,首次將電動汽車引入家電連鎖賣場,電動汽車作為新的電器商品門類與消費者見面。這是中國電動車在汽車流通領域的一次大膽嘗試,並將引發銷售管道顛覆性的變革。

    自此,消費者在逛電器商城時可以零距離地瞭解、購買電動汽車。此舉不僅為消費者帶來極大的便利,而且也論證了“電動汽車就是大家電”的全新理念。業內稱,這將為中國電動汽車行業的發展帶來革命性的思考和進步。

    中國汽車流通協會秘書長肖政三表示,將電動汽車引進家電連鎖賣場,迎合了汽車銷售多元化發展問題,汽車超市的概念是一種模式的創新,給消費者帶來的便利是可以預見的。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

    ※幫你省時又省力,新北清潔一流服務好口碑

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

  • SpringBoot 源碼解析 (一)—– SpringBoot核心原理入門

    SpringBoot 源碼解析 (一)—– SpringBoot核心原理入門

    Spring Boot 概述

    Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring.

    上面是引自官網的一段話,大概是說: Spring Boot 是所有基於 Spring 開發的項目的起點。Spring Boot 的設計是為了讓你盡可能快的跑起來 Spring 應用程序並且盡可能減少你的配置文件。

    什麼是 Spring Boot

    • 它使用 “習慣優於配置” (項目中存在大量的配置,此外還內置一個習慣性的配置,讓你無須手動配置)的理念讓你的項目快速運行起來。
    • 它並不是什麼新的框架,而是默認配置了很多框架的使用方式,就像 Maven 整合了所有的 jar 包一樣,Spring Boot 整合了所有框架

    使用 Spring Boot 有什麼好處

    回顧我們之前的 SSM 項目,搭建過程還是比較繁瑣的,需要:

    • 1)配置 web.xml,加載 spring 和 spring mvc
    • 2)配置數據庫連接、配置日誌文件
    • 3)配置家在配置文件的讀取,開啟註解
    • 4)配置mapper文件
    • …..

    而使用 Spring Boot 來開發項目則只需要非常少的幾個配置就可以搭建起來一個 Web 項目,並且利用 IDEA 可以自動生成生成

    • 划重點:簡單、快速、方便地搭建項目;對主流開發框架的無配置集成;極大提高了開發、部署效率。

    Spring Boot HelloWorld

    導入依賴spring boot相關的依賴

    <?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>cn.chenhao</groupId>
        <artifactId>springboot</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>springboot</name>
        <description>Demo project for Spring Boot</description>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.1.RELEASE</version>
            <relativePath/>
        </parent>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>

    編寫主程序

    /**
     * @SpringBootApplication來標註一個主程序類,說明這是一個SpringBoot應用
     */ @SpringBootApplication public class HelloWorldMainApplication {
    
        public static void main(String[] args) {
            //Spring應用啟動
            SpringApplication.run(HelloWorldMainApplication.class, args);
        }
    }

    編寫Controller、Service

    @RestController
    public class HelloController {
    
        @RequestMapping("/hello")
        public String hello(){
            return "Hello world";
        }
    }

    運行主程序測試

    使用maven打包命令將其打包成jar包后,直接使用命令:

    java -jar xxx.jar

    Hello World探究

    POM文件

    父項目

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/>
    </parent>

    其父項目是

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath>../../spring-boot-dependencies</relativePath>
    </parent>

    該父項目是真正管理Spring Boot應用裏面的所有依賴的版本:Spring Boot的版本仲裁中心,所以以後導入的依賴默認是不需要版本號。如下

    還有很多版本號沒有截圖出來

    啟動器

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

    spring-boot-starter : spring boot場景啟動器;幫助導入web模塊正常運行所依賴的組件;

    ​ Spring Boot將所有的功能場景抽取出來,做成一個個的starter(啟動器),只需要在項目中引入這些starter,那麼相關的場景的所有依賴都會導入進項目中。要用什麼功能就導入什麼場景的啟動器。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>

    添加了 spring-boot-starter-web 依賴,會自動添加 Tomcat 和 Spring MVC 的依賴

    spring-boot-starter-web中又引入了spring-boot-starter-tomcat

    主程序類(主入口類)

    @SpringBootApplication public class HelloWorldMainApplication {
    
        public static void main(String[] args) {
            //Spring應用啟動
            SpringApplication.run(HelloWorldMainApplication.class, args);
        }
    }

    @SpringBootApplication

    • Spring Boot應用標註在某個類上,說明這個類是SpringBoot的主配置類,SpringBoot就應該運行這個類的main方法來啟動SpringBoot應用。

    註解定義如下:

    @SpringBootConfiguration @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {}

    @SpringBootConfiguration

    • Spring Boot的配置類
    • 標註在某個類上,表示這是一個Spring Boot的配置類

    註解定義如下:

    @Configuration public @interface SpringBootConfiguration {}

    其實就是一個Configuration配置類,意思是HelloWorldMainApplication最終會被註冊到Spring容器中

    @EnableAutoConfiguration

    • 開啟自動配置功能
    • 以前使用Spring需要配置的信息,Spring Boot幫助自動配置;
    • @EnableAutoConfiguration通知SpringBoot開啟自動配置功能,這樣自動配置才能生效。

    註解定義如下:

    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}

    @AutoConfigurationPackage

    • 自動配置包註解
    @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {}

    @Import(AutoConfigurationPackages.Registrar.class):默認將主配置類(
    @SpringBootApplication)所在的包及其子包裏面的所有組件掃描到Spring容器中。如下

    @Order(Ordered.HIGHEST_PRECEDENCE)
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
              //默認將會掃描@SpringBootApplication標註的主配置類所在的包及其子包下所有組件
            register(registry, new PackageImport(metadata).getPackageName());
        }
    
        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.<Object>singleton(new PackageImport(metadata));
        }
    }

    @Import(EnableAutoConfigurationImportSelector.class)

    EnableAutoConfigurationImportSelector: 導入哪些組件的選擇器,將所有需要導入的組件以全類名的方式返回,這些組件就會被添加到容器中。

     1 //EnableAutoConfigurationImportSelector的父類:AutoConfigurationImportSelector
     2 @Override
     3 public String[] selectImports(AnnotationMetadata annotationMetadata) {
     4     if (!isEnabled(annotationMetadata)) {
     5         return NO_IMPORTS;
     6     }
     7     try {
     8         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
     9             .loadMetadata(this.beanClassLoader);
    10         AnnotationAttributes attributes = getAttributes(annotationMetadata);
    11         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 12         configurations = removeDuplicates(configurations);
    13         configurations = sort(configurations, autoConfigurationMetadata);
    14         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    15         checkExcludedClasses(configurations, exclusions);
    16         configurations.removeAll(exclusions);
    17         configurations = filter(configurations, autoConfigurationMetadata);
    18         fireAutoConfigurationImportEvents(configurations, exclusions);
    19         return configurations.toArray(new String[configurations.size()]);
    20     }
    21     catch (IOException ex) {
    22         throw new IllegalStateException(ex);
    23     }
    24 }

    我們主要看第11行List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);會給容器中注入眾多的自動配置類(xxxAutoConfiguration),就是給容器中導入這個場景需要的所有組件,並配置好這些組件。我們跟進去看看

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        //...
        return configurations;
    }
    
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
    
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            //從類路徑的META-INF/spring.factories中加載所有默認的自動配置類
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                //獲取EnableAutoConfiguration指定的所有值,也就是EnableAutoConfiguration.class的值
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

    SpringBoot啟動的時候從類路徑下的
    META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,並將這些值作為自動配置類導入到容器中,自動配置類就會生效,最後完成自動配置工作。EnableAutoConfiguration默認在spring-boot-autoconfigure這個包中,如下圖

    最終有96個自動配置類被加載並註冊進Spring容器中

    J2EE的整體整合解決方案和自動配置都在spring-boot-autoconfigure-xxx.jar中。在這些自動配置類中會通過@ConditionalOnClass等條件註解判斷是否導入了某些依賴包,從而通過@Bean註冊相應的對象進行自動配置。後面我們會有單獨文章講自動配置的內容

     

     

     

     

     

     

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

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • Elastic Stack 開源的大數據解決方案

    Elastic Stack 開源的大數據解決方案

    目的

    本文主要介紹的內容有以下三點:
    一. Elastic Stack是什麼以及組成部分
    二. Elastic Stack前景以及業務應用
    三. Elasticsearch原理(索引方向)
    四. Elasticsearch相對薄弱的地方

    一、Elastic Stack是什麼以及組成部分

    介紹Elastic Stack是什麼,其實只要一句話就可以,就是: 一套完整的大數據處理堆棧,從攝入、轉換到存儲分析、可視化

    它是不同產品的集合,各司其職,形成完整的數據處理鏈,因此Elastic Stack也可以簡稱為BLEK。

    Beats 輕量型數據採集器

    Logstash 輸入、過濾器和輸出

    Elasticsearch 查詢和分析

    Kibana 可視化,可自由選擇如何呈現數據

    1. Beats – 全品類採集器,搞定所有數據類型

    Filebeat(日誌文件):對成百上千、甚至上萬的服務器生成的日誌匯總,可搜索。

    Metricbeat(指標): 收集系統和服務指標,CPU 使用率、內存、文件系統、磁盤 IO 和網絡 IO 統計數據。

    Packetbeat(網絡數據):網絡數據包分析器,了解應用程序動態。

    Heartbeat(運行時間監控):通過主動探測來監測服務的可用性
    ……
    Beats支持許許多多的beat,這裏列的都是比較的常用的beat,了解更多可以點擊鏈接:

    2. Logstash – 服務器端數據處理管道

    介紹Logstash之前,我們先來看下Linux下常用的幾個命令

    cat alldata.txt | awk ‘{print $1}’ | sort | uniq | tee filterdata.txt

    只要接觸過Linux同學,應該都知道這幾個命名意思

    cat alldata.txt #將alldata.txt的內容輸出到標準設備上
    awk ‘{print $1}’ #對上面的內容做截取,只取每一行第一列數據
    sort | uniq  #對截取后的內容,進行排序和唯一性操作
    tee filterdata.txt #將上面的內容寫到filterdata.txt

    上面的幾個簡單的命令就可以看出來,這是對數據進行了常規的處理,用名詞修飾的話就是:數據獲取/輸入數據清洗數據過濾數據寫入/輸出

    而Logstash做的也是相同的事(看下圖)。

    將系統的日誌文件、應用日誌文件、系統指標等數據,輸入到Input,再通過數據清洗以及過濾,輸入到存儲設備中,這裏當然是輸入到Elasticsearch

    3. Elasticsearch – 分佈式文檔存儲、RESTful風格的搜索和數據分析引擎

    Elasticsearch主要也是最原始的功能就是搜索和分析功能。這裏就簡單說一下,下面講原理的時候會着重講到Elasticsearch

    搜索:全文搜索,完整的信息源轉化為計算機可以識別、處理的信息單元形成的數據集合 。

    分析:相關度,搜索所有內容,找到所需的具體信息(詞頻或熱度等對結果排序)

    4. Kibana- 可視化

    可視化看下圖(來源官網)便知

    可以對日誌分析、業務分析等做可視化

    現在從總體上來了解下,在心中對Elastic Stack有個清楚的認知(下圖)。

    二、Elastic Stack前景以及業務應用

    1. DB-Engines 排名

    Elasticsearch是Elastic Stack核心,由圖可以看出在搜索領域Elasticsearch暫時沒有對手。

    2. ES社區

    ES中文社區也是相當活躍的,會定期做一下分享,都是大公司的寶貴經驗,值得參考。

    3. 2018年攜程的使用情況(讓我們看看能處理多大的數據)

    集群數量是94個,最小的集群一般是3個節點。全部節點數量大概700+。

    最大的一個集群是做日誌分析的,其中數據節點330個,最高峰一天產生1600億文檔,寫入值300w/s。

    現在有2.5萬億文檔,大概是幾個PB的量

    三、Elasticsearch(ES)原理

    因為篇目有限,本篇只介紹ES的索引原理。

    ES為什麼可以做全文搜索,主要就是用了倒排索引,先來看下面的一張圖

    看圖可以簡單的理解倒排索引就是:關鍵字 + 頁碼

    對倒排索引有個基本的認識后,下面來做個簡單的數據例子。

    現在對Name做到排索引,記住:關鍵字 + ID(頁碼)。

    對Age做到排索引。

    對Intersets做到排索引。

    現在搜索Age等於18的,通過倒排索引就可以快速得到1和3的id,再通過id就可以得到具體數據,看,這樣是不是快的狠。

    如果是用Mysql等關係數據庫,現在有十多億數據(大數據嘛),就要一條一條的掃描下去找id,效率可想而知。而用倒排索引,找到所有的id就輕輕鬆鬆了。

    在ES中,關鍵詞叫Term,頁碼叫Posting List。

    但這樣就行了嗎? 如果Name有上億個Term,要找最後一個Term,效率豈不是還是很低?

    再來看Name的倒排索引,你會發現,將Armani放在了第一個,Tyloo放在了第三個,可以看出來,對Term做了簡單的排序。雖然簡單,但很實用。這樣查找Term就可以用二分查找法來查找了,將複雜度由n變成了logn。

    在ES中,這叫Term Dictionary。

    到這裏,再來想想MySQL的b+tree, 你有沒有發現原理是差不多的,那為什麼說ES搜索比MySQL快很多,究竟快在哪裡? 接下來再看。

    有一種數據結構叫Trie樹,又稱前綴樹或字典樹,是一種有序樹。這種數據結構的好處就是可以壓縮前綴和提高查詢數據。

    現在有這麼一組Term: apps, apple, apply, appear, back, backup, base, bear,用Trie樹表示如下圖。

    通過線路路徑字符連接就可以得到完成的Term,並且合用了前綴,比如apps, apple, apply, appear合用了app路徑,節省了大量空間。

    這個時候再來找base單詞,立即就可以排除了a字符開頭的單詞,比Term Dictionary快了不知多少。

    在ES中,這叫Term Index

    現在我們再從整體看下ES的索引

    先通過Trie樹快速定位block(相當於頁碼), 再到Term Dictionary 做二分查找,得到Posting List。

    索引優化

    ES是為了大數據而生的,這意味着ES要處理大量的數據,它的Term數據量也是不可想象的。比如一篇文章,要做全文索引,就會對全篇的內容做分詞,會產生大量的Term,而ES查詢的時候,這些Term肯定要放在內存裏面的。

    雖然Trie樹對前綴做了壓縮,但在大量Term面前還是不夠,會佔用大量的內存使用,於是就有ES對Trie樹進一步演化。

    FST(Finite State Transducer )確定無環有限狀態轉移器 (看下圖)

    可以看appear、bear 對共同的後綴做了壓縮。

    Posting List磁盤壓縮

    假設有一億的用戶數據,現在對性別做搜索,而性別無非兩種,可能”男”就有五千萬之多,按int4個字節存儲,就要消耗50M左右的磁盤空間,而這僅僅是其中一個Term。

    那麼面對成千上萬的Term,ES究竟是怎麼存儲的呢?接下來,就來看看ES的壓縮方法。

    Frame Of Reference (FOR) 增量編碼壓縮,將大數變小數,按字節存儲

    只要能把握“增量,大數變小數,按字節存儲”這幾個關鍵詞,這個算法就很好理解,現在來具體看看。

    現在有一組Posting List:[60, 150, 300,310, 315, 340], 按正常的int型存儲,size = 6 * 4(24)個字節。

    1. 按增量存儲:60 + 90(150)+ 150(300) + 10(310) + 5(315)+ 25(340),也就是[60, 90, 150, 10, 5, 25],這樣就將大數變成了小數。

    2. 切分成不同的block:[60, 90, 150]、[10, 5, 25],為什麼要切分,下面講。

    3. 按字節存儲:對於[60, 90, 150]這組block,究竟怎麼按字節存儲,其實很簡單,就是找其中最大的一個值,看X個比特能表示這個最大的數,那麼剩下的數也用X個比特表示(切分,可以盡可能的壓縮空間)。

    [60, 90, 150]最大數150 < 2^8 = 256,也就是這組每個數都用8個比特表示,也就是 3*8 = 24個比特,再除以8,也就是3個字節存在,再加上一個8的標識位(說明每個數是8個比特存儲),佔用一個字節,一共4個字節。

    [10, 5, 25]最大數25 < 2^5 = 32,每個數用5個比特表示,3*5=15比特,除以8,大約2個字節,加上5的標識位,一共3個字節。

    那麼總體size = 4 + 3(7)個字節,相當於24個字節,大大壓縮了空間。

    再看下圖表示

    Posting List內存壓縮

    同學們應該都知道越複雜的算法消耗的CPU性能就越大,比如常見的https,第一次交互會用非對稱密碼來驗證,驗證通過後就轉變成了對稱密碼驗證,FOR同樣如此,那麼ES是用什麼算法壓縮內存中的Posting List呢?

    Roaring Bitmaps 壓縮位圖索引

    Roaring Bitmaps 涉及到兩種數據結構 short[] 、bitmap。

    short好理解就是2個字節的整型。

    bitmap就是用比特表示數據,看下面的例子。

    Posting List:[1, 2, 4, 7, 10] -> [1, 1, 0, 1, 0, 0, 1,0, 0, 1],取最大的值10,那麼就用10個比特表示這組Posting List,第1, 2, 4, 7, 10位存在,就將相對應的“位”置為1,其他的為0。

    但這種bitmap數據結構有缺陷,看這組Posting List: [1, 3, 100000000] -> [1, 0, 1, 0, 0, 0, …, 0, 0, 1 ],最大數是1億,就要1億的比特表示,這麼算下來,反而消耗了更多的內存。

    那如何解決這個問題,其實也很簡單,跟上面一樣,將大數變成小數

    看下圖:

    第一步:將每個數除以65536,得到(商,餘數)。

    第二步:按照商,分成不同的block,也就是相同的商,放在同一個block裏面,餘數就是這個值在這個block裏面的位置(永遠不會超過65536,餘數嘛)。

    第三步:判斷底層block用什麼數據結構存儲數據,如果block裏面的餘數的個數超過4096個,就用short存儲,反之bitmap。

    上面那個圖是官網的圖,看下我畫的圖,可能更好理解些。

    到這裏,ES的索引原理就講完了,希望大家都能理解。

    四、Elasticsearch(ES)相對薄弱的地方

    1. 多表關聯

    其實ES有一個很重要的特性這裏沒有介紹到,也就是分佈式,每一個節點的數據和,才是整體數據。

    這也導致了多表關聯問題,雖然ES裏面也提供了Nested& Join 方法解決這個問題,但這裏還是不建議用。

    那這個問題在實際應用中應該如何解決? 其實也很簡單,裝換思路,ES無法解決,就到其他層解決,比如:應用層,用面向對象的架構,拆分查詢。

    2. 深度分頁

    分佈式架構下,取數據便不是那麼簡單,比如取前1000條數據,如果是10個節點,那麼每個節點都要取1000條,10個節點就是10000條,排序后,返回前1000條,如果是深度分頁就會變的相當的慢。

    ES提供的是Scroll + Scroll_after,但這個採取的是緩存的方式,取出10000條后,緩存在內存里,再來翻頁的時候,直接從緩存中取,這就代表着存在實時性問題。

    來看看百度是怎麼解決這個問題的。

    一樣在應用層解決,翻頁到一定的深度后,禁止翻頁。

    3. 更新應用

    頻繁更新的應用,用ES會有瓶頸,比如一些遊戲應用,要不斷的更新數據,用ES不是太適合,這個看大家自己的應用情況。

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 線性模型之邏輯回歸(LR)(原理、公式推導、模型對比、常見面試點),3種類型的梯度下降算法總結,李宏毅機器學習筆記2:Gradient Descent(附帶詳細的原理推導過程),L0、L1、L2范數正則化

    線性模型之邏輯回歸(LR)(原理、公式推導、模型對比、常見面試點),3種類型的梯度下降算法總結,李宏毅機器學習筆記2:Gradient Descent(附帶詳細的原理推導過程),L0、L1、L2范數正則化

    參考資料(要是對於本文的理解不夠透徹,必須將以下博客認知閱讀,方可全面了解LR):

    (1).

    (2).

    (3).

    (4).

    (5).

    (6).

     

    一、邏輯回歸介紹

      邏輯回歸(Logistic Regression)是一種廣義線性回歸。線性回歸解決的是回歸問題,預測值是實數範圍,邏輯回歸則相反,解決的是分類問題,預測值是[0,1]範圍。所以邏輯回歸名為回歸,實為分類。接下來讓我們用一句話來概括邏輯回歸(LR):

    邏輯回歸假設數據服從伯努利分佈,通過極大化似然函數的方法,運用梯度下降來求解參數,來達到將數據二分類的目的。

    這句話包含了五點,接下來一一介紹:

    • 邏輯回歸的假設
    • 邏輯回歸的損失函數
    • 邏輯回歸的求解方法
    • 邏輯回歸的目的
    • 邏輯回歸如何分類

     二、邏輯回歸的假設

    任何的模型都是有自己的假設,在這個假設下模型才是適用的。邏輯回歸的第一個基本假設是假設數據服從伯努利分佈。

    伯努利分佈:是一個離散型概率分佈,若成功,則隨機變量取值1;若失敗,隨機變量取值為0。成功概率記為p,失敗為q = 1-p。

    在邏輯回歸中,既然假設了數據分佈服從伯努利分佈,那就存在一個成功和失敗,對應二分類問題就是正類和負類,那麼就應該有一個樣本為正類的概率p,和樣本為負類的概率q = 1- p。具體我們寫成這樣的形式:

    邏輯回歸的第二個假設是正類的概率由sigmoid的函數計算,即

    即:

    寫在一起:

    個人理解,解釋一下這個公式,並不是用了樣本的標籤y,而是說你想要得到哪個的概率,y = 1時意思就是你想得到正類的概率,y = 0時就意思是你想要得到負類的概率。另外在求參數時,這個y是有用的,這點在下面會說到。

    另外關於這個值,  是個概率,還沒有到它真正能成為預測標籤的地步,更具體的過程應該是分別求出正類的概率,即y = 1時,和負類的概率,y = 0時,比較哪個大,因為兩個加起來是1,所以我們通常默認的是只用求正類概率,只要大於0.5即可歸為正類,但這個0.5是人為規定的,如果願意的話,可以規定為大於0.6才是正類,這樣的話就算求出來正類概率是0.55,那也不能預測為正類,應該預測為負類。

     理解了二元分類回歸的模型,接着我們就要看模型的損失函數了,我們的目標是極小化損失函數來得到對應的模型係數θ

     三、邏輯回歸的損失函數

     回顧下線性回歸的損失函數,由於線性回歸是連續的,所以可以使用模型誤差的的平方和來定義損失函數。但是邏輯回歸不是連續的,自然線性回歸損失函數定義的經驗就用不上了。不過我們可以用最大似然法(MLE)來推導出我們的損失函數。有點人把LR的loss function成為log損失函數,也有人把它稱為交叉熵損失函數(Cross Entropy)。

    極大似然估計:利用已知的樣本結果信息,反推最具有可能(最大概率)導致這些樣本結果出現的模型參數值(模型已定,參數未知)

    如何理解這句話呢?

    再聯繫到邏輯回歸里,一步步來分解上面這句話,首先確定一下模型是否已定,模型就是用來預測的那個公式:

    參數就是裏面的  ,那什麼是樣本結果信息,就是我們的x,y,是我們的樣本,分別為特徵和標籤,我們的已知信息就是在特徵取這些值的情況下,它應該屬於y類(正或負)。

    反推最具有可能(最大概率)導致這些樣本結果出現的參數,舉個例子,我們已經知道了一個樣本點,是正類,那麼我們把它丟入這個模型后,它預測的結果一定得是正類啊,正類才是正確的,才是我們所期望的,我們要盡可能的讓它最大,這樣才符合我們的真實標籤。反過來一樣的,如果你丟的是負類,那這個式子計算的就是負類的概率,同樣我們要讓它最大,所以此時不用區分正負類。

    這樣串下來,一切都說通了,概括一下:

    一個樣本,不分正負類,丟入模型,多的不說,就是一個字,讓它大

    一直只提了一個樣本,但對於整個訓練集,我們當然是期望所有樣本的概率都達到最大,也就是我們的目標函數,本身是個聯合概率,但是假設每個樣本獨立,那所有樣本的概率就可以由以下公式推到:

    設:

    似然函數:

    為了更方便求解,我們對等式兩邊同取對數,寫成對數似然函數:

    在機器學習中我們有損失函數的概念,其衡量的是模型預測錯誤的程度。如果取整個數據集上的平均對數似然損失,我們可以得到:

    即在邏輯回歸模型中,我們最大化似然函數和最小化損失函數實際上是等價的。所以說LR的loss function可以由MLE推導出來。

     四、邏輯回歸損失函數的求解

    解邏輯回歸的方法有非常多,主要有梯度下降(一階方法)和牛頓法(二階方法)。優化的主要目標是找到一個方向,參數朝這個方向移動之後使得損失函數的值能夠減小,這個方嚮往往由一階偏導或者二階偏導各種組合求得。邏輯回歸的損失函數是:

    隨機梯度下降:梯度下降是通過 J(w) 對 w 的一階導數來找下降方向,初始化參數w之後,並且以迭代的方式來更新參數,更新方式為 :

    其中 k 為迭代次數。每次更新參數后,可以通過比較  小於閾值或者到達最大迭代次數來停止迭代。

    梯度下降又有隨機梯度下降,批梯度下降,small batch 梯度下降三種方式:

    • 簡單來說 批梯度下降會獲得全局最優解,缺點是在更新每個參數的時候需要遍歷所有的數據,計算量會很大,並且會有很多的冗餘計算,導致的結果是當數據量大的時候,每個參數的更新都會很慢。
    • 隨機梯度下降是以高方差頻繁更新,優點是使得sgd會跳到新的和潛在更好的局部最優解,缺點是使得收斂到局部最優解的過程更加的複雜。
    • 小批量梯度下降結合了sgd和batch gd的優點,每次更新的時候使用n個樣本。減少了參數更新的次數,可以達到更加穩定收斂結果,一般在深度學習當中我們採用這種方法。

    加分項,看你了不了解諸如Adam,動量法等優化方法(在這就不展開了,以後有時間的話專門寫一篇關於優化方法的)。因為上述方法其實還有兩個致命的問題:

    • 第一個是如何對模型選擇合適的學習率。自始至終保持同樣的學習率其實不太合適。因為一開始參數剛剛開始學習的時候,此時的參數和最優解隔的比較遠,需要保持一個較大的學習率儘快逼近最優解。但是學習到後面的時候,參數和最優解已經隔的比較近了,你還保持最初的學習率,容易越過最優點,在最優點附近來回振蕩,通俗一點說,就很容易學過頭了,跑偏了。
    • 第二個是如何對參數選擇合適的學習率。在實踐中,對每個參數都保持的同樣的學習率也是很不合理的。有些參數更新頻繁,那麼學習率可以適當小一點。有些參數更新緩慢,那麼學習率就應該大一點。

    有關梯度下降原理以及優化算法詳情見我的博客:

     

    任何模型都會面臨過擬合問題,所以我們也要對邏輯回歸模型進行正則化考慮。常見的有L1正則化和L2正則化。

    L1 正則化

    LASSO 回歸,相當於為模型添加了這樣一個先驗知識:w 服從零均值拉普拉斯分佈。 首先看看拉普拉斯分佈長什麼樣子:

    由於引入了先驗知識,所以似然函數這樣寫:

    取 log 再取負,得到目標函數:

    等價於原始損失函數的後面加上了 L1 正則,因此 L1 正則的本質其實是為模型增加了“模型參數服從零均值拉普拉斯分佈”這一先驗知識。

    L2 正則化

    Ridge 回歸,相當於為模型添加了這樣一個先驗知識:w 服從零均值正態分佈。

    首先看看正態分佈長什麼樣子:

    由於引入了先驗知識,所以似然函數這樣寫:

    取 ln 再取負,得到目標函數:

    等價於原始的損失函數後面加上了 L2 正則,因此 L2 正則的本質其實是為模型增加了“模型參數服從零均值正態分佈”這一先驗知識。

    其餘有關正則化的內容詳見:

    五、邏輯回歸的目的

     該函數的目的便是將數據二分類,提高準確率。

    六、邏輯回歸如何分類

    這個在上面的時候提到了,要設定一個閾值,判斷正類概率是否大於該閾值,一般閾值是0.5,所以只用判斷正類概率是否大於0.5即可。

    七、為什麼LR不使用平方誤差(MSE)當作損失函數?

    1.  平方誤差損失函數加上sigmoid的函數將會是一個非凸的函數,不易求解,會得到局部解,用對數似然函數得到高階連續可導凸函數,可以得到最優解。

    2.  其次,是因為對數損失函數更新起來很快,因為只和x,y有關,和sigmoid本身的梯度無關。如果你使用平方損失函數,你會發現梯度更新的速度和sigmod函數本身的梯度是很相關的。sigmod函數在它在定義域內的梯度都不大於0.25。這樣訓練會非常的慢。

    八、邏輯回歸的優缺點

    優點:

    • 形式簡單,模型的可解釋性非常好。從特徵的權重可以看到不同的特徵對最後結果的影響,某個特徵的權重值比較高,那麼這個特徵最後對結果的影響會比較大。
    • 模型效果不錯。在工程上是可以接受的(作為baseline),如果特徵工程做的好,效果不會太差,並且特徵工程可以大家并行開發,大大加快開發的速度。
    • 訓練速度較快。分類的時候,計算量僅僅只和特徵的數目相關。並且邏輯回歸的分佈式優化sgd發展比較成熟,訓練的速度可以通過堆機器進一步提高,這樣我們可以在短時間內迭代好幾個版本的模型。
    • 資源佔用小,尤其是內存。因為只需要存儲各個維度的特徵值。
    • 方便輸出結果調整。邏輯回歸可以很方便的得到最後的分類結果,因為輸出的是每個樣本的概率分數,我們可以很容易的對這些概率分數進行cut off,也就是劃分閾值(大於某個閾值的是一類,小於某個閾值的是一類)。

    缺點:

    • 準確率並不是很高。因為形式非常的簡單(非常類似線性模型),很難去擬合數據的真實分佈。
    • 很難處理數據不平衡的問題。舉個例子:如果我們對於一個正負樣本非常不平衡的問題比如正負樣本比 10000:1.我們把所有樣本都預測為正也能使損失函數的值比較小。但是作為一個分類器,它對正負樣本的區分能力不會很好。
    • 處理非線性數據較麻煩。邏輯回歸在不引入其他方法的情況下,只能處理線性可分的數據,或者進一步說,處理二分類的問題 。
    • 邏輯回歸本身無法篩選特徵。有時候,我們會用gbdt來篩選特徵,然後再上邏輯回歸。

     

     

    西瓜書中提到了如何解決LR缺點中的藍色字體所示的缺點:

     

    九、 邏輯斯特回歸為什麼要對特徵進行離散化。

    • 非線性!非線性!非線性!邏輯回歸屬於廣義線性模型,表達能力受限;單變量離散化為N個后,每個變量有單獨的權重,相當於為模型引入了非線性,能夠提升模型表達能力,加大擬合; 離散特徵的增加和減少都很容易,易於模型的快速迭代;
    • 速度快!速度快!速度快!稀疏向量內積乘法運算速度快,計算結果方便存儲,容易擴展;
    • 魯棒性!魯棒性!魯棒性!離散化后的特徵對異常數據有很強的魯棒性:比如一個特徵是年齡>30是1,否則0。如果特徵沒有離散化,一個異常數據“年齡300歲”會給模型造成很大的干擾;
    • 方便交叉與特徵組合:離散化后可以進行特徵交叉,由M+N個變量變為M*N個變量,進一步引入非線性,提升表達能力;
    • 穩定性:特徵離散化后,模型會更穩定,比如如果對用戶年齡離散化,20-30作為一個區間,不會因為一個用戶年齡長了一歲就變成一個完全不同的人。當然處於區間相鄰處的樣本會剛好相反,所以怎麼劃分區間是門學問;
    • 簡化模型:特徵離散化以後,起到了簡化了邏輯回歸模型的作用,降低了模型過擬合的風險。

    十、與其他模型的對比

    與SVM

    相同點

    1. 都是線性分類器。本質上都是求一個最佳分類超平面。都是監督學習算法。
    2. 都是判別模型。通過決策函數,判別輸入特徵之間的差別來進行分類。

    • 常見的判別模型有:KNN、SVM、LR。
    • 常見的生成模型有:樸素貝恭弘=叶 恭弘斯,隱馬爾可夫模型。

     

    不同點

    (1). 本質上是損失函數不同
    LR的損失函數是交叉熵:

    SVM的目標函數:

    • 邏輯回歸基於概率理論,假設樣本為正樣本的概率可以用sigmoid函數(S型函數)來表示,然後通過極大似然估計的方法估計出參數的值。
    • 支持向量機基於幾何間隔最大化原理,認為存在最大幾何間隔的分類面為最優分類面。

    (2). 兩個模型對數據和參數的敏感程度不同

    • SVM考慮分類邊界線附近的樣本(決定分類超平面的樣本)。在支持向量外添加或減少任何樣本點對分類決策面沒有任何影響;
    • LR受所有數據點的影響。直接依賴數據分佈,每個樣本點都會影響決策面的結果。如果訓練數據不同類別嚴重不平衡,則一般需要先對數據做平衡處理,讓不同類別的樣本盡量平衡。
    • LR 是參數模型,SVM 是非參數模型,參數模型的前提是假設數據服從某一分佈,該分佈由一些參數確定(比如正太分佈由均值和方差確定),在此基礎上構建的模型稱為參數模型;非參數模型對於總體的分佈不做任何假設,只是知道總體是一個隨機變量,其分佈是存在的(分佈中也可能存在參數),但是無法知道其分佈的形式,更不知道分佈的相關參數,只有在給定一些樣本的條件下,能夠依據非參數統計的方法進行推斷。

    (3). SVM 基於距離分類,LR 基於概率分類。

    • SVM依賴數據表達的距離測度,所以需要對數據先做 normalization;LR不受其影響。

    (4). 在解決非線性問題時,支持向量機採用核函數的機制,而LR通常不採用核函數的方法。

    • SVM算法里,只有少數幾個代表支持向量的樣本參与分類決策計算,也就是只有少數幾個樣本需要參与核函數的計算。
    • LR算法里,每個樣本點都必須參与分類決策的計算過程,也就是說,假設我們在LR里也運用核函數的原理,那麼每個樣本點都必須參与核計算,這帶來的計算複雜度是相當高的。尤其是數據量很大時,我們無法承受。所以,在具體應用時,LR很少運用核函數機制。

    (5). 在小規模數據集上,Linear SVM要略好於LR,但差別也不是特別大,而且Linear SVM的計算複雜度受數據量限制,對海量數據LR使用更加廣泛。

    (6). SVM的損失函數就自帶正則,而 LR 必須另外在損失函數之外添加正則項。

    那怎麼根據特徵數量和樣本量來選擇SVM和LR模型呢?Andrew NG的課程中給出了以下建議:

     

    • 如果Feature的數量很大,跟樣本數量差不多,這時候選用LR或者是Linear Kernel的SVM
    • 如果Feature的數量比較小,樣本數量一般,不算大也不算小,選用SVM+Gaussian Kernel
    • 如果Feature的數量比較小,而樣本數量很多,需要手工添加一些feature變成第一種情況。(LR和不帶核函數的SVM比較類似。)

     

    插入一個知識點:判別模型與生成模型的區別?

    公式上看

    • 生成模型: 學習時先得到P(x,y),繼而得到 P(y|x)。預測時應用最大后驗概率法(MAP)得到預測類別 y。
    • 判別模型: 直接學習得到P(y|x),利用MAP得到 y。或者直接學得一個映射函數 y=f(x) 。

    直觀上看

    • 生成模型: 關注數據是如何生成的
    • 判別模型: 關注類別之間的差別

    數據要求:生成模型需要的數據量比較大,能夠較好地估計概率密度;而判別模型對數據樣本量的要求沒有那麼多。

    更多區別見

     

    與樸素貝恭弘=叶 恭弘斯

    相同點

    樸素貝恭弘=叶 恭弘斯和邏輯回歸都屬於分類模型,當樸素貝恭弘=叶 恭弘斯的條件概率  服從高斯分佈時,它計算出來的 P(Y=1|X) 形式跟邏輯回歸是一樣的。

    不同點

    • 邏輯回歸是判別式模型 p(y|x),樸素貝恭弘=叶 恭弘斯是生成式模型 p(x,y):判別式模型估計的是條件概率分佈,給定觀測變量 x 和目標變量 y 的條件模型,由數據直接學習決策函數 y=f(x) 或者條件概率分佈 P(y|x) 作為預測的模型。判別方法關心的是對於給定的輸入 x,應該預測什麼樣的輸出 y;而生成式模型估計的是聯合概率分佈,基本思想是首先建立樣本的聯合概率概率密度模型 P(x,y),然後再得到后驗概率 P(y|x),再利用它進行分類,生成式更關心的是對於給定輸入 x 和輸出 y 的生成關係;
    • 樸素貝恭弘=叶 恭弘斯的前提是條件獨立,每個特徵權重獨立,所以如果數據不符合這個情況,樸素貝恭弘=叶 恭弘斯的分類表現就沒邏輯會好了。

    十一、多分類問題(LR解決多分類問題)

    參考西瓜書!!!

    現實中我們經常遇到不只兩個類別的分類問題,即多分類問題,在這種情形下,我們常常運用“拆分”的策略,通過多個二分類學習器來解決多分類問題,即將多分類問題拆解為多個二分類問題,訓練出多個二分類學習器,最後將多個分類結果進行集成得出結論。最為經典的拆分策略有三種:“一對一”(OvO)、“一對其餘”(OvR)和“多對多”(MvM),核心思想與示意圖如下所示。

    • OvO:給定數據集D,假定其中有N個真實類別,將這N個類別進行兩兩配對(一個正類/一個反類),從而產生N(N-1)/2個二分類學習器,在測試階段,將新樣本放入所有的二分類學習器中測試,得出N(N-1)個結果,最終通過投票產生最終的分類結果。

              優點:它在一定程度上規避了數據集 unbalance 的情況,性能相對穩定,並且需要訓練的模型數雖然增多,但是每次訓練時訓練集的數量都降低很多,其訓練效率會提高。

         缺點:訓練出更多的 Classifier,會影響預測時間。

    • OvM:給定數據集D,假定其中有N個真實類別,每次取出一個類作為正類,剩餘的所有類別作為一個新的反類,從而產生N個二分類學習器,在測試階段,得出N個結果,若僅有一個學習器預測為正類,則對應的類標作為最終分類結果。

      優點:普適性還比較廣,可以應用於能輸出值或者概率的分類器,同時效率相對較好,有多少個類別就訓練多少個分類器。

      缺點:很容易造成訓練集樣本數量的不平衡(Unbalance),尤其在類別較多的情況下,經常容易出現正類樣本的數量遠遠不及負類樣本的數量,這樣就會造成分類器的偏向性。

    • MvM:給定數據集D,假定其中有N個真實類別,每次取若干個類作為正類,若干個類作為反類(通過ECOC碼給出,編碼),若進行了M次劃分,則生成了M個二分類學習器,在測試階段(解碼),得出M個結果組成一個新的碼,最終通過計算海明/歐式距離選擇距離最小的類別作為最終分類結果。

     

    十一、邏輯回歸實例(數據來源於Kaggle)

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

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 取道對歐輸送天然氣 俄烏達成初步協議

    摘錄自2019年12月20日中央社報導

    俄羅斯天然氣工業公司2019年向歐洲供應2008億立方公尺的天然氣,其中有約40%是借道烏克蘭運送,讓烏克蘭每年賺得約30億美元(約新台幣904億元)的過境費,此輸送合約將於年底到期,但自莫斯科2014年併吞克里米亞並支持烏克蘭東部的分離主義分子叛亂活動後,雙方關係急轉直下。

    俄羅斯與烏克蘭歷經數個月的艱難談判,在即將到來的新年截止期限前,簽署取道烏克蘭將俄羅斯天然氣運往歐洲的初步協議。

    俄羅斯通訊社引述俄羅斯天然氣工業公司(Gazprom)發言人說法報導:「俄羅斯與烏克蘭已經簽署諒解備忘錄。」但沒有提供合約細節。法新社報導,俄羅斯能源部長諾瓦克(Alexander Novak)說,這是5年合約,將在月底前簽署。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • 築起和平之牆 修復河壩 達佛「氣候變遷戰爭」平息有望

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 日產聆風在美“充電不花錢”項目將新增三個城市

    日產聆風在美國進行的“充電不花錢”(No Charge to Charge)項目將新增三個城市。

    據報導,此前有26個不同市城市消費者可以享受這個項目,不過好消息是,現在位於紐約市、費城與聖巴巴拉市的聆風用戶也可以進行免費充電了。用戶可以在指定的公共充電站使用贈送的費用充電,同樣,用戶也可以使用EZ-Charge網站或APP輕鬆的定位合作的充電站。

    “充電不花錢”項目針對日產聆風電動汽車的購買者或租賃者,並提供免費兩年的充電機會。新車主將受到一張EZ-Charge卡,這張卡可以介入充電點(ChargePoint)。

    除了新增的城市,其他26個城市是三藩市、洛杉磯、沙加緬度、聖地牙哥、夫勒斯諾市、波特蘭、芝加哥、達拉斯-沃思堡、惠斯頓、印度安納波利斯、那什維爾、鳳凰城、丹佛、華府、巴爾的摩、波斯頓、蒙特利、亞特蘭大、 羅利、鹽湖城和明尼阿波利斯—聖保羅都會區。
     

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • 騰訊、和諧汽車、富士康“互聯網+智慧電動汽車”專案公佈企業負責人

    騰訊、和諧汽車、富士康“互聯網+智慧電動汽車”專案公佈企業負責人

    和諧富騰互聯網加智慧電動汽車公司(以下簡稱“和諧富騰”)日前宣佈旗下“互聯網+智慧電動汽車”企業負責人,畢福康博士(Dr. Carsten Breitfeld)將擔任該企業首席執行官,戴雷博士(Dr. Daniel Kirchert)將擔任首席運營官。兩位管理層到任後將即刻啟動企業的運營。畢福康博士和戴雷博士亦將是該企業的事業合夥人和公司董事會成員。

    左:畢福康博士(Dr. Carsten Breitfeld);右:戴雷博士(Dr. Daniel Kirchert)

    和諧富騰是由中國和諧新能源汽車控股有限公司、鴻海集團與騰訊集團透過各自附屬實體聯合創立的創新投資平臺,“互聯網+智慧電動汽車”是該平臺旗下核心戰略專案和獨立企業,旨在開發面向未來的個人出行解決方案,塑造源自中國、佈局世界的高端品牌,為消費者提供智慧、愉悅、生態友好的駕乘體驗。

    畢福康博士擁有機械工程學博士學位,是全球電動汽車研發領域的一流專家。畢福康博士此前在寶馬集團總部工作20年,擔任過底盤開發、傳動系統開發及產品戰略等方面的多個高級管理崗位,2010年起擔任寶馬集團新一代電動超級跑車i8項目總監,成為這一世界汽車行業劃時代旗艦車型的研發主腦。通過引入革命性的開發流程,畢福康博士帶領團隊實現了i8車型于2014年成功面世,在產品性能、材料、技術革新及研發速度等各個方面顯著優於傳統汽車產品,創下了全球汽車行業新的標杆。在畢福康博士的推動下,i8車型還引進了創新的銷售和客戶體驗機制。

    戴雷博士是中國豪華汽車領域擁有最豐富銷售、運營和品牌塑造經驗的高層主管之一,也是業內公認的“中國通”。戴雷博士此前曾擔任東風英菲尼迪汽車有限公司總經理和華晨寶馬汽車有限公司行銷高級副總裁,相關品牌在其任內均創下豪華車市場的銷售增長紀錄,在品牌塑造和市場行銷方面也創造了若干標杆性的案例。戴雷博士在產品戰略、銷售網路發展和合資企業組建運營方面也擁有深厚的經驗。

    畢福康博士和戴雷博士將在就任後與傳媒見面,並就“互聯網+智慧電動汽車”的戰略規劃和企業具體運營資訊與傳媒和公眾溝通。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 「車和家」產品計畫 中國微信現身

    「車和家」產品計畫 中國微信現身

    4月16日,中國汽車之家創始人李想在微信朋友圈發文表示,目前個人精力110%投入在車和家上,並首次透露具體的產品計畫:車和家將打造兩款車型來滿足90%的城市需求,一款小而美的SEV,一款大而強的SUV。

    3月底,李想的電動車創業項目「車和家」完成A輪融資,並且申報造車資質。李想預計在四年時間裡,打造小而美電動車大概需要2億美元的投入。車和家已經準備好了1億美元,另外的1億美元將通過融資獲得。

    李想強調,車和家不是互聯網造車,也不玩互聯網概念,而是標準的新生代汽車品牌。一是自主研發,研發與製造工程師團隊平均汽車行業從業15年以上;二是自主生產,自建常州武進750畝的30萬產能的全鋁工廠,以及配套的BMS與電池廠;三是自己提供銷售與服務,打破4S店高價、低質、效率低的現狀。車和家的目標是像蘋果一樣去創新產品與商業模式,像華為一樣扎扎實實搞研發,並放眼全球市場。

    以下為李想微信朋友圈全文(簡體中文,截圖):

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • HashMap7淺析

    HashMap7淺析

    一、概述

      HashMap,基於哈希結構的Map接口的一個實現,無序,允許null鍵值對,線程不安全的。可以使用集合工具類Collections中的synchronizedMap方法,去創建一個線程安全的集合map。

      在jdk1.7中,HashMap主要是基於 數組+鏈表 的結構實現的。鏈表的存在主要是解決 hash 衝突而存在的。插入數據的時候,計算key的hash值,取得存儲的數組下標,如果衝突已有元素,則會在衝突地址上生成個鏈表,再通過key的比較,鏈表是否已存在,存在則覆蓋,不存在則鏈表上添加。這種方式,如果存在大量衝突的時候,會導致鏈表過長,那麼直接導致的就是犧牲了查詢和添加的效率。所以在jdk1.8版本之後,使用的就是 數組 + 鏈表 + 紅黑樹,當鏈表長度超過 8(實際加上初始的節點,整個有效長度是 9) 的時候,轉為紅黑樹存儲。

      本文中內容,主要基於jdk1.7版本,單線程環境下使用的HahsMap沒有啥問題,但是當在多線程下使用的時候,則可能會出現併發異常,具體表象是CPU會直線上升100%。下面是主要介紹相關的存取以及為什麼會出現線程安全性問題。

    二、結構

      

      HashMap默認初始化size=16的哈希數組,然後通過計算待存儲的key的hash值,去計算得到哈希數組的下標值,然後放入鏈表中(新增節點或更新)。鏈表的存在即是解決hash衝突的。

    三、源碼實現分析

      1、存儲具體數據的table數組:

          

        Entry為HashMap中的靜態內部類,其具體結構如下圖

          

        key、value屬性就是存儲鍵值對的,next則是指向鏈表的下一個元素節點。

         2、 默認初始化方法:

        

        默認構造方法,不對table進行初始化new(真正初始化動作放在put中,後面會看到),只是設置參數的默認值,hashmap長度和table長度初始化成DEFAULT_INITIAL_CAPACITY(16),加載因子loadFactor默認DEFAULT_LOAD_FACTOR(0.75f,至於為什麼是0.75,這個可以參見 )。

        加載因子:默認情況下,16*0.75=12,也就是在存儲第13個元素的時候,就會進行擴容(jdk1.7的threshold真正計算放在第一次初始化中,後面會再提及)。此元素的設置,直接影響到的是key的hash衝突問題。

      3、put方法

     public V put(K key, V value) {
       
    if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }

      3.1、EMPTY_TABLE是HashMap中的一個靜態的空的Entry數組,table也是HashMap的一個屬性,默認就是EMPTY_TABLE(這兩句可參見上面源碼),table就是我們真正數據存儲使用的。
      3.2、前面提及,無參構造的時候,並未真正完成對HashMap的初始化new操作,而僅僅只是設置幾個常量,所以在第一次put數據的時候,table是空的。則會進入下面的初始化table方法中。

    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    
    private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        int capacity = roundUpToPowerOf2(toSize);
    
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); //計算加載因子,默認情況下結果為12
        table = new Entry[capacity];  //真正的初始化table數組
        initHashSeedAsNeeded(capacity);
    }

      3.3、key的null判斷

    if (key == null)
        return putForNullKey(value);
    
    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
    
        createEntry(hash, key, value, bucketIndex);
    }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

      具體步驟解析:

        1、key為null,取出table[0]的鏈表結構Enrty,如果取出的元素不為null,則對其進行循環遍歷,查找其中是否存在key為null的節點元素。

           2、如果存在key == null的節點,則使用新的value去更新節點的oldValue,並且將oldValue返回。

        3、如果不存在key == null的元素,則執行新增元素addEntry方法:

          (1)判斷是否需要擴容,size為當前數組table中,已存放的Entry鏈表個數,更直接點說,就是map.size()方法的返回值。threshold上面的真正初始化HashMap的時候已經提到,默認情況下,計算得到 threshold=12。若同時滿足  (size >= threshold) && (null != table[bucketIndex]) ,則對map進行2倍的擴容,然後對key進行重新計算hash值和新的數組下標。

          (2)創建新的節點原色createEntry方法,首先獲取table數組中下標為bucketIndex的鏈表的表頭元素,然後新建個Entry作為新的表頭,並且新表頭其中的next指向老的表頭數據。

      3.4、key不為null的存儲  
        原理以及過程上通key==null的大體相同,只不過,key==null的時候,固定是獲取table[0]的鏈表進行操作,而在不為key != null的時候,下標位置是通過
      int hash = hash(key); int i = indexFor(hash, table.length); 計算得到的

      static int indexFor(int h, int length) {
            // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
            return h & (length-1);
        }

      很清晰的就能看明白,先計算key的hash,然後與當前table的長度進行相與,這樣計算得到待存放數據的下標。得到下標后,過程就與key==null一致了,遍歷是否存在,存在則更新並返回oldVlaue,不存在則新建Entry。

      4、get方法

     public V get(Object key) {
            if (key == null)
                return getForNullKey();
            Entry<K,V> entry = getEntry(key);
    
            return null == entry ? null : entry.getValue();
        }
        如果key == null,則調用getForNullKey方法,遍歷table[0]處的鏈表。
    private V getForNullKey() {
            if (size == 0) {
                return null;
            }
            for (Entry<K,V> e = table[0]; e != null; e = e.next) {
                if (e.key == null)
                    return e.value;
            }
            return null;
        }

      如果key != null,則調用getEntry,根據key計算得到在table數組中的下標,獲取鏈表Entry,然後遍歷查找元素,key相等,則返回該節點元素。

     final Entry<K,V> getEntry(Object key) {
            if (size == 0) {
                return null;
            }
    
            int hash = (key == null) ? 0 : hash(key);
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            }
            return null;
        }

    四、線程不安全分析

      上述,主要淺析了下HashMap的存取過程,HashMap的線程安全性問題主要也就是在上述的擴容resize方法上,下面來看看在高併發下,擴容后,是如何引起100%問題的。

      1、在進行新元素 put 的時候,這在上面中的3.3的代碼片段中可以查看,addEntry 添加新節點的時候,會計算是否需要擴容處理:(size >= threshold) && (null != table[bucketIndex]) 。

      2、如果擴容的話,會接下來調用 resize 方法

     void resize(int newCapacity) {
            Entry[] oldTable = table;
            int oldCapacity = oldTable.length;
            if (oldCapacity == MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return;
            }
    
            Entry[] newTable = new Entry[newCapacity];
            //關鍵性代碼,構建新hashmap並將老的數據移動過來
            transfer(newTable, initHashSeedAsNeeded(newCapacity));
            table = newTable;
            threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
        }

      3、其中,出現100%問題的關鍵就是上面的 transfer 方法,新建hashmap移動複製老數據

     1  void transfer(Entry[] newTable, boolean rehash) {
     2         int newCapacity = newTable.length;
     3         for (Entry<K,V> e : table) {
     4             // 遍歷老的HashMap,當遇到不為空的節點的是,進入移動方法
     5             while(null != e) {
     6                 // 首先創建個Entry節點 指向該節點所在鏈表的下一個節點數據
     7                 Entry<K,V> next = e.next;
     8                 if (rehash) {
     9                     e.hash = null == e.key ? 0 : hash(e.key);
    10                 }
    11               // 計算老的數據在新Hashmap中的下標位置
    12                 int i = indexFor(e.hash, newCapacity);
    13              // 將新HashMap中相應位置的元素,掛載到老數據的後面(不管有無數據)
    14                 e.next = newTable[i];
    15                 // 將新HashMap中相應位置指向上面已經成功掛載新數據的老數據
    16              newTable[i] = e;
    17              // 移動到鏈表節點中的下一個數據,繼續複製節點
    18                 e = next;
    19             }
    20         }
    21     }    

      問題的關鍵就在上述的14、15行上,這兩行的動作,在高併發下可能就會造成循環鏈表,循環鏈表在等待下一個嘗試 get 獲取數據的時候,就悲劇了。下面舉例模擬說說這個過程:

      (1)假設目前某個位置的鏈表存儲結構為 A -> B -> C,有兩個線程同時進行擴容操作

      (2)線程1執行到第7行 Entry<K,V> next = e.next; 的時候被掛起了,此時,線程1的 e 指向 A , next 指向的是 B

      (3)線程2執行完成了整個的擴容過程,那麼此時的鏈表結構應該是變為了 C -> B -> A

      (4)線程1喚醒繼續執行,而需要操作的鏈表實際就變成了了上述線程2完成后的 C ->B -> A,下面分為幾步去完成整個操作:

          第一次循環:

            (i)執行 e.next = newTable[i] ,將 A 的 next 指向線程1的新的HashMap,由於此時無數據,所以 e.next = null

            (ii)執行 newTable[i] = e,將線程1的新的HashMap的第一個元素指向 A 

            (iii)執行e = next,移動到鏈表中的下一個元素,也就是上面的(2)中的 線程掛起的時候的 B

          第二次循環:

            (i)執行 Entry<K,V> next = e.next,此時的 e 指向 B,next指向 A

            (ii)執行 e.next = newTable[i] ,將 B 的 next 指向線程1的新的HashMap,由於此時有數據A,所以 e.next = A

            (iii)執行 newTable[i] = e,將線程1的新的HashMap的第一個元素指向 B,此時線程1的新Hashmap鏈表結構為B -> A

            (iiii)執行e = next,移動到鏈表中的下一個元素 A

          第三次循環:

            (i)執行 Entry<K,V> next = e.next,此時的 e 指向 A,next指向 null

            (ii)執行 e.next = newTable[i] ,將 A 的 next 指向線程1的新的HashMap,由於此時有數據B,所以 e.next = B

            (iii)執行 newTable[i] = e,將線程1的新的HashMap的第一個元素指向 A ,此時線程1的新Hashmap鏈表結構為 A -> B -> A

            (iiii)執行e = next,移動到鏈表中的下一個元素,已移動到鏈表結尾,結束 while 循環,完成鏈表的轉移。

      (5)上述過程中,很顯然的,最終的鏈表結構中,出現了 A -> B -> A 的循環結構。擴容完成了,剩下的等待的是get獲取的時候, getEntry 方法中 for循環e = e.next中就永遠出不來了。

      注意:擴容過程中,newTable是每個擴容線程獨有的,共享的只是每個Entry節點數據,最終的擴容是會調用 table = newTable 賦值操作完成。

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

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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