ASP.NET Core 對Controller進行單元測試

單元測試對我們的代碼質量非常重要。很多同學都會對業務邏輯或者工具方法寫測試用例,但是往往忽略了對Controller層寫單元測試。我所在的公司沒見過一個對Controller寫過測試的。今天來演示下如果對Controller進行單元測試。以下內容默認您對單元測試有所了解,比如如何mock一個接口。在這裏多叨叨一句,面向接口的好處,除了能夠快速的替換實現類(其實大部分接口不會有多個實現),最大的好處就是可以進行mock,可以進行單元測試。

測試Action

下面的Action非常簡單,非常常見的一種代碼。根據用戶id去獲取用戶信息然後展示出來。下面看看如何對這個Action進行測試。

   public class UserController : Controller
    {
        private readonly IUserService _userService;
        public UserController(IUserService userService)
        {
            _userService = userService;
        }

        public IActionResult UserInfo(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException(nameof(userId));
            }

            var user = _userService.Get(userId);
            return View(user);
        }
      
    }

測試代碼:

  [TestMethod()]
        public void UserInfoTest()
        {

            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User());

            var ctrl = new UserController(userService.Object);
            //對空參數進行assert
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            //對空參數進行assert
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

我們對一個Action進行測試主要的思路就是模擬各種入參,使測試代碼能夠到達所有的分支,並且Assert輸出是否為空,是否為指定的類型等。

對ViewModel進行測試

我們編寫Action的時候還會涉及ViewModel給視圖傳遞數據,這部分也需要進行測試。修改測試用例,加入對ViewModel的測試代碼:

  [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            //對viewModel進行assert
            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);
        }

對ViewData進行測試

我們編寫Action的時候還會涉及ViewData給視圖傳遞數據,這部分同樣需要測試。修改Action代碼,對ViewData進行賦值:

   public IActionResult UserInfo(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException(nameof(userId));
            }

            var user = _userService.Get(userId);

            ViewData["title"] = "user_info";

            return View(user);
        }
      

修改測試用例,加入對ViewData的測試代碼:

   [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);
            //對viewData進行assert
            Assert.IsTrue(vr.ViewData.ContainsKey("title"));
            var title = vr.ViewData["title"];
            Assert.AreEqual("user_info", title);
        }

對ViewBag進行測試

因為ViewBag事實上是ViewData的dynamic類型的包裝,所以Action代碼不用改,可以直接對ViewBag進行測試:

     [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);

            Assert.IsTrue(vr.ViewData.ContainsKey("title"));
            var title = vr.ViewData["title"];
            Assert.AreEqual("user_info", title);
            //對viewBag進行assert
            string title1 = ctrl.ViewBag.title;
            Assert.AreEqual("user_info", title1);
        }

設置HttpContext

我們編寫Action的時候很多時候需要調用基類里的HttpContext,比如獲取Request對象,獲取Path,獲取Headers等等,所以有的時候需要自己實例化HttpContext以進行測試。

    var ctrl = new AccountController();
    ctrl.ControllerContext = new ControllerContext();
    ctrl.ControllerContext.HttpContext = new DefaultHttpContext();

對HttpContext.SignInAsync進行mock

我們使用ASP.NET Core框架進行登錄認證的時候,往往使用HttpContext.SignInAsync進行認證授權,所以單元測試的時候也需要進行mock。下面是一個典型的登錄Action,對密碼進行認證后調用SignInAsync在客戶端生成登錄憑證,否則跳到登錄失敗頁面。

   public async Task<IActionResult> Login(string password)
        {
            if (password == "123")
            {
                var claims = new List<Claim>
                {
                  new Claim("UserName","x")
                };
                var authProperties = new AuthenticationProperties
                {
                };
                var claimsIdentity = new ClaimsIdentity(
                  claims, CookieAuthenticationDefaults.AuthenticationScheme);
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity),
                    authProperties);
                return Redirect("login_success");
            }

            return Redirect("login_fail");
        }

HttpContext.SignInAsync其實個時擴展方法,SignInAsync其實最終是調用了IAuthenticationService里的SignInAsync方法。所以我們需要mock的就是IAuthenticationService接口,否者代碼走到HttpContext.SignInAsync會提示找不到IAuthenticationService的service。而IAuthenticationService本身是通過IServiceProvider注入到程序里的,所以同時需要mock接口IServiceProvider。

    [TestMethod()]
        public async Task LoginTest()
        {
            var ctrl = new AccountController();

            var authenticationService = new Mock<IAuthenticationService>();
            var sp = new Mock<IServiceProvider>();
            sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
                .Returns(() => {
                    return authenticationService.Object;
                });
            ctrl.ControllerContext = new ControllerContext();
            ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
            ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;

           var result = await ctrl.Login("123");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectResult));
            var rr = result as RedirectResult;
            Assert.AreEqual("login_success", rr.Url);

            result = await ctrl.Login("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectResult));
            rr = result as RedirectResult;
            Assert.AreEqual("login_fail", rr.Url);
        }

對HttpContext.AuthenticateAsync進行mock

HttpContext.AuthenticateAsync同樣比較常用。這個擴展方法同樣是在IAuthenticationService里,所以測試代碼跟上面的SignInAsync類似,只是需要對AuthenticateAsync繼續mock返回值success or fail。

     public async Task<IActionResult> Login()
        {
            if ((await HttpContext.AuthenticateAsync()).Succeeded)
            {
                return Redirect("/home");
            }

            return Redirect("/login");
        }

測試用例:


        [TestMethod()]
        public async Task LoginTest1()
        {
            var authenticationService = new Mock<IAuthenticationService>();
            //設置AuthenticateAsync為success
            authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
                .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal(), "")));
            var sp = new Mock<IServiceProvider>();
            sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
                .Returns(() => {
                    return authenticationService.Object;
                });

            var ctrl = new AccountController();
            ctrl.ControllerContext = new ControllerContext();
            ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
            ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;

            var act = await ctrl.Login();
            Assert.IsNotNull(act);
            Assert.IsInstanceOfType(act, typeof(RedirectResult));
            var rd = act as RedirectResult;
            Assert.AreEqual("/home", rd.Url);
            //設置AuthenticateAsync為fail
            authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
               .ReturnsAsync(AuthenticateResult.Fail(""));

            act = await ctrl.Login();
            Assert.IsNotNull(act);
            Assert.IsInstanceOfType(act, typeof(RedirectResult));
            rd = act as RedirectResult;
            Assert.AreEqual("/login", rd.Url);

        }

Filter進行測試

我們寫Controller的時候往往需要配合很多Filter使用,所以Filter的測試也很重要。下面演示下如何對Fitler進行測試。

    public class MyFilter: ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (context.HttpContext.Request.Path.Value.Contains("/abc/"))
            {
                context.Result = new ContentResult() {
                    Content = "拒絕訪問"
                };
            }

            base.OnActionExecuting(context);
        }
    }

對Filter的測試最主要的是模擬ActionExecutingContext參數,以及其中的HttpContext等,然後對預期進行Assert。

       [TestMethod()]
        public void OnActionExecutingTest()
        {
            var filter = new MyFilter();
            var actContext = new ActionContext(new DefaultHttpContext(),new RouteData(), new ActionDescriptor());
            actContext.HttpContext.Request.Path = "/abc/123";
            var listFilters = new List<IFilterMetadata>();
            var argDict = new Dictionary<string, object>();
            var actExContext = new ActionExecutingContext(
                actContext ,
                listFilters ,
                argDict ,
                new AccountController()
                );
             filter.OnActionExecuting(actExContext);

            Assert.IsNotNull(actExContext.Result);
            Assert.IsInstanceOfType(actExContext.Result, typeof(ContentResult));
            var cr = actExContext.Result as ContentResult;
            Assert.AreEqual("拒絕訪問", cr.Content);

            actContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
            actContext.HttpContext.Request.Path = "/1/123";
            listFilters = new List<IFilterMetadata>();
            argDict = new Dictionary<string, object>();
            actExContext = new ActionExecutingContext(
                actContext,
                listFilters,
                argDict,
                new AccountController()
                );
            filter.OnActionExecuting(actExContext);
            Assert.IsNull(actExContext.Result);
        }

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

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

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

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

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

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

※超省錢租車方案

聚甘新

文本挖掘之情感分析(一)

一、文本挖掘  

     文本挖掘則是對文本進行處理,從中挖掘出來文本中有用的信息和關鍵的規則,在文本挖掘領域應用最往廣泛的是對文本進行分類和聚類,其挖掘的方法分為無監督學習和監督學習。文本挖掘還可以劃分為7大類:關鍵詞提取、文本摘要、文本主題模型、文本聚類、文本分類、觀點提取、情感分析。

   關鍵詞提取:對長文本的內容進行分析,輸出能夠反映文本關鍵信息的關鍵詞。

   文本摘要:許多文本挖掘應用程序需要總結文本文檔,以便對大型文檔或某一主題的文檔集合做出簡要概述。

   文本聚類:主要是對未標註的文本進行標註,常見的有 K均值聚類和層次聚類。

   文本分類:文本分類使用監督學習的方法,以對未知數據的分類進行預測的機器學習方法。

   文本主題模型 LDA:LDA(Latent Dirichlet Allocation)是一種文檔主題生成模型,也稱為一個三層貝恭弘=叶 恭弘斯概率模型,包含詞、主題和文檔三層結構,該模型可以用於獲取語料的主題提取和對不同類別的文檔進行分類。

   觀點抽取:對文本(主要針對評論)進行分析,抽取出核心觀點,並判斷極性(正負面),主要用於電商、美食、酒店、汽車等評論進行分析。

   情感分析:對文本進行情感傾向判斷,將文本情感分為正向、負向、中性。用於口碑分析、話題監控、輿情分析。

   因為自己的論文寫的是關於情感分析方面的內容,因此打算接下來主要寫情感分析系列的內容,今天主要寫關於情感分析的介紹以及發展史。

二、情感分析

1. 含義

     情感分析主要是通過分析人們對於服務、產品、事件、話題來挖掘出說話人/作者觀點、情感、情緒等的研究。情感分析按照研究內容的不同,可以分為:意見挖掘 / 意見提取 / 主觀性分析 / 情感傾向分析、情感情緒分析、情感打分等。情感傾向問題,即是指挖掘出一段語料中說話人/作者對於某一話題/事件所持有的態度,如褒義、貶義、中性、兩者兼有。情感情緒,則是將情感傾向進行更進一步的細化,依據“大連理工大學的情感詞彙本體庫”可知,可以將情感傾向可以細化為:“喜歡”、“憤怒”、“討厭”等具體的7個大類——21個小類別。情感打分,即根據情感態度對於某一事物進行評分,如淘寶系統的1-5分。 文本中的情感分析還可以分為:顯式情感、隱式情感,顯式情感是指包含明顯的情感詞語(如:高興、漂亮、討厭等),隱式情感則是指不包含情感詞語的情感文本,如:“這個杯子上面有一層灰”。由於隱式情感分析難度較大,因此目前的工作多集中在顯式情感分析領域。

     情感分析按照不同的分析對象,可以分為:文章級別的情感分析、句子級別的情感分析、詞彙級別的情感分析。按照不同的研究內容以及研究的粒度的不同,其研究情感分析的方法也有很大的變化。

     目前的情感分析研究可歸納為:情感資源構建、情感元素抽取、情感分類及情感分析應用系統;

     情感資源構建:情感資源一般來說有:情感詞典、情感語料庫。情感詞典的構建即是將現有的、整理好的情感詞典資源進行整合,比如中文情感詞典有:大連理工大學的情感詞彙本體庫、知網Hownet情感詞典、台灣大學的NTUSD簡體中文情感詞典等,根據不同的需求,應用這些情感詞典。情感語料庫,則是我們要分析的文本,如關於新聞的文本、微博評論文本、商品評論文本、電影評論文本等,這些語料的獲取可以是尋找已經整理好的數據,或者自己爬蟲獲取。推薦一個比較全的中文語料庫網站:中文NLP語料庫。

    情感元素抽取:情感元素抽取則是從語料中抽取出來能夠代表說話人/作者情感態度問題的詞彙,也稱為細粒度情感分析。語料中的評價對象和表達抽取是情感元素抽取的核心內容。評價對象是指語料中被討論的主題,比如對於商品評論來說,用戶常提起的“外觀”、“快遞”、“包裝”等方面;表達抽取主要針對顯式情感表達的文本,是指文本抽取出來能夠代表說話人/作者情感、情緒、意見等的詞彙,比如“漂亮”、“贊同”、“不贊同”等。一般來說,評價對象和表達抽取也可以作為相互獨立的兩個任務。一般來說,分析這兩者的方法有:基於規則、基於機器學習。對於評價對象來說,現如今使用最多的方法是利用主題模型中的LDA(Latent Dirichlet Allocation)模型進行分析;對於表達抽取則有:深度學習的方法、基於JST (Joint Sentiment/Topic )模型的方法等。

     情感分類:情感分類則是將文本分為一個具體的類別,比如情感傾向分析,則是將文檔分為:褒義、貶義、中性等。一般來說,進行情感分類的方法有,基於情感詞典、基於機器學習。基於情感詞典,最典型的方法則是基於知網Hownet情感詞典的So-Hownet指標進行情感分類,基於機器學習的方法則有監督學習方法、半監督學習方法等。

    針對於情感分析,現已經存在一些專有平台,如:基於Boson 數據的情感分析平台,基於產品評論的平台Google Shopping。情感分析除了在電商平台應用廣泛之外,情感分析技術還被引入到對話機器人領域。例如,微軟的“小冰”機器人 可以通過分析用戶的文本輸入和表情貼圖,理解用戶當前的情緒狀況,並據此回復文本或者語音等情感回應。部分研究機構還將情感分析技術融入實體機器人中。

 2. 發展史

     V. H. 和 K. R. McKeown 於 1997 年發表的論文 [1],該論文使用對數線性回歸模型從大量語料庫中識別形容詞的正面或負面語義,同時藉助該模型對語料中出現的形容詞進行分類預測。

     Peter Turney在 2002年在論文 [2] 提出了一種無監督學習的算法,其可以很好的將語料中的詞語分類成正面情感詞和負面情感詞。

    2002 年 Bo Pang 等人在論文 [3] 中使用了傳統的機器學習方法對電影評論數據進行分類,同時也驗證了機器學習的方法的確要比之前基於規則的方法要優。

    2003 年 Blei 等人在論文 [4] 中提出了 LDA(Latent Dirichlet Allocation)模型,在之後的情感分析領域的工作中,很多學者/研究人員都使用主題模型來進行情感分析,當然也不只是基於主題模型來進行情感分析研究,還有很多利用深度學習方法來進行情感分析的研究。

   Lin 和 He在 2009 年的論文 [5] 提出了一種基於主題模型的模型 —JST(Joint Sentiment/Topic),其有效的將情感加入到了經典的主題模型當中,因此利用該模型可以獲取到不同情感極性標籤下不同主題的分佈情況。傳統的主題模型獲取文檔的主題以及詞的分佈情況,但並沒有關注到情感的存在,因此基於該模型可以對文檔的情感傾向進行分析。利用 JST 模型可以有效的直接將語料的主題、情感信息挖掘出來,同時 JST 模型還考慮到了主題、文檔、情感、詞之間的聯繫。

    基於對於語義和句法的考慮,Jo 和 H.OH 在 2011 年提出了 ASUM(Aspect and Sentiment Unification Model)模型,該模型和 JST 模型很相似都是四層的貝恭弘=叶 恭弘斯網絡結構 [6] 。

    基於神經網絡的語義組合算法被驗證是一種非常有效的特徵學習手段,2013年,Richard Socher和Christopher Potts等人提出多個基於樹結構的Recursive Neural Network,該方法通過迭代運算的方式學習變量長度的句子或短語的語義表示,在斯坦福情感分析樹庫(Stanford Sentiment Treebank)上驗證了該方法的有效性 [7]。Nal Kalchbrenner等人描述了一個卷積體繫結構,稱為動態卷積神經網絡(DCNN),他們採用它來進行句子的語義建模。 該網絡使用動態k-Max池,這是一種線性序列的全局池操作。 該網絡處理不同長度的輸入句子,並在句子上引入能夠明確捕獲短程和長程關係的特徵圖。 網絡不依賴於解析樹,並且很容易適用於任何語言。該模型在句子級情感分類任務上取得了非常出色的效果[8]。2015年,Kai Sheng Tai,Richard Socher, Christopher D. Manning在序列化的LSTM (Long Short-Term Memory)模型的基礎上加入了句法結構的因素,該方法在句法分析的結果上進行語義組合,在句子級情感分類和文本蘊含任務(text entailment)上都取得了很好的效果[9]。

   2016年,Qiao Qian, Xiaoyan Zhu等人在LSTM和Bi-LSTM模型的基礎上加入四種規則約束,這四種規則分別是: Non-Sentiment Regularizer,Sentiment Regularizer, Negation Regularizer, Intensity Regularizer,利用語言資源和神經網絡相結合來提升情感分類問題的精度。

  除了上面的一些研究,關於情感分析領域的應用仍然有很多,比如:2015 年鄭祥雲等人通過主題模型提取出來圖書館用戶的主題信息,最後利用這些信息來進行個性化圖書的有效推薦。將 JST 模型中直接引入了情感詞典作為外部先驗知識來對新聞文本進行分析,獲取其中的主旨句,並對主旨句進行情感打分,同時利用情感主旨句來代替全文,這樣能夠使得用戶更有效、更快速的閱讀文章。

  總的來說,情感分析在很多的領域被應用,當然情感分析也有很多的局限性,就是過多的依賴於語料庫信息,同時還需要使用自然語言、人工智能的方法來能夠最大化的挖掘出來其中的信息。

 

 參考文獻:

 [1] :Predicting the semantic orientation of adjectives

 [2]:  Thumbs up or thumbs down? Semantic orientation applied to unsupervised classification of reviews 

 [3] : Thumbs up? Sentiment Classification using Machine Learning Techniques

 [4] :   Latent Dirichlet Allocation

 [5] : Joint sentiment/topic model for sentiment analysis

 [6] : Aspect and sentiment unification model for online review analysis

 [7] : Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank

 [8]:  A Convolutional Neural Network for Modelling Sentences

 [9]: Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks

 [10] : Linguistically Regularized LSTMs for Sentiment Classification

 

 

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

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

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

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

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

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

※超省錢租車方案

聚甘新

環境篇:Kylin3.0.1集成CDH6.2.0

環境篇:Kylin3.0.1集成CDH6.2.0

Kylin是什麼?

Apache Kylin™是一個開源的、分佈式的分析型數據倉庫,提供Hadoop/Spark 之上的 SQL 查詢接口及多維分析(OLAP)能力以支持超大規模數據,最初由 eBay 開發並貢獻至開源社區。它能在亞秒內查詢巨大的表。

Apache Kylin™ 令使用者僅需三步,即可實現超大數據集上的亞秒級查詢。

  1. 定義數據集上的一個星形或雪花形模型
  2. 在定義的數據表上構建cube
  3. 使用標準 SQL 通過 ODBC、JDBC 或 RESTFUL API 進行查詢,僅需亞秒級響應時間即可獲得查詢結果

如果沒有Kylin

大數據在數據積累后,需要計算,而數據越多,算力越差,內存需求也越高,詢時間與數據量成線性增長,而這些對於Kylin影響不大,大數據中硬盤往往比內存要更便宜,Kylin通過與計算的形式,以空間換時間,亞秒級的響應讓人們愛不釋手。

注:所謂詢時間與數據量成線性增長:假設查詢 1 億條記錄耗時 1 分鐘,那麼查詢 10 億條記錄就需 10分鐘,100 億條記錄就至少需要 1 小時 40 分鐘。

http://kylin.apache.org/cn/

1 Kylin架構

Kylin 提供與多種數據可視化工具的整合能力,如 Tableau,PowerBI 等,令用戶可以使用 BI 工具對 Hadoop 數據進行分析

  1. REST Server REST Server

是一套面嚮應用程序開發的入口點,旨在實現針對 Kylin 平台的應用開發 工作。 此類應用程序可以提供查詢、獲取結果、觸發 cube 構建任務、獲取元數據以及獲取 用戶權限等等。另外可以通過 Restful 接口實現 SQL 查詢。

  1. 查詢引擎(Query Engine)

當 cube 準備就緒后,查詢引擎就能夠獲取並解析用戶查詢。它隨後會與系統中的其它 組件進行交互,從而向用戶返回對應的結果。

  1. 路由器(Routing)

在最初設計時曾考慮過將 Kylin 不能執行的查詢引導去 Hive 中繼續執行,但在實踐后 發現 Hive 與 Kylin 的速度差異過大,導致用戶無法對查詢的速度有一致的期望,很可能大 多數查詢幾秒內就返回結果了,而有些查詢則要等幾分鐘到幾十分鐘,因此體驗非常糟糕。 最後這個路由功能在發行版中默認關閉。

  1. 元數據管理工具(Metadata)

Kylin 是一款元數據驅動型應用程序。元數據管理工具是一大關鍵性組件,用於對保存 在 Kylin 當中的所有元數據進行管理,其中包括最為重要的 cube 元數據。其它全部組件的 正常運作都需以元數據管理工具為基礎。 Kylin 的元數據存儲在 hbase 中。

  1. 任務引擎(Cube Build Engine)

這套引擎的設計目的在於處理所有離線任務,其中包括 shell 腳本、Java API 以及 MapReduce 任務等等。任務引擎對 Kylin 當中的全部任務加以管理與協調,從而確保每一項任務 都能得到切實執行並解決其間出現的故障。

2 Kylin軟硬件要求

  • 軟件要求
    • Hadoop: 2.7+, 3.1+ (since v2.5)
    • Hive: 0.13 – 1.2.1+
    • HBase: 1.1+, 2.0 (since v2.5)
    • Spark (optional) 2.3.0+
    • Kafka (optional) 1.0.0+ (since v2.5)
    • JDK: 1.8+ (since v2.5)
    • OS: Linux only, CentOS 6.5+ or Ubuntu 16.0.4+
  • 硬件要求
    • 最低配置:4 core CPU, 16 GB memory
    • 高負載場景:24 core CPU, 64 GB memory

3 Kylin單機安裝

3.1 修改環境變量

vim /etc/profile 
#>>>注意地址指定為自己的
#kylin
export KYLIN_HOME=/usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60
export PATH=$PATH:$KYLIN_HOME/bin
    
#cdh
export CDH_HOME=/opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373

#hadoop
export HADOOP_HOME=${CDH_HOME}/lib/hadoop
export HADOOP_DIR=${HADOOP_HOME}
export HADOOP_CLASSPATH=${HADOOP_HOME}
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
    
#hbase
export HBASE_HOME=${CDH_HOME}/lib/hbase
export PATH=$PATH:$HBASE_HOME/bin
    
 #hive
export HIVE_HOME=${CDH_HOME}/lib/hive
export PATH=$PATH:$HIVE_HOME/bin
    
#spark
export SPARK_HOME=${CDH_HOME}/lib/spark
export PATH=$PATH:$SPARK_HOME/bin   

#kafka
export KAFKA_HOME=${CDH_HOME}/lib/kafka
export PATH=$PATH:$KAFKA_HOME/bin 
#<<<

source /etc/profile 

3.2 修改hdfs用戶權限

usermod -s /bin/bash hdfs
su hdfs
hdfs dfs -mkdir /kylin
hdfs dfs -chmod a+rwx /kylin
su

3.3 上傳安裝包解壓

mkdir /usr/local/src/kylin
cd /usr/local/src/kylin
tar -zxvf apache-kylin-3.0.1-bin-cdh60.tar.gz
cd /usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60

3.4 Java兼容hbase

  • hbase 所有節點

在CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar后添加

>>---
:/opt/cloudera/parcels/CDH/lib/hbase/lib/*
<<---
  • Kylin節點添加jar包
cp /opt/cloudera/cm/common_jars/commons-configuration-1.9.cf57559743f64f0b3a504aba449c9649.jar /usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60/tomcat/lib

這2步不做會引起 Could not find or load main class org.apache.hadoop.hbase.util.GetJavaProperty

3.5 啟動停止

./bin/kylin.sh start
#停止  ./bin/kylin.sh stop

3.6 web頁面

訪問端口7070

賬號密碼:ADMIN / KYLIN

4 Kylin集群安裝

4.1 修改環境變量

vim /etc/profile 
#>>>注意地址指定為自己的
#kylin
export KYLIN_HOME=/usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60
export PATH=$PATH:$KYLIN_HOME/bin
    
#cdh
export CDH_HOME=/opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373

#hadoop
export HADOOP_HOME=${CDH_HOME}/lib/hadoop
export HADOOP_DIR=${HADOOP_HOME}
export HADOOP_CLASSPATH=${HADOOP_HOME}
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
    
#hbase
export HBASE_HOME=${CDH_HOME}/lib/hbase
export PATH=$PATH:$HBASE_HOME/bin
    
 #hive
export HIVE_HOME=${CDH_HOME}/lib/hive
export PATH=$PATH:$HIVE_HOME/bin
    
#spark
export SPARK_HOME=${CDH_HOME}/lib/spark
export PATH=$PATH:$SPARK_HOME/bin   

#kafka
export KAFKA_HOME=${CDH_HOME}/lib/kafka
export PATH=$PATH:$KAFKA_HOME/bin 
#<<<

source /etc/profile 

4.2 修改hdfs用戶權限

usermod -s /bin/bash hdfs
su hdfs
hdfs dfs -mkdir /kylin
hdfs dfs -chmod a+rwx /kylin
su

4.3 上傳安裝包解壓

mkdir /usr/local/src/kylin
cd /usr/local/src/kylin
tar -zxvf apache-kylin-3.0.1-bin-cdh60.tar.gz
cd /usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60

4.4 Java兼容hbase

  • hbase 所有節點

在CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar后添加

vim /opt/cloudera/parcels/CDH/lib/hbase/bin/hbase
>>---
:/opt/cloudera/parcels/CDH/lib/hbase/lib/*
<<---
  • Kylin節點添加jar包
cp /opt/cloudera/cm/common_jars/commons-configuration-1.9.cf57559743f64f0b3a504aba449c9649.jar /usr/local/src/kylin/apache-kylin-3.0.1-bin-cdh60/tomcat/lib

這2步不做會引起 Could not find or load main class org.apache.hadoop.hbase.util.GetJavaProperty

4.5 修改kylin配置文件

Kylin根據自己的運行職責狀態,可以劃分為以下三大類角色

  • Job節點:僅用於任務調度,不用於查詢
  • Query節點:僅用於查詢,不用於構建任務的調度
  • All節點:模式代表該服務同時用於任務調度和 SQL 查詢
    • 2.0以前同一個集群只能有一個節點(Kylin實例)用於job調度(all或者job模式的只能有一個實例)
    • 2.0開始可以多個job或者all節點實現HA
vim conf/kylin.properties
>>----
#指定元數據庫路徑,默認值為 kylin_metadata@hbase,確保kylin集群使用一致
kylin.metadata.url=kylin_metadata@hbase
#指定 Kylin 服務所用的 HDFS 路徑,默認值為 /kylin,請確保啟動 Kylin 實例的用戶有讀寫該目錄的權限
kylin.env.hdfs-working-dir=/kylin
kylin.server.mode=all
kylin.server.cluster-servers=cdh01.cm:7070,cdh02.cm:7070,cdh03.cm:7070
kylin.storage.url=hbase
#構建任務失敗后的重試次數,默認值為 0
kylin.job.retry=2
#最大構建併發數,默認值為 10
kylin.job.max-concurrent-jobs=10
#構建引擎間隔多久檢查 Hadoop 任務的狀態,默認值為 10(s)
kylin.engine.mr.yarn-check-interval-seconds=10
#MapReduce 任務啟動前會依據輸入預估 Reducer 接收數據的總量,再除以該參數得出 Reducer 的數目,默認值為 500(MB)
kylin.engine.mr.reduce-input-mb=500
#MapReduce 任務中 Reducer 數目的最大值,默認值為 500
kylin.engine.mr.max-reducer-number=500
#每個 Mapper 可以處理的行數,默認值為 1000000,如果將這個值調小,會起更多的 Mapper
kylin.engine.mr.mapper-input-rows=1000000
#啟用分佈式任務鎖
kylin.job.scheduler.default=2
kylin.job.lock=org.apache.kylin.storage.hbase.util.ZookeeperJobLock
<<----

4.6 啟動停止

所有Kylin節點

./bin/kylin.sh start
#停止  ./bin/kylin.sh stop

4.7 nginx負載均衡

yum -y install nginx

vim /etc/nginx/nginx.conf
>>---http中添加替換內容
upstream kylin {
        least_conn;
        server 192.168.37.10:7070 weight=8;
        server 192.168.37.11:7070 weight=7;
        server 192.168.37.12:7070 weight=7;
	}
    server {
        listen       9090;
        server_name  localhost;

        location / {
                proxy_pass http://kylin;
        }
    }

<<---

#重啟 nginx 服務
systemctl restart nginx  

4.8 訪問web頁面

訪問任何節點的7070端口都可以進入kylin

訪問nginx所在機器9090端口/kylin負載均衡進入kylin

賬號密碼:ADMIN / KYLIN

4 大規模并行處理@列式存儲

自從 10 年前 Hadoop 誕生以來,大數據的存儲和批處理問題均得到了妥善解決,而如何高速地分析數據也就成為了下一個挑戰。於是各式各樣的“SQL on Hadoop”技術應運而生,其中以 Hive 為代表,Impala、Presto、Phoenix、Drill、 SparkSQL 等緊隨其後(何以解憂–唯有CV SQL BOY)。它們的主要技術是“大規模并行處理”(Massive Parallel Processing,MPP)和“列式存儲”(Columnar Storage)

大規模并行處理可以調動多台機器一起進行并行計算,用線性增加的資源來換取計算時間的線性下降

列式存儲則將記錄按列存放,這樣做不僅可以在訪問時只讀取需要的列,還可以利用存儲設備擅長連續讀取的特點,大大提高讀取的速率。

這兩項關鍵技術使得 Hadoop 上的 SQL 查詢速度從小時提高到了分鐘。 然而分鐘級別的查詢響應仍然離交互式分析的現實需求還很遠。分析師敲入 查詢指令,按下回車,還需要去倒杯咖啡,靜靜地等待查詢結果。得到結果之後才能根據情況調整查詢,再做下一輪分析。如此反覆,一個具體的場景分析常常需要幾小時甚至幾天才能完成,效率低下。 這是因為大規模并行處理和列式存儲雖然提高了計算和存儲的速度,但並沒有改變查詢問題本身的時間複雜度,也沒有改變查詢時間與數據量成線性增長的關係這一事實。

假設查詢 1 億條記錄耗時 1 分鐘,那麼查詢 10 億條記錄就需 10分鐘,100 億條記錄就至少需要 1 小時 40 分鐘。 當然,可以用很多的優化技術縮短查詢的時間,比如更快的存儲、更高效的壓縮算法,等等,但總體來說,查詢性能與數據量呈線性相關這一點是無法改變的。雖然大規模并行處理允許十倍或百倍地擴張計算集群,以期望保持分鐘級別的查詢速度,但購買和部署十倍或百倍的計算集群又怎能輕易做到,更何況還有 高昂的硬件運維成本。 另外,對於分析師來說,完備的、經過驗證的數據模型比分析性能更加重要, 直接訪問紛繁複雜的原始數據並進行相關分析其實並不是很友好的體驗,特別是在超大規模的數據集上,分析師將更多的精力花在了等待查詢結果上,而不是在更加重要的建立領域模型上

5 Kylin如何解決海量數據的查詢問題

**Apache Kylin 的初衷就是要解決千億條、萬億條記錄的秒級查詢問題,其中的關鍵就是要打破查詢時間隨着數據量成線性增長的這個規律。根據OLAP分析,可以注意到兩個結論: **

  • 大數據查詢要的一般是統計結果,是多條記錄經過聚合函數計算后的統計值。原始的記錄則不是必需的,或者訪問頻率和概率都極低。

  • 聚合是按維度進行的,由於業務範圍和分析需求是有限的,有意義的維度聚合組合也是相對有限的,一般不會隨着數據的膨脹而增長。

**基於以上兩點,我們可以得到一個新的思路——“預計算”。應盡量多地預先計算聚合結果,在查詢時刻應盡量使用預算的結果得出查詢結果,從而避免直 接掃描可能無限增長的原始記錄。 **

舉例來說,使用如下的 SQL 來查詢 11月 11日 那天銷量最高的商品:

select item,sum(sell_amount)
from sell_details
where sell_date='2020-11-11'
group by item
order by sum(sell_amount) desc

用傳統的方法時需要掃描所有的記錄,再找到 11月 11日 的銷售記錄,然後按商品聚合銷售額,最後排序返回。

假如 11月 11日 有 1 億條交易,那麼查詢必須讀取並累計至少 1 億條記錄,且這個查詢速度會隨將來銷量的增加而逐步下降。如果日交易量提高一倍到 2 億,那麼查詢執行的時間可能也會增加一倍。

而使用預 計算的方法則會事先按維度 [sell_date , item] 計 算 sum(sell_amount)並存儲下來,在查詢時找到 11月 11日 的銷售商品就可以直接排序返回了。讀取的記錄數最大不會超過維度[sell_date,item]的組合數。

顯然這個数字將遠遠小於實際的銷售記錄,比如 11月 11日 的 1 億條交易包含了 100萬條商品,那麼預計算后就只有 100 萬條記錄了,是原來的百分之一。並且這些 記錄已經是按商品聚合的結果,因此又省去了運行時的聚合運算。從未來的發展來看,查詢速度只會隨日期和商品數目(時間,商品維度)的增長而變化,與銷售記錄的總數不再有直接聯繫。假如日交易量提高一倍到 2 億,但只要商品的總數不變,那麼預計算的結果記錄總數就不會變,查詢的速度也不會變。

預計算就是 Kylin 在“大規模并行處理”和“列式存儲”之外,提供給大數據分析的第三個關鍵技術。

6 Kylin 入門案例

6.1 hive數據準備

--創建數據庫kylin_hive
create database kylin_hive; 

--創建表部門表dept
create external table if not exists kylin_hive.dept(
deptno int,
dname string,
loc int )
row format delimited fields terminated by '\t';
--添加數據
INSERT INTO TABLE kylin_hive.dept VALUES(10,"ACCOUNTING",1700),(20,"RESEARCH",1800),(30,"SALES",1900),(40,"OPERATIONS",1700)
--查看數據
SELECT * FROM kylin_hive.dept

--創建員工表emp
create external table if not exists kylin_hive.emp(
empno int,
ename string,
job string,
mgr int,
hiredate string, 
sal double, 
comm double,
deptno int)
row format delimited fields terminated by '\t';

--添加數據
INSERT INTO TABLE kylin_hive.emp VALUES(7369,"SMITHC","LERK",7902,"1980-12-17",800.00,0.00,20),(7499,"ALLENS","ALESMAN",7698,"1981-2-20",1600.00,300.00,30),(7521,"WARDSA","LESMAN",7698,"1981-2-22",1250.00,500.00,30),(7566,"JONESM","ANAGER",7839,"1981-4-2",2975.00,0.00,20),(7654,"MARTIN","SALESMAN",7698,"1981-9-28",1250.00,1400.00,30),(7698,"BLAKEM","ANAGER",7839,"1981-5-1",2850.00,0.00,30),(7782,"CLARKM","ANAGER",7839,"1981-6-9",2450.00,0.00,10),(7788,"SCOTTA","NALYST",7566,"1987-4-19",3000.00,0.00,20),(7839,"KINGPR","ESIDENT",7533,"1981-11-17",5000.00,0.00,10),(7844,"TURNER","SALESMAN",7698,"1981-9-8",1500.00,0.00,30),(7876,"ADAMSC","LERK",7788,"1987-5-23",1100.00,0.00,20),(7900,"JAMESC","LERK",7698,"1981-12-3",950.00,0.00,30),(7902,"FORDAN","ALYST",7566,"1981-12-3",3000.00,0.00,20),(7934,"MILLER","CLERK",7782,"1982-1-23",1300.00,0.00,10)
--查看數據
SELECT * FROM kylin_hive.emp

6.2 創建工程

  • 輸入工程名稱以及工程描述

6.3 Kylin加載Hive表

雖然 Kylin 使用 SQL 作為查詢接口並利用 Hive 元數據,Kylin 不會讓用戶查詢所有的 hive 表,因為到目前為止它是一個預構建 OLAP(MOLAP) 系統。為了使表在 Kylin 中可用,使用 “Sync” 方法能夠方便地從 Hive 中同步表。

  • 選擇項目添加hive數據源
  • 添加數據源表–>hive庫名稱.表名稱(以逗號分隔)

  • 這裏只添加了表的Schema元信息,如果需要加載數據,還需要點擊Reload Table

6.4 Kylin添加Models(模型)

  • 填寫模型名字
  • 選擇事實表,這裏選擇員工EMP表為事實表
  • 添加維度表,這裏選擇部門DEPT表為維度表,並選擇我們的join方式,以及join連接字段

  • 選擇聚合維度信息
  • 選擇度量信息
  • 添加分區信息及過濾條件之後“Save”

6.5 Kylin構建Cube

Kylin 的 OLAP Cube 是從星型模式的 Hive 表中獲取的預計算數據集,這是供用戶探索、管理所有 cube 的網頁管理頁面。由菜單欄進入Model 頁面,系統中所有可用的 cube 將被列出。

  • 創建一個new cube
  • 選擇我們的model以及指定cube name
  • 添加我們的自定義維度,這裡是在創建Models模型時指定的事實表和維度表中取
    • LookUpTable可選擇normal或derived(一般列、衍生列)
    • normal緯度作為普通獨立的緯度,而derived 維度不會計算入cube,將由事實表的外鍵推算出

  • 添加統計維度,勾選相應列作為度量,kylin提供8種度量:SUM、MAX、MIN、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE
    • DISTINCT_COUNT有兩個實現:
      1. 近似實現 HyperLogLog,選擇可接受的錯誤率,低錯誤率需要更多存儲;
      2. 精確實現 bitmap
    • TopN 度量在每個維度結合時預計算,需要兩個參數:
      1. 一是被用來作為 Top 記錄的度量列,Kylin 將計算它的 SUM 值並做倒序排列,如sum(price)
      2. 二是 literal ID,代表最 Top 的記錄,如seller_id
    • EXTENDED_COLUMN
      • Extended_Column 作為度量比作為維度更節省空間。一列和零一列可以生成新的列
    • PERCENTILE
      • Percentile 代表了百分比。值越大,錯誤就越少。100為最合適的值

  • 設置多個分區cube合併信息

如果是分區統計,需要關於歷史cube的合併,

這裡是全量統計,不涉及多個分區cube進行合併,所以不用設置歷史多個cube進行合併

  • Auto Merge Thresholds:

    • 自動合併小的 segments 到中等甚至更大的 segment。如果不想自動合併,刪除默認2個選項
  • Volatile Range:

    • 默認為0,會自動合併所有可能的cube segments,或者用 ‘Auto Merge’ 將不會合併最新的 [Volatile Range] 天的 cube segments
  • Retention Threshold:

    • 默認為0,只會保存 cube 過去幾天的 segment,舊的 segment 將會自動從頭部刪除
  • Partition Start Date:

    • cube 的開始日期
  • 高級設置

暫時也不做任何設

置高級設定關係到立方體是否足夠優化,可根據實際情況將維度列定義為強制維度、層級維度、聯合維度

  • Mandatory維度指的是總會存在於group by或where中的維度
  • Hierarchy是一組有層級關係的維度,如國家、省份、城市
  • Joint是將多個維度組合成一個維度

  • 額外的其他的配置屬性

這裏也暫時不做配置

Kylin 允許在 Cube 級別覆蓋部分 kylin.properties 中的配置

  • 完成保存配置

通過Planner計劃者,可以看到4個維度,得到Cuboid Conut=15,為2的4次方-1,因為全部沒有的指標不會使用,所以結果等於15。

  • 構建Cube

6.6 數據查詢

  • 根據部門查詢,部門工資總和
SELECT  DEPT.DNAME,SUM(EMP.SAL) 
FROM EMP 
LEFT JOIN DEPT 
ON DEPT.DEPTNO = EMP.DEPTNO  
GROUP BY DEPT.DNAME

7 入門案例構建流程

  • 動畫演示

8 Kylin的工作原理

就是對數據模型做 Cube 預計算,並利用計算的結果加速查詢,具體工作過程如下:

  1. 指定數據模型,定義維度和度量。

  2. 預計算 Cube,計算所有 Cuboid 並保存為物化視圖。

  3. 執行查詢時,讀取 Cuboid,運算,產生查詢結果。

由於 Kylin 的查詢過程不會掃描原始記錄,而是通過預計算預先完成表的關聯、聚合等複雜運算,並利用預計算的結果來執行查詢,因此相比非預計算的查詢技術,其速度一般要快一到兩個數量級,並且這點在超大的數據集上優勢更明顯。當數據集達到千億乃至萬億級別時,Kylin 的速度甚至可以超越其他非預計算技術 1000 倍以上。

9 Cube 和 Cuboid

Cube(或 Data Cube),即數據立方體,是一種常用於數據分析與索引的技術;它可以對原始數據建立多維度索引。通過 Cube 對數據進行分析,可以大大加快數據的查詢效率。

Cuboid 特指在某一種維度組合下所計算的數據。 給定一個數據模型,我們可以對其上的所有維度進行組合。對於 N 個維度來說,組合的所有可能性共有 2 的 N 次方種。對於每一種維度的組合,將度量做 聚合運算,然後將運算的結果保存為一個物化視圖,稱為 Cuboid。

所有維度組合的 Cuboid 作為一個整體,被稱為 Cube。所以簡單來說,一個 Cube 就是許多按維度聚合的物化視圖的集合。

下面來列舉一個具體的例子:

假定有一個電商的銷售數據集,其中維度包括 時間(Time)、商品(Item)、地點(Location)和供應商(Supplier),度量為銷售額(GMV)。

  • 那麼所有維度的組合就有 2 的 4 次方 =16 種
    • 一維度(1D) 的組合有[Time]、[Item]、[Location]、[Supplier]4 種
    • 二維度(2D)的組合 有[Time,Item]、[Time,Location]、[Time、Supplier]、[Item,Location]、 [Item,Supplier]、[Location,Supplier]6 種
    • 三維度(3D)的組合也有 4 種
    • 零維度(0D)的組合有 1 種
    • 四維度(4D)的組合有 1 種

10 cube構建算法

10.1 逐層構建算法

我們知道,一個N維的Cube,是由1個N維子立方體、N個(N-1)維子立方體、N*(N-1)/2個(N-2)維子立方體、……、N個1維子立方體和1個0維子立方體構成,總共有2^N個子立方體組成。

在逐層算法中,按維度數逐層減少來計算,每個層級的計算(除了第一層,它是從原始數據聚合而來),是基於它上一層級的結果來計算的。比如,[Group by A, B]的結果,可以基於[Group by A, B, C]的結果,通過去掉C后聚合得來的;這樣可以減少重複計算;當 0維度Cuboid計算出來的時候,整個Cube的計算也就完成了。

每一輪的計算都是一個MapReduce任務,且串行執行;一個N維的Cube,至少需要N次MapReduce Job。

算法優點:

  1. 此算法充分利用了MapReduce的優點,處理了中間複雜的排序和shuffle工作,故而算法代碼清晰簡單,易於維護;

  2. 受益於Hadoop的日趨成熟,此算法非常穩定,即便是集群資源緊張時,也能保證最終能夠完成。

算法缺點:

  1. 當Cube有比較多維度的時候,所需要的MapReduce任務也相應增加;由於Hadoop的任務調度需要耗費額外資源,特別是集群較龐大的時候,反覆遞交任務造成的額外開銷會相當可觀;

  2. 由於Mapper邏輯中並未進行聚合操作,所以每輪MR的shuffle工作量都很大,導致效率低下。

  3. 對HDFS的讀寫操作較多:由於每一層計算的輸出會用做下一層計算的輸入,這些Key-Value需要寫到HDFS上;當所有計算都完成后,Kylin還需要額外的一輪任務將這些文件轉成HBase的HFile格式,以導入到HBase中去;

總體而言,該算法的效率較低,尤其是當Cube維度數較大的時候。

10.2 快速構建算法

也被稱作“逐段”(By Segment) 或“逐塊”(By Split) 算法,從1.5.x開始引入該算法,該算法的主要思想是,每個Mapper將其所分配到的數據塊,計算成一個完整的小Cube 段(包含所有Cuboid)。每個Mapper將計算完的Cube段輸出給Reducer做合併,生成大Cube,也就是最終結果。如圖所示解釋了此流程。

與舊的逐層構建算法相比,快速算法主要有兩點不同:

  1. Mapper會利用內存做預聚合,算出所有組合;Mapper輸出的每個Key都是不同的,這樣會減少輸出到Hadoop MapReduce的數據量,Combiner也不再需要;

  2. 一輪MapReduce便會完成所有層次的計算,減少Hadoop任務的調配。

11 備份及恢復

Kylin將它全部的元數據(包括cube描述和實例、項目、倒排索引描述和實例、任務、表和字典)組織成層級文件系統的形式。然而,Kylin使用hbase來存儲元數據,而不是一個普通的文件系統。如果你查看過Kylin的配置文件(kylin.properties),你會發現這樣一行:

## The metadata store in hbase
kylin.metadata.url=kylin_metadata@hbase

這表明元數據會被保存在一個叫作“kylin_metadata”的htable里。你可以在hbase shell里scan該htbale來獲取它。

11.1 使用二進制包來備份Metadata Store

有時你需要將Kylin的Metadata Store從hbase備份到磁盤文件系統。在這種情況下,假設你在部署Kylin的hadoop命令行(或沙盒)里,你可以到KYLIN_HOME並運行:

./bin/metastore.sh backup

來將你的元數據導出到本地目錄,這個目錄在KYLIN_HOME/metadata_backps下,它的命名規則使用了當前時間作為參數:KYLIN_HOME/meta_backups/meta_year_month_day_hour_minute_second,如:meta_backups/meta_2020_06_18_19_37_49/

11.2 使用二進制包來恢復Metatdara Store

萬一你發現你的元數據被搞得一團糟,想要恢復先前的備份:

  1. 首先,重置Metatdara Store(這個會清理Kylin在hbase的Metadata Store的所有信息,請確保先備份):
./bin/metastore.sh reset
  1. 然後上傳備份的元數據到Kylin的Metadata Store:
./bin/metastore.sh restore $KYLIN_HOME/meta_backups/meta_xxxx_xx_xx_xx_xx_xx
  1. 等恢復操作完成,可以在“Web UI”的“System”頁面單擊“Reload Metadata”按鈕對元數據緩存進行刷新,即可看到最新的元數據

做完備份,刪除一些文件,然後進行恢複測試,完美恢復,叮叮叮!

12 kylin的垃圾清理

Kylin在構建cube期間會在HDFS上生成中間文件;除此之外,當清理/刪除/合併cube時,一些HBase表可能被遺留在HBase卻以後再也不會被查詢;雖然Kylin已經開始做自動化的垃圾回收,但不一定能覆蓋到所有的情況;你可以定期做離線的存儲清理:

  1. 檢查哪些資源可以清理,這一步不會刪除任何東西:
${KYLIN_HOME}/bin/kylin.sh org.apache.kylin.tool.StorageCleanupJob --delete false
  1. 你可以抽查一兩個資源來檢查它們是否已經沒有被引用了;然後加上“–delete true”選項進行清理。
${KYLIN_HOME}/bin/kylin.sh org.apache.kylin.tool.StorageCleanupJob --delete true

完成后,中間HDFS上的中間文件和HTable會被移除。

13 Kylin優化

13.1 維度優化

如果不進行任何維度優化,直接將所有的維度放在一個聚集組裡,Kylin就會計算所有的維度組合(cuboid)。

比如,有12個維度,Kylin就會計算2的12次方即4096個cuboid,實際上查詢可能用到的cuboid不到1000個,甚至更少。 如果對維度不進行優化,會造成集群計算和存儲資源的浪費,也會影響cube的build時間和查詢性能,所以我們需要進行cube的維度優化。

當你在保存cube時遇到下面的異常信息時,意味1個聚集組的維度組合數已經大於 4096 ,你就必須進行維度優化了。

或者發現cube的膨脹率過大。

但在現實情況中,用戶的維度數量一般遠遠大於4個。假設用戶有10 個維度,那麼沒有經過任何優化的Cube就會存在 2的10次方 = 1024個Cuboid;雖然每個Cuboid的大小存在很大的差異,但是單單想到Cuboid的數量就足以讓人想象到這樣的Cube對構建引擎、存儲引擎來說壓力有多麼巨大。因此,在構建維度數量較多的Cube時,尤其要注意Cube的剪枝優化(即減少Cuboid的生成)。

13.2 使用衍生維度

  • 衍生維度:維表中可以由主鍵推導出值的列可以作為衍⽣維度。

  • 使用場景:以星型模型接入時。例如用戶維表可以從userid推導出用戶的姓名,年齡,性別。

  • 優化效果:維度表的N個維度組合成的cuboid個數會從2的N次方降為2。

衍生維度用於在有效維度內將維度表上的非主鍵維度排除掉,並使用維度表的主鍵(其實是事實表上相應的外鍵)來替代它們。Kylin會在底層記錄維度表主鍵與維度表其他維度之間的映射關係,以便在查詢時能夠動態地將維度表的主鍵“翻譯”成這些非主鍵維度,並進行實時聚合。

雖然衍生維度具有非常大的吸引力,但這也並不是說所有維度表上的維度都得變成衍生維度,如果從維度表主鍵到某個維度表維度所需要的聚合工作量非常大,則不建議使用衍生維度。

13.3 使用聚合組(Aggregation group)

聚合組(Aggregation Group)是一種強大的剪枝工具。聚合組假設一個Cube的所有維度均可以根據業務需求劃分成若干組(當然也可以是一個組),由於同一個組內的維度更可能同時被同一個查詢用到,因此會表現出更加緊密的內在關聯。每個分組的維度集合均是Cube所有維度的一個子集,不同的分組各自擁有一套維度集合,它們可能與其他分組有相同的維度,也可能沒有相同的維度。每個分組各自獨立地根據自身的規則貢獻出一批需要被物化的Cuboid,所有分組貢獻的Cuboid的並集就成為了當前Cube中所有需要物化的Cuboid的集合。不同的分組有可能會貢獻出相同的Cuboid,構建引擎會察覺到這點,並且保證每一個Cuboid無論在多少個分組中出現,它都只會被物化一次。

對於每個分組內部的維度,用戶可以使用如下三種可選的方式定義,它們之間的關係,具體如下。

  1. 強制維度(Mandatory)

    • 強制維度:所有cuboid必須包含的維度,不會計算不包含強制維度的cuboid。

    • 適用場景:可以將確定在查詢時一定會使用的維度設為強制維度。例如,時間維度。

    • 優化效果:將一個維度設為強制維度,則cuboid個數直接減半。

如果一個維度被定義為強制維度,那麼這個分組產生的所有Cuboid中每一個Cuboid都會包含該維度。每個分組中都可以有0個、1個或多個強制維度。如果根據這個分組的業務邏輯,則相關的查詢一定會在過濾條件或分組條件中,因此可以在該分組中把該維度設置為強制維度。

  1. 層級維度(Hierarchy),

    • 層級維度:具有一定層次關係的維度。

    • 使用場景:像年,月,日;國家,省份,城市這類具有層次關係的維度。

    • 優化效果:將N個維度設置為層次維度,則這N個維度組合成的cuboid個數會從2的N次方減少到N+1。

每個層級包含兩個或更多個維度。假設一個層級中包含D1,D2…Dn這n個維度,那麼在該分組產生的任何Cuboid中, 這n個維度只會以(),(D1),(D1,D2)…(D1,D2…Dn)這n+1種形式中的一種出現。每個分組中可以有0個、1個或多個層級,不同的層級之間不應當有共享的維度。如果根據這個分組的業務邏輯,則多個維度直接存在層級關係,因此可以在該分組中把這些維度設置為層級維度。

  1. 聯合維度(Joint),

    • 聯合維度:將幾個維度視為一個維度。

    • 適用場景:

      1. 可以將確定在查詢時一定會同時使用的幾個維度設為一個聯合維度。
      2. 可以將基數很小的幾個維度設為一個聯合維度。
      3. 可以將查詢時很少使用的幾個維度設為一個聯合維度。
    • 優化效果:將N個維度設置為聯合維度,則這N個維度組合成的cuboid個數會從2的N次方減少到1。

每個聯合中包含兩個或更多個維度,如果某些列形成一個聯合,那麼在該分組產生的任何Cuboid中,這些聯合維度要麼一起出現,要麼都不出現。每個分組中可以有0個或多個聯合,但是不同的聯合之間不應當有共享的維度(否則它們可以合併成一個聯合)。如果根據這個分組的業務邏輯,多個維度在查詢中總是同時出現,則可以在該分組中把這些維度設置為聯合維度。

這些操作可以在Cube Designer的Advanced Setting中的Aggregation Groups區域完成,如下圖所示。

聚合組的設計非常靈活,甚至可以用來描述一些極端的設計。假設我們的業務需求非常單一,只需要某些特定的Cuboid,那麼可以創建多個聚合組,每個聚合組代表一個Cuboid。具體的方法是在聚合組中先包含某個Cuboid所需的所有維度,然後把這些維度都設置為強制維度。這樣當前的聚合組就只能產生我們想要的那一個Cuboid了。

再比如,有的時候我們的Cube中有一些基數非常大的維度,如果不做特殊處理,它就會和其他的維度進行各種組合,從而產生一大堆包含它的Cuboid。包含高基數維度的Cuboid在行數和體積上往往非常龐大,這會導致整個Cube的膨脹率變大。如果根據業務需求知道這個高基數的維度只會與若干個維度(而不是所有維度)同時被查詢到,那麼就可以通過聚合組對這個高基數維度做一定的“隔離”。我們把這個高基數的維度放入一個單獨的聚合組,再把所有可能會與這個高基數維度一起被查詢到的其他維度也放進來。這樣,這個高基數的維度就被“隔離”在一個聚合組中了,所有不會與它一起被查詢到的維度都沒有和它一起出現在任何一個分組中,因此也就不會有多餘的Cuboid產生。這點也大大減少了包含該高基數維度的Cuboid的數量,可以有效地控制Cube的膨脹率。

13.4 併發粒度優化

當Segment中某一個Cuboid的大小超出一定的閾值時,系統會將該Cuboid的數據分片到多個分區中,以實現Cuboid數據讀取的并行化,從而優化Cube的查詢速度。具體的實現方式如下:構建引擎根據Segment估計的大小,以及參數“kylin.hbase.region.cut”的設置決定Segment在存儲引擎中總共需要幾個分區來存儲,如果存儲引擎是HBase,那麼分區的數量就對應於HBase中的Region數量。kylin.hbase.region.cut的默認值是5.0,單位是GB,也就是說對於一個大小估計是50GB的Segment,構建引擎會給它分配10個分區。用戶還可以通過設置kylin.hbase.region.count.min(默認為1)和kylin.hbase.region.count.max(默認為500)兩個配置來決定每個Segment最少或最多被劃分成多少個分區。

由於每個Cube的併發粒度控制不盡相同,因此建議在Cube Designer 的Configuration Overwrites(上圖所示)中為每個Cube量身定製控制併發粒度的參數。假設將把當前Cube的kylin.hbase.region.count.min設置為2,kylin.hbase.region.count.max設置為100。這樣無論Segment的大小如何變化,它的分區數量最小都不會低於2,最大都不會超過100。相應地,這個Segment背後的存儲引擎(HBase)為了存儲這個Segment,也不會使用小於兩個或超過100個的分區。我們還調整了默認的kylin.hbase.region.cut,這樣50GB的Segment基本上會被分配到50個分區,相比默認設置,我們的Cuboid可能最多會獲得5倍的併發量。

13.5 Row Key優化

Kylin會把所有的維度按照順序組合成一個完整的Rowkey,並且按照這個Rowkey升序排列Cuboid中所有的行。

設計良好的Rowkey將更有效地完成數據的查詢過濾和定位,減少IO次數,提高查詢速度,維度在rowkey中的次序,對查詢性能有顯著的影響。

Row key的設計原則如下:

  1. 被用作where過濾的維度放在前邊。
  1. 基數大的維度放在基數小的維度前邊。

13.6 增量cube構建

構建全量cube,也可以實現增量cube的構建,就是通過分區表的分區時間字段來進行增量構建

  1. 更改model

  1. 更改cube

14 Kafka 流構建 Cube(Kylin實時案例)

Kylin v1.6 發布了可擴展的 streaming cubing 功能,它利用 Hadoop 消費 Kafka 數據的方式構建 cube。

參考:http://kylin.apache.org/blog/2016/10/18/new-nrt-streaming/

前期準備:kylin v1.6.0 或以上版本 和 可運行的 Kafka(v0.10.0 或以上版本)的 Hadoop 環境

14.1 Kafka創建Topic

  • 創建樣例名為 “kylin_streaming_topic” 具有一個副本三個分區的 topic
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic kylin_streaming_topic

  • 將樣例數據放入 topic,Kylin 有一個實用類可以做這項工作;
cd $KYLIN_HOME
./bin/kylin.sh org.apache.kylin.source.kafka.util.KafkaSampleProducer --topic kylin_streaming_topic --broker cdh01.cm:9092,cdh02.cm:9092,cdh03.cm:9092

工具每一秒會向 Kafka 發送 100 條記錄。直至本案例結束請讓其一直運行。

14.2 用streaming定義一張表

登陸 Kylin Web GUI,選擇一個已存在的 project 或創建一個新的 project;點擊 “Model” -> “Data Source”,點擊 “Add Streaming Table” 圖標

  • 在彈出的對話框中,輸入您從 kafka-console-consumer 中獲得的樣例記錄,點擊 “»” 按鈕,Kylin 會解析 JSON 消息並列出所有的消息
{"country":"CHINA","amount":41.53789973661185,"qty":6,"currency":"USD","order_time":1592485535129,"category":"TOY","device":"iOS","user":{"gender":"Male","id":"12d127ab-707e-592f-2e4c-69ad654afa48","first_name":"unknown","age":25}}
  • 您需要為這個 streaming 數據源起一個邏輯表名;該名字會在後續用於 SQL 查詢;這裡是在 “Table Name” 字段輸入 “STREAMING_SALES_TABLE” 作為樣例。

  • 您需要選擇一個時間戳字段用來標識消息的時間;Kylin 可以從這列值中獲得其他時間值,如 “year_start”,”quarter_start”,這為您構建和查詢 cube 提供了更高的靈活性。這裏可以查看 “order_time”。您可以取消選擇那些 cube 不需要的屬性。這裏我們保留了所有字段。

  • 注意 Kylin 從 1.6 版本開始支持結構化 (或稱為 “嵌入”) 消息,會將其轉換成一個 flat table structure。默認使用 “_” 作為結構化屬性的分隔符。

  • 點擊 “Next”。在這個頁面,提供了 Kafka 集群信息;輸入 “kylin_streaming_topic” 作為 “Topic” 名;集群有 3 個 broker,其主機名為”cdh01.cm,cdh02.cm,cdh03.cm“,端口為 “9092”,點擊 “Save”。
  • 在 “Advanced setting” 部分,”timeout” 和 “buffer size” 是和 Kafka 進行連接的配置,保留它們。

  • 在 “Parser Setting”,Kylin 默認您的消息為 JSON 格式,每一個記錄的時間戳列 (由 “tsColName” 指定) 是 bigint (新紀元時間) 類型值;在這個例子中,您只需設置 “tsColumn” 為 “order_time”;

  • 在現實情況中如果時間戳值為 string 如 “Jul 20,2016 9:59:17 AM”,您需要用 “tsParser” 指定解析類和時間模式例如:
  • 點擊 “Submit” 保存設置。現在 “Streaming” 表就創建好了。

14.3 定義數據模型

  • 有了上一步創建的表,現在我們可以創建數據模型了。步驟和您創建普通數據模型是一樣的,但有兩個要求:

    • Streaming Cube 不支持與 lookup 表進行 join;當定義數據模型時,只選擇 fact 表,不選 lookup 表;
    • Streaming Cube 必須進行分區;如果您想要在分鐘級別增量的構建 Cube,選擇 “MINUTE_START” 作為 cube 的分區日期列。如果是在小時級別,選擇 “HOUR_START”。
  • 這裏我們選擇 13 個 dimension 和 2 個 measure 列:

保存數據模型。

14.4 創建 Cube

Streaming Cube 和普通的 cube 大致上一樣. 有以下幾點需要您注意:

  • 分區時間列應該是 Cube 的一個 dimension。在 Streaming OLAP 中時間總是一個查詢條件,Kylin 利用它來縮小掃描分區的範圍。
  • 不要使用 “order_time” 作為 dimension 因為它非常的精細;建議使用 “mintue_start”,”hour_start” 或其他,取決於您如何檢查數據。
  • 定義 “year_start”,”quarter_start”,”month_start”,”day_start”,”hour_start”,”minute_start” 作為層級以減少組合計算。
  • 在 “refersh setting” 這一步,創建更多合併的範圍,如 0.5 小時,4 小時,1 天,然後是 7 天;這將會幫助您控制 cube segment 的數量。
  • 在 “rowkeys” 部分,拖拽 “minute_start” 到最上面的位置,對於 streaming 查詢,時間條件會一直显示;將其放到前面將會幫助您縮小掃描範圍。

保存 cube。

14.5 運行Cube

可以在 web GUI 觸發 build,通過點擊 “Actions” -> “Build”,或用 ‘curl’ 命令發送一個請求到 Kylin RESTful API:

curl -X PUT --user ADMIN:KYLIN -H "Content-Type: application/json;charset=utf-8" -d '{ "sourceOffsetStart": 0, "sourceOffsetEnd": 9223372036854775807, "buildType": "BUILD"}' http://localhost:7070/kylin/api/cubes/{your_cube_name}/build2

請注意 API 終端和普通 cube 不一樣 (這個 URL 以 “build2” 結尾)。

這裏的 0 表示從最後一個位置開始,9223372036854775807 (Long 類型的最大值) 表示到 Kafka topic 的結束位置。如果這是第一次 build (沒有以前的 segment),Kylin 將會尋找 topics 的開頭作為開始位置。

在 “Monitor” 頁面,一個新的 job 生成了;等待其直到 100% 完成。

14.6 查看結果

點擊 “Insight” 標籤,編寫 SQL 運行,例如:

select minute_start, count(*), sum(amount), sum(qty) from streaming_sales_table group by minute_start order by minute_start

14.7 自動 build

一旦第一個 build 和查詢成功了,您可以按照一定的頻率調度增量 build。Kylin 將會記錄每一個 build 的 offsets;當收到一個 build 請求,它將會從上一個結束的位置開始,然後從 Kafka 獲取最新的 offsets。有了 REST API 您可以使用任何像 Linux cron 調度工具觸發它:

crontab -e
*/5 * * * * curl -X PUT --user ADMIN:KYLIN -H "Content-Type: application/json;charset=utf-8" -d '{ "sourceOffsetStart": 0, "sourceOffsetEnd": 9223372036854775807, "buildType": "BUILD"}' http://localhost:7070/kylin/api/cubes/{your_cube_name}/build2

現在您可以觀看 cube 從 streaming 中自動 built。當 cube segments 累積到更大的時間範圍,Kylin 將會自動的將其合併到一個更大的 segment 中。

15 JDBC查詢kylin

  • maven依賴
    <dependencies>
        <dependency>
            <groupId>org.apache.kylin</groupId>
            <artifactId>kylin-jdbc</artifactId>
            <version>3.0.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 限制jdk版本插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • java類
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class KylinJdbc {
    public static void main(String[] args) throws Exception {
        //Kylin_JDBC 驅動
        String KYLIN_DRIVER = "org.apache.kylin.jdbc.Driver";
        //Kylin_URL
        String KYLIN_URL = "jdbc:kylin://localhost:9090/kylin_hive";
        //Kylin的用戶名
        String KYLIN_USER = "ADMIN";
        //Kylin的密碼
        String KYLIN_PASSWD = "KYLIN";
        //添加驅動信息
        Class.forName(KYLIN_DRIVER);
        //獲取連接
        Connection connection = DriverManager.getConnection(KYLIN_URL, KYLIN_USER, KYLIN_PASSWD);
        //預編譯SQL
        PreparedStatement ps = connection.prepareStatement("SELECT sum(sal) FROM emp group by deptno");
        //執行查詢
        ResultSet resultSet = ps.executeQuery();
        //遍歷打印
        while (resultSet.next()) {
                    System.out.println(resultSet.getInt(1));
        }
    }
}

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

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

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

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

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

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

※超省錢租車方案

聚甘新

印度雨季後氣候乾燥 德里登革熱疫情飆升

摘錄自2018年09月18日中央社報導

「印度斯坦時報」(Hindustan Times)18日引述南德里市政機構(SDMC)彙整新德里、東德里和北德里等另3個市政機構的統計顯示,德里地區今年迄今的登革熱病例達243例。

在15日之前的一個星期,德里地區就通報了106起登革熱病例,高達全年迄今243例的近一半。這段時間是德里近2個月連續降雨以來,首個未降雨的星期。而雨季後出現的乾燥氣候,專家認為是登革熱病例突然飆升的主因。醫護人員和專家都擔心,最近登革熱病例的上升,現在只是開端。

為防止登革熱疫情傳布,南德里市政機構最近已對社區大規模噴灑殺蟲劑。北德里市政機構也表示,正派檢查員調查和處罰未清理積水者。

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

聚甘新

Honda與微軟技術合作,推出AI電動車

日本汽車大廠本田(Honda)近日宣布,即將在下個月美國消費性電子展(CES 2017)中展出的最新電動概念車「NeuV」,將搭載軟銀(SoftBank)旗下雲端AI 技術研發公司cocoro SB 株式會社所打造的「情感引擎」,可望改變人與車之間冰冷的相處形態,增添更多情感交流與互動。

對汽車產業發展來說,人工智慧(AI)、機器人科技與大數據分析等,已逐漸成為改善行動體驗所不可或缺的重要技術。本田在今年7 月宣布,其子公司本田技術研究所(Honda R&D Co., Ltd.)將與軟銀合作,針對cocoro SB 所研發的情感引擎(emotion engine)共同研究有關應用。本田也在同年9 月於東京赤坂設置本田創新研究室「Honda R&D Innovation Lab Tokyo」,進一步研發AI 技術並強化其開放式創新發展。

提到cocoro SB 就不得不提人形機器人Pepper,因為Pepper 的情感引擎正是採用cocoro SB 的AI 雲端運算技術,在搭配攝影設備及多種感測器之下,能夠感知人類情緒與外在環境,從而建立自我情感並做出反應。

本田NeuV 概念車也採類似概念,透過系統收集而來的訊息判斷駕駛人的情緒,並學習做出適當反應加以交流對話,目標成為駕駛人的行動好夥伴,改變原本人與機器間的關係,同時也將為人們生活創造全新價值。至於NeuV 設計概念與技術細節,預計在下個月的CES 展中會有進一步說明。

(本文由《》授權提供。照片來源:Honda)

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

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

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

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

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

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

※超省錢租車方案

聚甘新

第七屆中國國際新能源汽車論壇2017之驅動創新

每當提起新能源汽車核心零部件,我們首先想到的是電池,而對電機電控等方面的探討和認知卻很少,究其原因,電控電機這兩大核心零部件在平日裡少有熱點和創新,尤其是在電控領域,國內供應商還處於一個相對初級的階段,所研發的產品還無法達到國際領先水準,因此極大的限制了廣大消費者們對電機電控技術的關心。然而,未來的5年時間內電機電控將會進入發展最關鍵的時期,並且熱度持續飆升。相關機構資料統計,預計到2020年,驅動電控及電機的累積市場規模將達到1600億元。

現階段,電控電機的產業已經形成了多重勢力競相爭奪的局面,除了國內本土的電控電機供應商外,整車廠、國內外汽車零部件供應商及外資新能源汽車生產商都相繼加入戰局。同時,隨著市場的熱度提升,電控電機技術領域也面臨著更大的挑戰。為了縮小國內外電控電機的技術差距,國內企業也在除了沿用傳統的製造工藝同時,在技術及工藝上也在進行不斷地研發及創新。

新能源汽車對於電控電機的品質要求相比于傳統汽車要更高,並且總體而言,電控電機產品的可靠性,一體化與新能源汽車的使用要求還存在一定差距,中國國際新能源汽車論壇—致力於打造全球規模最大,最國際化的新能源汽車論壇,第七屆大會攜手上海市嘉定區人民政府將會專門設立電控電機板塊,進行一天8小時的技術探討及分享,屆時國內外知名的整車商,電控電機供應商及零部件企業高層領導等將前來分享各自對於目前電控電機市場的發展前景看法及介紹最前沿的技術,同時,也會側重于目前應用最普遍的永磁電機從磁性材料和技術方面進行更深層次的交流。

本屆大會將於2017年5月17-19日在上海舉辦,大會涉及共七個論壇,頒獎典禮,研討會及晚宴。屆時將會有全球範圍內的整車製造商、電網電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員600多位行業人士一起,對新能源汽車產業面臨的挑戰,機遇與對策各方面進行為期三天更深層次並具有建設和戰略性的探討,期待您的參與。

若您對峰會有更多要求,請撥打021-6045 1760與我們聯繫,謝謝理解和支持!
我們期待與貴單位一起出席於5月17-19日在上海舉辦的第七屆中國國際新能源汽車論壇2017,以利決策!
想瞭解詳細內容,請登陸官方網站:
連絡人:Hill Zeng(曾先生)
電話:+86-21-6045 1760
傳真:+86-21-6047 5887
郵箱:

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

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

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

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

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

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

※超省錢租車方案

聚甘新

法國罕見地震規模5.4 近16年來最強

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

法國東南部11日近午時分發生一起芮氏規模5.4的地震,這個地區罕有超過規模5的地震,當地民眾感受特別深刻,已知有4人受傷,其中一人重傷。

法新社報導,巴黎地球物理研究所(IPGP)史特拉斯堡辦公室的學者梅格哈威(Mustapha Meghraoui)表示,這一地區的地震規模極少超過5,可以說這起地震很罕見。

震央鄰近克魯亞斯(Cruas)核電站及特里卡斯坦(Tricastin)核電站,法國電力集團(EDF)晚間決定暫停克魯亞斯核電站的反應爐,以便進行預防性檢測;法國核能安全署(ASN)表示,地震並未在相關設施造成顯著損壞,但仍將檢視反應爐重啟的條件。

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

【K8S】Service服務詳解,看這一篇就夠了!!

k8s用命名空間namespace把資源進行隔離,默認情況下,相同的命名空間里的服務可以相互通訊,反之進行隔離。

1.1 Service

Kubernetes中一個應用服務會有一個或多個實例(Pod,Pod可以通過rs進行多複本的建立),每個實例(Pod)的IP地址由網絡插件動態隨機分配(Pod重啟后IP地址會改變)。為屏蔽這些後端實例的動態變化和對多實例的負載均衡,引入了Service這個資源對象,如下所示:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  ports:
    - port: 80
       targetPort: 80
  selector:  #service通過selector和pod建立關聯
    app: nginx

根據創建Service的type類型不同,可分成4種模式:

  • ClusterIP: 默認方式。根據是否生成ClusterIP又可分為普通Service和Headless Service兩類:
    • 普通Service:通過為Kubernetes的Service分配一個集群內部可訪問的固定虛擬IP(Cluster IP),實現集群內的訪問。為最常見的方式。
    • Headless Service:該服務不會分配Cluster IP,也不通過kube-proxy做反向代理和負載均衡。而是通過DNS提供穩定的絡ID來訪問,DNS會將headless service的後端直接解析為podIP列表。主要供StatefulSet使用。
  • NodePort:除了使用Cluster IP之外,還通過將service的port映射到集群內每個節點的相同一個端口,實現通過nodeIP:nodePort從集群外訪問服務。
  • LoadBalancer:和nodePort類似,不過除了使用一個Cluster IP和nodePort之外,還會向所使用的公有雲申請一個負載均衡器(負載均衡器後端映射到各節點的nodePort),實現從集群外通過LB訪問服務。
  • ExternalName:是 Service 的特例。此模式主要面向運行在集群外部的服務,通過它可以將外部服務映射進k8s集群,且具備k8s內服務的一些特徵(如具備namespace等屬性),來為集群內部提供服務。此模式要求kube-dns的版本為1.7或以上。這種模式和前三種模式(除headless service)最大的不同是重定向依賴的是dns層次,而不是通過kube-proxy。
    比如,在service定義中指定externalName的值”my.database.example.com”:

此時k8s集群內的DNS服務會給集群內的服務名 ..svc.cluster.local 創建一個CNAME記錄,其值為指定的”my.database.example.com”。
當查詢k8s集群內的服務my-service.prod.svc.cluster.local時,集群的 DNS 服務將返回映射的CNAME記錄”foo.bar.example.com”。

備註: 前3種模式,定義服務的時候通過selector指定服務對應的pods,根據pods的地址創建出endpoints作為服務後端;Endpoints Controller會watch Service以及pod的變化,維護對應的Endpoint信息。kube-proxy根據Service和Endpoint來維護本地的路由規則。當Endpoint發生變化,即Service以及關聯的pod發生變化,kube-proxy都會在每個節點上更新iptables,實現一層負載均衡。 而ExternalName模式則不指定selector,相應的也就沒有port和endpoints。 ExternalName和ClusterIP中的Headles Service同屬於Headless Service的兩種情況。Headless Service主要是指不分配Service IP,且不通過kube-proxy做反向代理和負載均衡的服務。

1.2 Port

Service中主要涉及三種Port: * port 這裏的port表示service暴露在clusterIP上的端口,clusterIP:Port 是提供給集群內部訪問kubernetes服務的入口。

  • targetPort

containerPort,targetPort是pod上的端口,從port和nodePort上到來的數據最終經過kube-proxy流入到後端pod的targetPort上進入容器。

  • nodePort

nodeIP:nodePort 是提供給從集群外部訪問kubernetes服務的入口。

總的來說,port和nodePort都是service的端口,前者暴露給從集群內訪問服務,後者暴露給從集群外訪問服務。從這兩個端口到來的數據都需要經過反向代理kube-proxy流入後端具體pod的targetPort,從而進入到pod上的容器內。

1.3 IP

使用Service服務還會涉及到幾種IP:

  • ClusterIP

Pod IP 地址是實際存在於某個網卡(可以是虛擬設備)上的,但clusterIP就不一樣了,沒有網絡設備承載這個地址。它是一個虛擬地址,由kube-proxy使用iptables規則重新定向到其本地端口,再均衡到後端Pod。當kube-proxy發現一個新的service后,它會在本地節點打開一個任意端口,創建相應的iptables規則,重定向服務的clusterIP和port到這個新建的端口,開始接受到達這個服務的連接。

  • Pod IP

Pod的IP,每個Pod啟動時,會自動創建一個鏡像為gcr.io/google_containers/pause的容器,Pod內部其他容器的網絡模式使用container模式,並指定為pause容器的ID,即:network_mode: “container:pause容器ID”,使得Pod內所有容器共享pause容器的網絡,與外部的通信經由此容器代理,pause容器的IP也可以稱為Pod IP。

  • 節點IP

Node-IP,service對象在Cluster IP range池中分配到的IP只能在內部訪問,如果服務作為一個應用程序內部的層次,還是很合適的。如果這個service作為前端服務,準備為集群外的客戶提供業務,我們就需要給這個服務提供公共IP了。指定service的spec.type=NodePort,這個類型的service,系統會給它在集群的各個代理節點上分配一個節點級別的端口,能訪問到代理節點的客戶端都能訪問這個端口,從而訪問到服務。

 

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

C#中的閉包和意想不到的坑

雖然閉包主要是函數式編程的玩意兒,而C#的最主要特徵是面向對象,但是利用委託或lambda表達式,C#也可以寫出具有函數式編程風味的代碼。同樣的,使用委託或者lambda表達式,也可以在C#中使用閉包。

根據WIKI的定義,閉包又稱語法閉包或函數閉包,是在函數式編程語言中實現語法綁定的一種技術。閉包在實現上是一個結構體,它存儲了一個函數(通常是其入口地址)和一個關聯的環境(相當於一個符號查找表)。閉包也可以延遲變量的生存周期。

嗯。。看定義好像有點迷糊,讓我們看看下面的例子吧

    class Program
    {
        static Action CreateGreeting(string message)
        {
            return () => { Console.WriteLine("Hello " + message); };
        }

        static void Main()
        {
            Action action = CreateGreeting("DeathArthas");
            action();
        }
    }

這個例子非常簡單,用lambda表達式創建一個Action對象,之後再調用這個Action對象。
但是仔細觀察會發現,當Action對象被調用的時候,CreateGreeting方法已經返回了,作為它的實參的message應該已經被銷毀了,那麼為什麼我們在調用Action對象的時候,還是能夠得到正確的結果呢?
 
原來奧秘就在於,這裏形成了閉包。雖然CreateGreeting已經返回了,但是它的局部變量被返回的lambda表達式所捕獲,延遲了其生命周期。怎麼樣,這樣再回頭看閉包定義,是不是更清楚了一些?
 
閉包就是這麼簡單,其實我們經常都在使用,只是有時候我們都不自知而已。比如大家肯定都寫過類似下面的代碼。

void AddControlClickLogger(Control control, string message)
{
	control.Click += delegate
	{
		Console.WriteLine("Control clicked: {0}", message);
	}
}

這裏的代碼其實就用了閉包,因為我們可以肯定,在control被點擊的時候,這個message早就超過了它的聲明周期。合理使用閉包,可以確保我們寫出在空間和時間上面解耦的委託。
 
不過在使用閉包的時候,要注意一個陷阱。因為閉包會延遲局部變量的生命周期,在某些情況下程序產生的結果會和預想的不一樣。讓我們看看下面的例子。

    class Program
    {
	static List<Action> CreateActions()
        {
            var result = new List<Action>();
            for(int i = 0; i < 5; i++)
            {
                result.Add(() => Console.WriteLine(i));
            }
            return result;
        }

        static void Main()
        {
            var actions = CreateActions();
            for(int i = 0;i<actions.Count;i++)
            {
                actions[i]();
            }
        }
    }

這個例子也非常簡單,創建一個Action鏈表並依次執行它們。看看結果

相信很多人看到這個結果的表情是這樣的!!難道不應該是0,1,2,3,4嗎?出了什麼問題?

刨根問底,這兒的問題還是出現在閉包的本質上面,作為“閉包延遲了變量的生命周期”這個硬幣的另外一面,是一個變量可能在不經意間被多個閉包所引用。

在這個例子裏面,局部變量i同時被5個閉包引用,這5個閉包共享i,所以最後他們打印出來的值是一樣的,都是i最後退出循環時候的值5。

要想解決這個問題也很簡單,多聲明一個局部變量,讓各個閉包引用自己的局部變量就可以了。

	//其他都保持與之前一致
        static List<Action> CreateActions()
        {
            var result = new List<Action>();
            for (int i = 0; i < 5; i++)
            {
                int temp = i; //添加局部變量
                result.Add(() => Console.WriteLine(temp));
            }
            return result;
        }

這樣各個閉包引用不同的局部變量,剛剛的問題就解決了。

除此之外,還有一個修復的方法,在創建閉包的時候,使用foreach而不是for。至少在C# 7.0 的版本上面,這個問題已經被注意到了,使用foreach的時候編譯器會自動生成代碼繞過這個閉包陷阱。

	//這樣fix也是可以的
        static List<Action> CreateActions()
        {
            var result = new List<Action>();
            foreach (var i in Enumerable.Range(0,5))
            {
                result.Add(() => Console.WriteLine(i));
            }
            return result;
        }

這就是在閉包在C#中的使用和其使用中的一個小陷阱,希望大家能通過老胡的文章了解到這個知識點並且在開發中少走彎路!

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

區塊鏈系列教程之:比特幣的錢包與交易

目錄

  • 簡介
  • 比特幣密碼學的基礎
    • 單向散列函數(hash算法)
    • 非對稱加密算法
    • 擴展閱讀:同態加密
  • 密鑰,地址和錢包
  • 比特幣中的交易
  • 擴展閱讀:圖靈非完備性
  • 總結

簡介

錢包在比特幣中是做什麼的呢?比特幣的交易又有什麼特點呢?怎麼才能偽造比特幣的交易呢?今天和大家一起學習一下比特幣中的錢包和交易。

比特幣密碼學的基礎

之前我們提到過比特幣使用的並不是什麼新技術,只是對於老的技術比如:P2P網絡,分佈式系統,密碼學,共識算法的重新而又巧妙的應用。

在錢包和交易生成驗證的過程中,都需要使用到密碼學的計算。這裏我們先介紹一下比特幣中會使用到的幾種密碼學技術。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

單向散列函數(hash算法)

在介紹單向散列函數之前,我們先了解一下什麼情況下需要使用到單向散列函數。

如果你需要從國外的網站上下載一個軟件,但是因為種種原因,國外的網絡太慢了,下載幾個G的數據幾乎是不可能的。剛好國內有鏡像網站,可以從國內下載數據。但是如何保證國內的鏡像不是被篡改過後的呢?這個時候就需要單向散列函數了。一般來說網站會提供MD5或者SHA的值作為驗證值。

單向散列函數有一個輸入和輸出。輸入稱為消息,輸出稱為散列值。

散列值的長度跟消息的長度無關,不論多少大小的長度的消息,都會計算出固定長度的散列值。

hash算法有下面幾個特點:

  1. 能夠根據任意長度的消息計算出固定長度的散列值。

  2. 計算速度要快。

  3. 消息不同,散列值也不同。

    這就意味着,如果僅僅是一點點的變動都會引起整個散列值的巨大變化。

    因為散列值的大小是固定的,所以有可能會出現不同的消息產生相同散列值的情況。這種情況叫做碰撞。

    難以發現碰撞的性質被稱為抗碰撞性。當給定某條消息的散列值時,必須保證很難找到和該消息具有相同散列值的另一條消息。

  4. 單向散列函數必須具有單向性。所謂單向性是指無法通過散列值來反推出消息的性質。

比特幣使用的散列算法是SHA256,他是安全散列算法SHA(Secure Hash Algorithm)系列算法的一種(另外還有SHA-1、SHA-224、SHA-384 和 SHA-512 等變體),SHA是美國國家安全局 (NSA) 設計,美國國家標準與技術研究院(NIST) 發布的,主要適用於数字簽名標準(DigitalSignature Standard DSS)裏面定義的数字簽名算法(Digital Signature Algorithm DSA)。

RIPEMD(RACE Integrity Primitives Evaluation Message Digest,RACE原始完整性校驗消息摘要),是Hans Dobbertin等3人在md4,md5的基礎上,於1996年提出來的。

非對稱加密算法

非對稱加密算法也叫公鑰密碼算法,通過生成的公私鑰來對明文密文進行加密解密。

非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。

擴展閱讀:同態加密

同態加密是一種加密形式,它允許人們對密文進行特定的代數運算得到仍然是加密的結果,將其解密所得到的結果與對明文進行同樣的運算結果一樣。換言之,這項技術令人們可以在加密的數據中進行諸如檢索、比較等操作,得出正確的結果,而在整個處理過程中無需對數據進行解密。其意義在於,真正從根本上解決將數據及其操作委託給第三方時的保密問題,例如對於各種雲計算的應用。

密鑰,地址和錢包

比特幣的所有權是通過数字密鑰、比特幣地址和数字簽名來確立的。数字密鑰實際上並不是存儲在網絡中,而是由用戶生成並存儲在一個文件或簡單的數據庫 中,稱為錢包。存儲在用戶錢包中的数字密鑰完全獨立於比特幣協議,可由用戶的錢包軟件生成並管理,而無需區塊鏈或網絡連接。密鑰實現了比特幣的許多有趣特性,包括去中心化信任和控制、所有權認證和基於密碼學證明的安全模型。

比特幣錢包只包含私鑰而不是比特幣。每一個用戶有一個包含多個私鑰的錢包。錢包中包含成對的私鑰和公鑰。用戶用這些私鑰來簽名交易,從而證明它們擁有交易的輸出(也就是其中的比特幣)。比特幣是以交易輸出的形式來儲存在區塊鏈中(通常記為vout或txout)。

如果錢包只包含私鑰,那麼錢包地址是什麼呢?錢包地址是從公鑰的hash值的出來的,如下圖所示:

  1. 首先使用隨機數發生器生成一個『私鑰』。一般來說這是一個256bits的數,擁有了這串数字就可以對相應『錢包地址』中的比特幣進行操作,所以必須被安全地保存起來。

  2. 『私鑰』經過SECP256K1算法處理生成了『公鑰』。SECP256K1是一種橢圓曲線算法,通過一個已知『私鑰』時可以算得『公鑰』,而『公鑰』已知時卻無法反向計算出『私鑰』。這是保障比特幣安全的算法基礎。

  3. 同SHA256一樣,RIPEMD160也是一種Hash算法,由『公鑰』可以計算得到『公鑰哈希』,而反過來是行不通的。

  4. 將一個字節的地址版本號連接到『公鑰哈希』頭部(對於比特幣網絡的pubkey地址,這一字節為“0”),然後對其進行兩次SHA256運算,將結果的前4字節作為『公鑰哈希』的校驗值,連接在其尾部。

  5. 將上一步結果使用BASE58進行編碼(比特幣定製版本),就得到了『錢包地址』。 比如,1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa。

所以私鑰,公鑰和錢包地址的關係如下圖所示:

大家看到錢包地址1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa有什麼想法呢?

肯定有人在想,這麼一大長串字母和数字實在是太不好記憶了。能不能生產一個比較好記的錢包地址呢? 比如MyNameIsHanMeiMei….這樣開頭的地址呢?

當然可以,這叫做靚號地址,只不過需要大量的算力才行。

比特幣中的交易

簡單來說,交易就是告知全網:比特幣的持有者已授權把比特幣轉帳給其他人。而新持有者能夠再次授權,轉移給該比特幣所有權鏈中的其他人。

注意, 在比特幣的世界里既沒有賬戶,也沒有餘額,只有分散到區塊鏈里的UTXO(Unspent Transaction Outputs)。

怎麼理解這個UTXO呢?沒有賬戶也沒有餘額,那麼錢包裏面的金額是怎麼計算出來的呢?

別急,讓我們一一道來。

話說,在比特幣中,比特幣錢包間的轉賬是通過交易(Transaction)實現的。

我們看一個標準的交易流程。

那麼問題來了,世界上第一個比特幣是哪裡來的呢?

答,是挖礦來的。好了,我們的001交易表示的就是一個挖礦的過程,在這個交易中,輸入就是挖礦,輸出編號1,BTC數目是50,目的地址是A,表示這50個BTC給A了。

接下來,A想發25個BTC給B,怎麼構造這個交易呢?

同樣的,我們需要一個輸入,這個輸入就是001交易的1號輸出,我們用001.1來表示。輸出分為兩個,第一個輸出編號1,表示要付25個BTC給B。第二個輸出編號2,表示剩下的BTC要還給A。

大家可能會問了,輸入是50BTC,兩個輸出加起來才45個BTC,好像還少了5個BTC?沒錯,這個5個BTC就是給礦工的挖礦所得。

接下來,A又繼續轉賬給C,同樣的道理,把一個一個的交易連接起來。

從上面的例子我們可以看到,實際上錢是存在一個一個的交易記錄裏面的,那些未被花費的輸出,就叫做UTXO(Unspent Transaction Outputs)。

那麼怎麼保證轉賬給B的錢,不會被其他的人消費呢?這就涉及到交易的加密過程了。

我們以單個輸入和輸出為例來詳細了解一下交易的構成:

上圖中,交易的輸入就是txid,也就是之前生成的還有未花費暑輸出的交易ID。output index就是交易的輸出id。

一個非常重要的ScriptSig是輸入交易的驗證,表明這個用戶擁有這個賬戶的轉賬權限。

輸出是一個腳本,只有滿足腳本運行條件的人才能花費這個output。這也就是ScriptSig需要驗證的腳本。

我們看下腳本是怎麼做認證的吧。

比特幣的標準輸出形式有兩種。Pay To Public Key Hash (P2PKH) 和 Pay To Script Hash (P2SH)。兩者的區別在於,一個是輸出到public key的hash,一個是輸出到任意的一個腳本輸出hash。

為了保證輸出只能由特定的人來花費,一般的情況下是直接輸出到對方的public key hash。由於只有對方擁有的私鑰能夠生成這個public key hash,也就是說只有對方才能夠對這個輸出進行驗證。

但每次都需要知道對方的public key hash還是比較麻煩的,更簡單的做法就是,發送者直接輸出到一個特定的hash值就行了,只要對方能夠生成這個hash就可以。

下面的例子是一個P2PKH的腳本形式。

P2PKH的輸出是一個腳本,裏面一個重要的值就是PK hash。

怎麼驗證呢?

驗證方提供兩個值,一個是sig,一個是PubKey。因為比特幣的虛擬機是棧結構的,我們先把這兩個值入棧。

然後調用OP_DUP對最上層的PubKey進行拷貝,然後調用OP_HASH160算法來計算Pk Hash,然後將發送方保存的Pk Hash入棧。接下來調用OP_EQUALVERIFY對兩個PK Hash進行對比。

如果對比成功,最後一步就是驗證Sig和PubKey是否匹配。

如果都成功,說明接收方的確是這個PK Hash的擁有者。那麼對方就可以盡情使用了。

擴展閱讀:圖靈非完備性

和馮·諾伊曼同為現代計算機奠基人的阿蘭·圖靈(AlanTurin)在1950年提出了判定計算機能否像人那般實際“思考”的標準,也就是著名的“圖靈檢驗”。

他設想一台超級計算機和一個人躲藏在幕後回答提問者的問題,而提問者則試圖分辨哪個是人哪個是計算機。

圖靈爭辯說,假如計算機偽裝得如此巧妙,以致沒有人可以在實際上把它和一個真人分辨開來的話,那麼我們就可以聲稱,這台計算機和人一樣具備了思考能力,或者說,意識(他的原詞是“智慧”)。

在可計算性理論里,如果一系列操作數據的規則(如指令集、編程語言、細胞自動機)按照一定的順序可以計算出結果,被稱為圖靈完備(turing complete)。

比特幣腳本語言不是圖靈完備的,具有一定的局限性,它沒有循環語句和複雜的條件控制語句。

總結

本文介紹了比特幣的錢包和交易的概念,希望大家能夠喜歡。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/bitcoin-transactions/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案