内存Join——一种经过内存映射技术优化海量数据Join性能的方法-DDD死党 (内存条怎么选)
1.为什么"内存Join"是个不可绕过的话题
首先,咱们先繁难解释下,什么是内存Join。
置信大家对相关数据库的join语句必需不生疏,其作用就是经过关联相关从多个表中查问数据,关联条件和数据聚合所有由数据库服务成功。
而内存Join,繁难来说就是把原本数据库帮咱们成功的数据聚合操作迁徙到运行服务,在运行服务的内存中成功。
数据库join十分繁难,但随着系统的开展,内存join变得越来越关键,其外围驱能源有:
2.课程先导
发现变动,封装变动,治理变动,是开发人员的必备技艺。
本篇文章从查问订单这个业务场景为入口,针对数据的内存join启动屡次形象和封装,最终成功内存Join申明化。
首先,先看下最终的成果,从直观上感触下形象带来的效率优化。
经过形象,可以到达如下成果:
奥秘面前的实质便是形象。让咱们以订单查问为线索,层层递进,最终成功才干申明化。
才干申明化,是形象的一种初级体现,无需编写代码,经过性能的方式为特定组件启动才干增强。
在正式开局之前,可以先了解下全体的推演流程:
3.【案例剖析】订单查问
假定,咱们是订单中心的一位研发同伴,须要开发我的订单模块,其外围接口包含:
依据需求定义OrderService接口如下:
publicinterfaceOrderService{//我的订单List<OrderListVO>getByUserId(LonguserId);//订单概略OrderDetlVOgetDetailByOrderId(LongorderId);}//为配合多种成功战略,经常使用形象类启动一致publicabstractclassOrderListVO{publicabstractOrderVOgetOrder();publicabstractUserVOgetUser();publicabstractAddressVOgetAddress();publicabstractProductVOgetProduct();}//为配合多种成功战略,经常使用形象类启动一致publicabstractclassOrderDetailVO{publicabstractOrderVOgetOrder();publicabstractUserVOgetUser();publicabstractAddressVOgetAddress();publicabstractProductVOgetProduct();publicabstractList<PayInfoVO>getPayInfo();}
3.1.Foreach+单条抓取打算
这么繁难的需求,那不是信手拈来,很快就提供了一版
代码详细如下:
@ServicepublicclassOrderServiceCodingV1implementsOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateAddressRepositoryaddressRepository;@AutowiredprivateProductRepositoryproductRepository;@AutowiredprivateUserRepositoryuserRepository;@AutowiredprivatePayInfoRepositorypayInfoRepository;@OverridepublicList<OrderListVO>getByUserId(LonguserId){//失掉用户订单List<Order>orders=this.orderRepository.getByUserId(userId);//依次启动数据绑定returnorders.stream().map(order->convertToOrderListVO(order)).collect(toList());}privateOrderListVOCodingV1convertToOrderListVO(Orderorder){OrderVOorderVO=OrderVO.ly(order);OrderListVOCodingV1orderDetailVO=newOrderListVOCodingV1(orderVO);//绑定地址消息Addressaddress=this.addressRepository.getById(order.getAddressId());AddressVOaddressVO=AddressVO.apply(address);orderDetailVO.setAddress(addressVO);//绑定用户消息Useruser=this.userRepository.getById(order.getUserId());UserVOuserVO=UserVO.apply(user);orderDetailVO.setUser(userVO);//绑定商品消息Productproduct=this.productRepository.getById(order.getProductId());ProductVOproductVO=ProductVO.apply(product);orderDetailVO.setProduct(productVO);returnorderDetailVO;}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){//临时疏忽Orderorder=this.orderRepository.getById(orderId);returnconvertToOrderDetailVO(order);}privateOrderDetailVOconvertToOrderDetailVO(Orderorder){OrderDetailVOCodingV1orderDetail=newOrderDetailVOCodingV1(OrderVO.apply(order));//失掉地址并启动绑定Addressaddress=this.addressRepository.getById(order.getAddressId());AddressVOaddressVO=AddressVO.apply(address);orderDetail.setAddress(addressVO);//失掉用户并启动绑定Useruser=this.userRepository.getById(order.getUserId());UserVOuserVO=UserVO.apply(user);orderDetail.setUser(userVO);//失掉商品并启动绑定Productproduct=this.productRepository.getById(order.getProductId());ProductVOproductVO=ProductVO.apply(product);orderDetail.setProduct(productVO);//失掉支付消息并启动绑定List<PayInfo>payInfos=this.payInfoRepository.getByOrderId(order.getId());List<PayInfoVO>payInfoVOList=payInfos.stream().map(PayInfoVO::apply).collect(toList());orderDetail.setPayInfo(payInfoVOList);returnorderDetail;}}
假设真的这样成功,那你离被跑路不远了。
为什么会这么说呢?由于==我的订单这个接口存在重大的性能疑问!==
我的订单接口详细成功如下:
单个用户恳求,数据库访问总次数=1(失掉用户订单)+N(订复数量)*3(须要抓取的关联数据)
其中,N(订复数量)*3(关联数据数量)存在性能隐患,存在重大的==读加大效应==。一旦遇到虔诚用户,存在成千盈百订单,除了超时别无方法。
订单概略接口成功,目前疑问不大,最大的疑问为: 订单概略与我的订单两个接口存在少量的重复逻辑!
3.2.批量查问+内存Join
首先,咱们先来处置我的订单接口的性能疑问。从之前的剖析可知,性能低下的基本要素在于 读加大效应 ,数据库恳求次数与用户订复数成正比,为了更好的保证性能,最好将数据库操作控制在一个常量。
全体思绪为:先批量失掉要绑定的数据,而后遍历每一个订单,在内存中成功数据绑定。
实现代码如下:
@ServicepublicclassOrderServiceCodingV2implementsOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateAddressRepositoryaddressRepository;@AutowiredprivateProductRepositoryproductRepository;@AutowiredprivateUserRepositoryuserRepository;@AutowiredprivatePayInfoRepositorypayInfoRepository;@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOCodingV2>orderDetailVOS=orders.stream().map(order->newOrderListVOCodingV2(OrderVO.apply(order))).collect(toList());//批量失掉用户,并依次启动绑定List<Long>userIds=orders.stream().map(Order::getUserId).collect(toList());List<User>users=this.userRepository.getByIds(userIds);Map<Long,User>userMap=users.stream().collect(toMap(User::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Useruser=userMap.get(orderDetailVO.getOrder().getUserId());UserVOuserVO=UserVO.apply(user);orderDetailVO.setUser(userVO);}//批量失掉地址,并依次启动绑定List<Long>addressIds=orders.stream().map(Order::getAddressId).collect(toList());List<Address>addresses=this.addressRepository.getByIds(addressIds);Map<Long,Address>addressMap=addresses.stream().collect(toMap(Address::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Addressaddress=addressMap.get(orderDetailVO.getOrder().getAddressId());AddressVOaddressVO=AddressVO.apply(address);orderDetailVO.setAddress(addressVO);}//批量失掉商品,并依次启动绑定List<Long>productIds=orders.stream().map(Order::getProductId).collect(toList());List<Product>products=this.productRepository.getByIds(productIds);Map<Long,Product>productMap=products.stream().collect(toMap(Product::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Productproduct=productMap.get(orderDetailVO.getOrder().getProductId());ProductVOproductVO=ProductVO.apply(product);orderDetailVO.setProduct(productVO);}returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){//临时疏忽Orderorder=this.orderRepository.getById(orderId);returnconvertToOrderDetailVO(order);}privateOrderDetailVOconvertToOrderDetailVO(Orderorder){//临时疏忽returnorderDetail;}}
调整之后,关于我的订单接口,单个用户恳求==数据库的访问次数变成了常量(4)==。
假设你是这么成功的,那祝贺你,你已步入 合格程序员行列 。
3.3.并行批量查问+内存Join
批量查问+内存Join打算能满足大局部场景,假设要抓取的数据太多,也就是数据库访问这个==常质变大==时,性能也会越来越差。
要素很繁难,由于串行口头, 全体耗时=失掉订单耗时+sum(抓取数据耗时)
痴呆的同窗早就摩拳擦掌,这个我会: 多线程并行口头呗。
是的,基于Future的成功如下(还有很多版本,比如CountDownLatch)
全体设计如下:
示例代码如下:
@ServicepublicclassOrderServiceCodingV3implementsOrderService{privateExecutorServiceexecutorService;@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateAddressRepositoryaddressRepository;@AutowiredprivateProductRepositoryproductRepository;@AutowiredprivateUserRepositoryuserRepository;@AutowiredprivatePayInfoRepositorypayInfoRepository;@PostConstructpublicvoidinit(){//初始化线程池(不要经常使用Executors,这里只是展示,须要对资源启动评价)this.executorService=Executors.newFixedThreadPool(20);}@SneakyThrows@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOCodingV2>orderDetailVOS=orders.stream().map(order->newOrderListVOCodingV2(OrderVO.apply(order))).collect(toList());List<Callable<Void>>callables=Lists.newArrayListWithCapacity(3);//创立异步义务callables.add(()->{//批量失掉用户,并依次启动绑定List<Long>userIds=orders.stream().map(Order::getUserId).collect(toList());List<User>users=this.userRepository.getByIds(userIds);Map<Long,User>userMap=users.stream().collect(toMap(User::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Useruser=userMap.get(orderDetailVO.getOrder().getUserId());UserVOuserVO=UserVO.apply(user);orderDetailVO.setUser(userVO);}returnnull;});//创立异步义务callables.add(()->{//批量失掉地址,并依次启动绑定List<Long>addressIds=orders.stream().map(Order::getAddressId).collect(toList());List<Address>addresses=this.addressRepository.getByIds(addressIds);Map<Long,Address>addressMap=addresses.stream().collect(toMap(Address::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Addressaddress=addressMap.get(orderDetailVO.getOrder().getAddressId());AddressVOaddressVO=AddressVO.apply(address);orderDetailVO.setAddress(addressVO);}returnnull;});//创立异步义务callables.add(()->{//批量失掉商品,并依次启动绑定List<Long>productIds=orders.stream().map(Order::getProductId).collect(toList());List<Product>products=this.productRepository.getByIds(productIds);Map<Long,Product>productMap=products.stream().collect(toMap(Product::getId,Function.identity(),(a,b)->a));for(OrderListVOCodingV2orderDetailVO:orderDetailVOS){Productproduct=productMap.get(orderDetailVO.getOrder().getProductId());ProductVOproductVO=ProductVO.apply(product);orderDetailVO.setProduct(productVO);}returnnull;});//口头异步义务this.executorService.invokeAll(callables);returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){//临时疏忽Orderorder=this.orderRepository.getById(orderId);returnconvertToOrderDetailVO(order);}privateOrderDetailVOconvertToOrderDetailVO(Orderorder){//临时疏忽}}
多线程并发口头, 全体耗时=失掉订单耗时+max(抓取数据耗时)
假设你能够这样成功的,那祝贺你,你已步入 初级程序员行列 。
而后呢,到此为止了?NO,接上去才是高潮!!!
让咱们关上认知,开启形象+封装之旅。
4.Fetcher封装
细心钻研上述代码,寻觅外面的==变与不变==,你会发现:
找到逻辑中的变动点,接上去便是有针对性的启动封装。
4.1.消弭方法中的重复代码
关于我的订单和订单概略前往==不同的VO==,该怎样处置呢?
十分繁难,思绪如下:
全体设计如下:
繁难示例如下:
//以UserVO为例,ProductVO、AddressVO,PayInfoVO基本分歧,不在赘述publicinterfaceUserVOFetcherV1{LonggetUserId();voidsetUser(UserVOuser);}//OrderDetailVO成功对应的接口,为了突出重点临时疏忽详细成功publicclassOrderDetailVOFetcherV1extendsOrderDetailVOimplementsAddressVOFetcherV1,ProductVOFetcherV1,UserVOFetcherV1,PayInfoVOFetcherV1{}//OrderListVO成功对应接口,为了突出重点临时疏忽详细成功publicclassOrderListVOFetcherV1extendsOrderListVOimplementsAddressVOFetcherV1,ProductVOFetcherV1,UserVOFetcherV1{}
有了一致的操作接口,接上去便是抽取详细的绑定逻辑,以UserVOFetcherExecutor为例:
@ComponentpublicclassUserVOFetcherExecutorV1{@AutowiredprivateUserRepositoryuserRepository;publicvoidfetch(List<?extendsUserVOFetcherV1>fetchers){List<Long>ids=fetchers.stream().map(UserVOFetcherV1::getUserId).distinct().collect(Collectors.toList());List<User>users=userRepository.getByIds(ids);Map<Long,User>userMap=users.stream().collect(toMap(user->user.getId(),Function.identity()));fetchers.forEach(fetcher->{LonguserId=fetcher.getUserId();Useruser=userMap.get(userId);if(user!=null){UserVOuserVO=UserVO.apply(user);fetcher.setUser(userVO);}});}}
成功逻辑没有变动,最关键的变动在于入参类型,不在是详细的VO,而是形象的UserVOFetcher接口。
AddressVOFetcherExecutor、ProductVOFetcherExecutor、PayInfoVOFetcherExecutor与UserVOFetcherExecutorV1逻辑基本分歧,篇幅疑问不在赘述。
这样一个小小的调整,会给经常使用方带来什么便利?一同看下经常使用方的变动:
@ServicepublicclassOrderServiceFetcherV1implementsOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateAddressVOFetcherExecutorV1addressVOFetcherExecutorV1;@AutowiredprivateProductVOFetcherExecutorV1productVOFetcherExecutorV1;@AutowiredprivateUserVOFetcherExecutorV1userVOFetcherExecutorV1;@AutowiredprivatePayInfoVOFetcherExecutorV1payInfoVOFetcherExecutorV1;@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOFetcherV1>orderDetailVOS=orders.stream().map(order->newOrderListVOFetcherV1(OrderVO.apply(order))).collect(toList());//间接经常使用FetcherExecutor成功数据绑定this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){Orderorder=this.orderRepository.getById(orderId);OrderDetailVOFetcherV1orderDetail=newOrderDetailVOFetcherV1(OrderVO.apply(order));List<OrderDetailVOFetcherV1>orderDetailVOS=Arrays.asList(orderDetail);//间接经常使用FetcherExecutor成功数据绑定this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);this.payInfoVOFetcherExecutorV1.fetch(orderDetailVOS);returnorderDetail;}}
两个方法间接经常使用FetcherExecutor成功数据抓取和绑定,成功了==绑定逻辑的复用==。
假设再有VO须要启动数据绑定,只要:
至此,面对新业务基本上与绑定逻辑说再见了。
4.2.重构绑定逻辑
接上去让咱们一同聚焦于绑定逻辑,先对比下上述的UserVOFetcherExecutor与上方的AddressVOFetcherExecutor,找到外面的变动与不变:
@ComponentpublicclassAddressVOFetcherExecutorV1{@AutowiredprivateAddressRepositoryaddressRepository;publicvoidfetch(List<?extendsAddressVOFetcherV1>fetchers){//失掉关联消息List<Long>ids=fetchers.stream().map(AddressVOFetcherV1::getAddressId).distinct().collect(Collectors.toList());//查问关联数据List<Address>addresses=addressRepository.getByIds(ids);//转为为MapMap<Long,Address>addressMap=addresses.stream().collect(toMap(address->address.getId(),Function.identity()));//依次启动数据绑定fetchers.forEach(fetcher->{LongaddressId=fetcher.getAddressId();Addressaddress=addressMap.get(addressId);if(address!=null){//转换为VOAddressVOaddressVO=AddressVO.apply(address);//将数据写回到结果fetcher.setAddress(addressVO);}});}}
细心观察,会发现:
【不变】逻辑骨架基本分歧 ,基本是由:
【变动】成功细节存在差异 ;
相熟设计形式的同伴能否眼前一亮?进度一下好好回忆一下,哪种形式就是用来处置这种疑问的?
答案便是:模板方法形式
全体思维为:
全体设计如下:
抽取公共父类如下:
abstractclassBaseItemFetcherExecutor<FETCHERextendsItemFetcher,>@ComponentpublicclassUserVOFetcherExecutorV2extendsBaseItemFetcherExecutor<UserVOFetcherV2,User,UserVO>{@AutowiredprivateUserRepositoryuserRepository;@OverrideprotectedLonggetFetchId(UserVOFetcherV2fetcher){returnfetcher.getUserId();}@OverrideprotectedList<User>loadData(List<Long>ids){returnthis.userRepository.getByIds(ids);}@OverrideprotectedLonggetDataId(Useruser){returnuser.getId();}@OverrideprotectedUserVOconvertToVo(Useruser){returnUserVO.apply(user);}@OverrideprotectedvoidsetResult(UserVOFetcherV2fetcher,List<UserVO>userVO){if(CollectionUtils.isNotEmpty(userVO)){fetcher.setUser(userVO.get(0));}}@Overridepublicbooleansupport(Class<UserVOFetcherV2>cls){//临时疏忽,稍后会细讲returnUserVOFetcherV2.class.isAssignableFrom(cls);}}
UserVOFetcherExecutor终究出现什么变动呢?如同变得更复杂了:
那咱们终究失掉了什么好处?可以花几分钟好好思索一下!!!
在说结果之前,让咱们看下另一个变动点。回忆下FetcherExecutor的口头点,如下:
@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOFetcherV1>orderDetailVOS=orders.stream().map(order->newOrderListVOFetcherV1(OrderVO.apply(order))).collect(toList());//手工调用,OrderListVO成功新接口,须要参与新的依赖和调用this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){Orderorder=this.orderRepository.getById(orderId);OrderDetailVOFetcherV1orderDetail=newOrderDetailVOFetcherV1(OrderVO.apply(order));List<OrderDetailVOFetcherV1>orderDetailVOS=Arrays.asList(orderDetail);//手工调用,OrderDetailVO成功新接口,须要参与新的依赖和调用this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);this.payInfoVOFetcherExecutorV1.fetch(orderDetailVOS);returnorderDetail;}
其实,须要调用哪些FetcherExecutor齐全可以由VO成功的接口来确定。也就是说, 须要绑定新数据,只要VO承袭并成功新的Fetcher接口即可。
对此,咱们须要:
哪个设计形式是用来处置这个疑问?花几分钟好好思索一下!
答案是: 责任链模型
规范的责任链形式用起来比拟繁琐,在Spring成功中少量经常使用他的一种变现,及提供一个验证接口,由组件自身成功判别,用于选择能否口头自身逻辑。
全体设计如下:
首先,为了一致FetcherExecutor的行为,抽取通用接口:
publicinterfaceItemFetcherExecutor<FextendsItemFetcher>{/***该组件能否能处置cls类型*@paramcls*@return*/booleansupport(Class<F>cls);/***口头真正的数据绑定*@paramfetchers*/voidfetch(List<F>fetchers);}
详细的成功,可以见UserVOFetcherExecutorV2的support方法:
@Overridepublicbooleansupport(Class<UserVOFetcherV2>cls){returnUserVOFetcherV2.class.isAssignableFrom(cls);}
成功逻辑十分繁难,只是判别cls能否成功了UserVOFetcherV2接口。
有了FetcherExecutor组件后,接上去就是为其提供一致的访问入口:
@ServicepublicclassFetcherService{@AutowiredprivateList<ItemFetcherExecutor>itemFetcherExecutors;public<FextendsItemFetcher>voidfetch(Class<F>cls,List<F>fetchers){if(CollectionUtils.isNotEmpty(fetchers)){this.itemFetcherExecutors.stream()//能否能处置该类型.filter(itemFetcherExecutor->itemFetcherExecutor.support(cls))//口头真正的绑定.forEach(itemFetcherExecutor->itemFetcherExecutor.fetch(fetchers));}}}
逻辑即为繁难,依次遍历FetcherExecutor,依据support口头结果,口头fetch逻辑。
【小知识】Spring可以将容器中的所有成功间接注入到List<Bean>。在上述代码中,将会把一切的ItemFetcherExecutor成功注入到itemFetcherExecutors属性。因此,在新增FetcherExecutor时,只要将其申明为SpringBean,无需调整代码逻辑。
OK,咱们有了FetcherService提供一致的数据绑定才干,原来OrderServiceFetcher中fetch操作的变动点转移到FetcherService,自身变得十分稳固。详细如下:
@ServicepublicclassOrderServiceFetcherV2implementsOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateFetcherServicefetcherService;@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOFetcherV2>orderDetailVOS=orders.stream().map(order->newOrderListVOFetcherV2(OrderVO.apply(order))).collect(toList());//VO数据绑定出现变动,只要调整VO成功接口,此处无需变动fetcherService.fetch(OrderListVOFetcherV2.class,orderDetailVOS);returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){Orderorder=this.orderRepository.getById(orderId);OrderDetailVOFetcherV2orderDetail=newOrderDetailVOFetcherV2(OrderVO.apply(order));//VO数据绑定出现变动,只要调整VO成功接口,此处无需变动fetcherService.fetch(OrderDetailVOFetcherV2.class,Arrays.asList(orderDetail));returnorderDetail;}}
终于,咱们将变动收敛到VO内,VO须要绑定新的数据,只要成功对应接口即可。
4.3.并发绑定
经过重构,代码结构变得十清楚晰,假构想经过多线程并发方式提供性能,须要调整哪些组件呢?好好想想!!!
只要对FetcherService启动调整,让咱们来一个并发版本,详细如下:
@ServicepublicclassConcurrentFetcherService{privateExecutorServiceexecutorService;@AutowiredprivateList<ItemFetcherExecutor>itemFetcherExecutors;@PostConstructpublicvoidinit(){this.executorService=Executors.newFixedThreadPool(20);}@SneakyThrowspublic<FextendsItemFetcher>voidfetch(Class<F>cls,List<F>fetchers){if(CollectionUtils.isNotEmpty(fetchers)){//创立异步口头义务List<Callable<Void>>callables=this.itemFetcherExecutors.stream().filter(itemFetcherExecutor->itemFetcherExecutor.support(cls)).map(itemFetcherExecutor->(Callable<Void>)()->{itemFetcherExecutor.fetch(fetchers);returnnull;}).collect(Collectors.toList());//线程池中并行口头this.executorService.invokeAll(callables);}}}
OrderServiceFetcherV3只要经常使用ConcurrentFetcherService代替原来的FetcherService并领有了并发才干。
5.注解打算
5.1.复杂性能@JoinInMemory来帮助
纵观整个Fetcher封装,只管结构明晰,但细节过于繁琐,特意是:
这些不便将成为落地最大的阻碍,那有没有方法进后退一步简化?
这须要思索下这些设计面前的深层需求:
Fetcher接口目的包含
FetcherExecutor设计的指标包含:
一切这些需求能否可用==注解==的方式成功?
依据上述剖析,注解可成功所有义务,新建注解如下:
@Target({ElementType.FIELD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfaceJoinInMemory{/***从sourceData中提取key*@return*/StringkeyFromSourceData();/***从joinData中提取key*@return*/StringkeyFromJoinData();/***批量数据抓取*@return*/Stringloader();/***结果转换器*@return*/StringjoinDataConverter()default"";/***运转级别,同一级别的join可并行口头*@return*/intrunLevel()default10;}
乍一看,须要性能的消息真多,其实大少数性能所有与FetcherExecutor成功相关。
abstractclassAbstractJoinItemExecutor<SOURCE_DATA,JOIN_KEY,JOIN_DATA,JOIN_RESULT>implementsJoinItemExecutor<SOURCE_DATA>{/***从原始数据中生成JoinKey*@param>@DatapublicclassOrderDetailVOAnnV1extendsOrderDetailVO{privatefinalOrderVOorder;@JoinInMemory(keyFromSourceData="#{order.userId}",keyFromJoinData="#{id}",loader="#{@userRepository.getByIds(#root)}",joinDataConverter="#{T(com.geekhalo.lego.joininmemory.order.UserVO).apply(#root)}")privateUserVOuser;//其余临时疏忽}@DatapublicclassOrderListVOAnnV1extendsOrderListVO{privatefinalOrderVOorder;@JoinInMemory(keyFromSourceData="#{order.userId}",keyFromJoinData="#{id}",loader="#{@userRepository.getByIds(#root)}",joinDataConverter="#{T(com.geekhalo.lego.joininmemory.order.UserVO).apply(#root)}")privateUserVOuser;//其余临时疏忽}
咱们以UserVOuser属性为例
@JoinInMemory注解中少量经常使用SpEL,不相熟的同伴可以自行网上启动检索。
其余局部不变,定义OrderService如下:
@ServicepublicclassOrderServiceAnnV1implementsOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateJoinServicejoinService;@OverridepublicList<OrderListVO>getByUserId(LonguserId){List<Order>orders=this.orderRepository.getByUserId(userId);List<OrderListVOAnnV1>orderDetailVOS=orders.stream().map(order->newOrderListVOAnnV1(OrderVO.apply(order))).collect(toList());this.joinService.joinInMemory(OrderListVOAnnV1.class,orderDetailVOS);returnorderDetailVOS.stream().collect(toList());}@OverridepublicOrderDetailVOgetDetailByOrderId(LongorderId){Orderorder=this.orderRepository.getById(orderId);OrderDetailVOAnnV1orderDetail=newOrderDetailVOAnnV1(OrderVO.apply(order));this.joinService.joinInMemory(OrderDetailVOAnnV1.class,Arrays.asList(orderDetail));returnorderDetail;}}
相关于Fetcher形象,咱们将Fetcher、FetcherExecutor所有性能化,并经过注解的方式启动出现,相关于Coding打算,注解打算愈加灵敏,上班量也更小。
5.2.复杂性能@Alias来帮助
相关于Fetcher封装,一个@JoinInMemory成功干掉了两个组件,但观其自身性能起来还是十分繁琐。比如,在订单查问这个场景,在OrderListVO和OrderDetailVO中都须要对UserVO启动数据绑定,观察两个注解,咱们会发现很多重复性能:
//OrderListVO@JoinInMemory(keyFromSourceData="#{order.userId}",keyFromJoinData="#{id}",loader="#{@userRepository.getByIds(#root)}",joinDataConverter="#{T(com.geekhalo.lego.joininmemory.order.UserVO).apply(#root)}")privateUserVOuser;//OrderDetailVO@JoinInMemory(keyFromSourceData="#{order.userId}",keyFromJoinData="#{id}",loader="#{@userRepository.getByIds(#root)}",joinDataConverter="#{T(com.geekhalo.lego.joininmemory.order.UserVO).apply(#root)}")privateUserVOuser;
两特性能齐全一样,细品之后会发现:
【变动】入参变动,读取的属性不同,只是本次凑巧相反而已
【不变】处置逻辑不变
【不变】
关于不变局部如何启动一致治理?
自定义注解联合Spring@AliasFor便可以处置这个疑问,以UserVO为例:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)//治理通用属性@JoinInMemory(keyFromSourceData="",keyFromJoinData="#{id}",loader="#{@userRepository.getByIds(#root)}",joinDataConverter="#{T(com.geekhalo.lego.joininmemory.order.UserVO).apply(#root)}")public@interfaceJoinUserVOOnId{//经常使用别名将keyFromSourceData的性能暴显露来@AliasFor(annotation=JoinInMemory.class)StringkeyFromSourceData();}
新注解有如下几个特点:
有了自定义注解,经常使用变的十分繁难:
@DatapublicclassOrderListVOAnnV2extendsOrderListVO{privatefinalOrderVOorder;//只要性能参数即可,其余性能由JoinUserVOOnId启动治理@JoinUserVOOnId(keyFromSourceData="#{order.userId}")privateUserVOuser;}@DatapublicclassOrderDetailVOAnnV2extendsOrderDetailVO{privatefinalOrderVOorder;//只要性能参数即可,其余性能由JoinUserVOOnId启动治理@JoinUserVOOnId(keyFromSourceData="#{order.userId}")privateUserVOuser;}
其余经常使用方式不变,但成功了逻辑简化:
5.3.开启并发@JoinInMemoryConfig来帮助
假设担忧性能,可以一键开启并发绑定,示例如下:
@Data@JoinInMemoryConfig(executorType=JoinInMemeoryExecutorType.PARALLEL)publicclassOrderListVOAnnV3extendsOrderListVO{privatefinalOrderVOorder;@JoinUserVOOnId(keyFromSourceData="#{order.userId}")privateUserVOuser;@JoinAddressVOOnId(keyFromSourceData="#{order.addressId}")privateAddressVOaddress;@JoinProductVOOnId(keyFromSourceData="#{order.productId}")privateProductVOproduct;}
JoinInMemoryConfig性能如下:
6.最佳通常
6.1.将定义注解视为最佳通常
@JoinInMemory注解上性能的消息太多,假设间接在业务代码中经常使用,十分难以保养,当每特性能出现变动后,很难一次性性修正到位。所以,倡导只将他作为原注解经常使用。
全体思绪详见:
6.2.留意线程池隔离
关于不同的数据绑定需求,倡导经常使用不同的线程池,从资源层面对不同性能启动隔离,从而将由于依赖接口出现阻塞造成线程耗尽所形成的影响控制在最小范围。
@JoinInMemoryConfig的executorName属性性能的便是口头器称号,不性能间接经常使用defaultExecutor,详细代码如下:
@BeanpublicExecutorServicedefaultExecutor(){BasicThreadFactorybasicThreadFactory=newBasicThreadFactory.Builder().namingPattern("JoinInMemory-Thread-%d").daemon(true).build();intmaxSize=Runtime.getRuntime().availableProcessors()*3;returnnewThreadPoolExecutor(0,maxSize,60L,TimeUnit.SECONDS,newSynchronousQueue<>(),basicThreadFactory,newThreadPoolExecutor.CallerRunsPolicy());}
如需经常使用自定义线程池需:
7.小结
推导逻辑有点长不知道你get到多少,先繁难回忆一下:
大数据三大核心技术:拿数据、算数据、卖数据!
大数据的由来
对于“大数据”(Big highlight=true>增长率和多样化的信息资产。
麦肯锡全球研究所给出的定义是:一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合,具有海量的数据规模、快速的数据流转、多样的数据类型和价值密度低四大特征。
大数据技术的战略意义不在于掌握庞大的数据信息,而在于对这些含有意义的数据进行专业化处理。换而言之,如果把大数据比作一种产业,那么这种产业实现盈利的关键,在于提高对数据的“加工能力”,通过“加工”实现数据的“增值”。
从技术上看,大数据与云计算的关系就像一枚硬币的正反面一样密不可分。大数据必然无法用单台的计算机进行处理,必须采用分布式架构。它的特色在于对海量数据进行分布式数据挖掘。但它必须依托云计算的分布式处理、分布式数据库和云存储、虚拟化技术。
大数据需要特殊的技术,以有效地处理大量的容忍经过时间内的数据。适用于大数据的技术,包括大规模并行处理(MPP)数据库、数据挖掘、分布式文件系统、分布式数据库、云计算平台、互联网和可扩展的存储系统。
最小的基本单位是bit,按顺序给出所有单位:bit、Byte、KB、MB、GB、TB、PB、EB、ZB、YB、BB、NB、DB。
大数据的应用领域
大数据无处不在,大数据应用于各个行业,包括金融、 汽车 、餐饮、电信、能源、体能和 娱乐 等在内的 社会 各行各业都已经融入了大数据的印迹。
制造业,利用工业大数据提升制造业水平,包括产品故障诊断与预测、分析工艺流程、改进生产工艺,优化生产过程能耗、工业供应链分析与优化、生产计划与排程。
金融行业,大数据在高频交易、社交情绪分析和信贷风险分析三大金融创新领域发挥重大作用。
汽车 行业,利用大数据和物联网技术的无人驾驶 汽车 ,在不远的未来将走入我们的日常生活。
互联网行业,借助于大数据技术,可以分析客户行为,进行商品推荐和针对性广告投放。
电信行业,利用大数据技术实现客户离网分析,及时掌握客户离网倾向,出台客户挽留措施。
能源行业,随着智能电网的发展,电力公司可以掌握海量的用户用电信息,利用大数据技术分析用户用电模式,可以改进电网运行,合理设计电力需求响应系统,确保电网运行安全。
物流行业,利用大数据优化物流网络,提高物流效率,降低物流成本。
城市管理,可以利用大数据实现智能交通、环保监测、城市规划和智能安防。
体育娱乐 ,大数据可以帮助我们训练球队,决定投拍哪种 题财的 影视作品,以及预测比赛结果。
安全领域,政府可以利用大数据技术构建起强大的国家安全保障体系,企业可以利用大数据抵御网络攻击,警察可以借助大数据来预防犯罪。
个人生活, 大数据还可以应用于个人生活,利用与每个人相关联的“个人大数据”,分析个人生活行为习惯,为其提供更加周到的个性化服务。
大数据的价值,远远不止于此,大数据对各行各业的渗透,大大推动了 社会 生产和生活,未来必将产生重大而深远的影响。
大数据方面核心技术有哪些?
大数据技术的体系庞大且复杂,基础的技术包含数据的采集、数据预处理、分布式存储、NoSQL数据库、数据仓库、机器学习、并行计算、可视化等各种技术范畴和不同的技术层面。首先给出一个通用化的大数据处理框架,主要分为下面几个方面:数据采集与预处理、数据存储、数据清洗、数据查询分析和数据可视化。
数据采集与预处理
对于各种来源的数据,包括移动互联网数据、社交网络的数据等,这些结构化和非结构化的海量数据是零散的,也就是所谓的数据孤岛,此时的这些数据并没有什么意义,数据采集就是将这些数据写入数据仓库中,把零散的数据整合在一起,对这些数据综合起来进行分析。数据采集包括文件日志的采集、数据库日志的采集、关系型数据库的接入和应用程序的接入等。在数据量比较小的时候,可以写个定时的脚本将日志写入存储系统,但随着数据量的增长,这些方法无法提供数据安全保障,并且运维困难,需要更强壮的解决方案。
Flume NG作为实时日志收集系统,支持在日志系统中定制各类数据发送方,用于收集数据,同时,对数据进行简单处理,并写到各种数据接收方(比如文本,HDFS,Hbase等)。Flume NG采用的是三层架构:Agent层,Collector层和Store层,每一层均可水平拓展。其中Agent包含Source,Channel和 Sink,source用来消费(收集)数据源到channel组件中,channel作为中间临时存储,保存所有source的组件信息,sink从channel中读取数据,读取成功之后会删除channel中的信息。
Logstash是开源的服务器端数据处理管道,能够同时从多个来源采集数据、转换数据,然后将数据发送到您最喜欢的 “存储库” 中。一般常用的存储库是Elasticsearch。Logstash 支持各种输入选择,可以在同一时间从众多常用的数据来源捕捉事件,能够以连续的流式传输方式,轻松地从您的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。
Sqoop,用来将关系型数据库和Hadoop中的数据进行相互转移的工具,可以将一个关系型数据库(例如Mysql、Oracle)中的数据导入到Hadoop(例如HDFS、Hive、Hbase)中,也可以将Hadoop(例如HDFS、Hive、Hbase)中的数据导入到关系型数据库(例如Mysql、Oracle)中。Sqoop 启用了一个 MapReduce 作业(极其容错的分布式并行计算)来执行任务。Sqoop 的另一大优势是其传输大量结构化或半结构化数据的过程是完全自动化的。
流式计算
流式计算是行业研究的一个热点,流式计算对多个高吞吐量的数据源进行实时的清洗、聚合和分析,可以对存在于社交网站、新闻等的数据信息流进行快速的处理并反馈,目前大数据流分析工具有很多,比如开源的strom,spark streaming等。
Strom集群结构是有一个主节点(nimbus)和多个工作节点(supervisor)组成的主从结构,主节点通过配置静态指定或者在运行时动态选举,nimbus与supervisor都是Storm提供的后台守护进程,之间的通信是结合Zookeeper的状态变更通知和监控通知来处理。nimbus进程的主要职责是管理、协调和监控集群上运行的topology(包括topology的发布、任务指派、事件处理时重新指派任务等)。supervisor进程等待nimbus分配任务后生成并监控worker(jvm进程)执行任务。supervisor与worker运行在不同的jvm上,如果由supervisor启动的某个worker因为错误异常退出(或被kill掉),supervisor会尝试重新生成新的worker进程。
Zookeeper是一个分布式的,开放源码的分布式应用程序协调服务,提供数据同步服务。它的作用主要有配置管理、名字服务、分布式锁和集群管理。配置管理指的是在一个地方修改了配置,那么对这个地方的配置感兴趣的所有的都可以获得变更,省去了手动拷贝配置的繁琐,还很好的保证了数据的可靠和一致性,同时它可以通过名字来获取资源或者服务的地址等信息,可以监控集群中机器的变化,实现了类似于心跳机制的功能。
数据存储
Hadoop作为一个开源的框架,专为离线和大规模数据分析而设计,HDFS作为其核心的存储引擎,已被广泛用于数据存储。
HBase,是一个分布式的、面向列的开源数据库,可以认为是hdfs的封装,本质是数据存储、NoSQL数据库。HBase是一种Key/Value系统,部署在hdfs上,克服了hdfs在随机读写这个方面的缺点,与hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。
Phoenix,相当于一个Java中间件,帮助开发工程师能够像使用JDBC访问关系型数据库一样访问NoSQL数据库HBase。
Yarn是一种Hadoop资源管理器,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。Yarn由下面的几大组件构成:一个全局的资源管理器ResourceManager、ResourceManager的每个节点代理NodeManager、表示每个应用的Application以及每一个ApplicationMaster拥有多个Container在NodeManager上运行。
Mesos是一款开源的集群管理软件,支持Hadoop、ElasticSearch、Spark、Storm 和Kafka等应用架构。
Redis是一种速度非常快的非关系数据库,可以存储键与5种不同类型的值之间的映射,可以将存储在内存的键值对数据持久化到硬盘中,使用复制特性来扩展性能,还可以使用客户端分片来扩展写性能。
Atlas是一个位于应用程序与MySQL之间的中间件。在后端DB看来,Atlas相当于连接它的客户端,在前端应用看来,Atlas相当于一个DB。Atlas作为服务端与应用程序通讯,它实现了MySQL的客户端和服务端协议,同时作为客户端与MySQL通讯。它对应用程序屏蔽了DB的细节,同时为了降低MySQL负担,它还维护了连接池。Atlas启动后会创建多个线程,其中一个为主线程,其余为工作线程。主线程负责监听所有的客户端连接请求,工作线程只监听主线程的命令请求。
Kudu是围绕Hadoop生态圈建立的存储引擎,Kudu拥有和Hadoop生态圈共同的设计理念,它运行在普通的服务器上、可分布式规模化部署、并且满足工业界的高可用要求。其设计理念为fast analytics on fast data。作为一个开源的存储引擎,可以同时提供低延迟的随机读写和高效的数据分析能力。Kudu不但提供了行级的插入、更新、删除API,同时也提供了接近Parquet性能的批量扫描操作。使用同一份存储,既可以进行随机读写,也可以满足数据分析的要求。Kudu的应用场景很广泛,比如可以进行实时的数据分析,用于数据可能会存在变化的时序数据应用等。
在数据存储过程中,涉及到的数据表都是成千上百列,包含各种复杂的Query,推荐使用列式存储方法,比如parquent,ORC等对数据进行压缩。Parquet 可以支持灵活的压缩选项,显著减少磁盘上的存储。
数据清洗
MapReduce作为Hadoop的查询引擎,用于大规模数据集的并行计算,”Map(映射)”和”Reduce(归约)”,是它的主要思想。它极大的方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统中。
随着业务数据量的增多,需要进行训练和清洗的数据会变得越来越复杂,这个时候就需要任务调度系统,比如oozie或者azkaban,对关键任务进行调度和监控。
Oozie是用于Hadoop平台的一种工作流调度引擎,提供了RESTful API接口来接受用户的提交请求(提交工作流作业),当提交了workflow后,由工作流引擎负责workflow的执行以及状态的转换。用户在HDFS上部署好作业(MR作业),然后向Oozie提交Workflow,Oozie以异步方式将作业(MR作业)提交给Hadoop。这也是为什么当调用Oozie 的RESTful接口提交作业之后能立即返回一个JobId的原因,用户程序不必等待作业执行完成(因为有些大作业可能会执行很久(几个小时甚至几天))。Oozie在后台以异步方式,再将workflow对应的Action提交给hadoop执行。
Azkaban也是一种工作流的控制引擎,可以用来解决有多个hadoop或者spark等离线计算任务之间的依赖关系问题。azkaban主要是由三部分构成:Relational Database,Azkaban Web Server和Azkaban Executor Server。azkaban将大多数的状态信息都保存在MySQL中,Azkaban Web Server提供了Web UI,是azkaban主要的管理者,包括project的管理、认证、调度以及对工作流执行过程中的监控等;Azkaban Executor Server用来调度工作流和任务,记录工作流或者任务的日志。
流计算任务的处理平台Sloth,是网易首个自研流计算平台,旨在解决公司内各产品日益增长的流计算需求。作为一个计算服务平台,其特点是易用、实时、可靠,为用户节省技术方面(开发、运维)的投入,帮助用户专注于解决产品本身的流计算需求
数据查询分析
Hive的核心工作就是把SQL语句翻译成MR程序,可以将结构化的数据映射为一张数据库表,并提供 HQL(Hive SQL)查询功能。Hive本身不存储和计算数据,它完全依赖于HDFS和MapReduce。可以将Hive理解为一个客户端工具,将SQL操作转换为相应的MapReduce jobs,然后在hadoop上面运行。Hive支持标准的SQL语法,免去了用户编写MapReduce程序的过程,它的出现可以让那些精通SQL技能、但是不熟悉MapReduce 、编程能力较弱与不擅长Java语言的用户能够在HDFS大规模数据集上很方便地利用SQL 语言查询、汇总、分析数据。
Hive是为大数据批量处理而生的,Hive的出现解决了传统的关系型数据库(MySql、Oracle)在大数据处理上的瓶颈 。Hive 将执行计划分成map->shuffle->reduce->map->shuffle->reduce…的模型。如果一个Query会被编译成多轮MapReduce,则会有更多的写中间结果。由于MapReduce执行框架本身的特点,过多的中间过程会增加整个Query的执行时间。在Hive的运行过程中,用户只需要创建表,导入数据,编写SQL分析语句即可。剩下的过程由Hive框架自动的完成。
Impala是对Hive的一个补充,可以实现高效的SQL查询。使用Impala来实现SQL on Hadoop,用来进行大数据实时查询分析。通过熟悉的传统关系型数据库的SQL风格来操作大数据,同时数据也是可以存储到HDFS和HBase中的。Impala没有再使用缓慢的Hive+MapReduce批处理,而是通过使用与商用并行关系数据库中类似的分布式查询引擎(由Query Planner、Query Coordinator和Query Exec Engine三部分组成),可以直接从HDFS或HBase中用SELECT、JOIN和统计函数查询数据,从而大大降低了延迟。Impala将整个查询分成一执行计划树,而不是一连串的MapReduce任务,相比Hive没了MapReduce启动时间。
Hive 适合于长时间的批处理查询分析,而Impala适合于实时交互式SQL查询,Impala给数据人员提供了快速实验,验证想法的大数据分析工具,可以先使用Hive进行数据转换处理,之后使用Impala在Hive处理好后的数据集上进行快速的数据分析。总的来说:Impala把执行计划表现为一棵完整的执行计划树,可以更自然地分发执行计划到各个Impalad执行查询,而不用像Hive那样把它组合成管道型的map->reduce模式,以此保证Impala有更好的并发性和避免不必要的中间sort与shuffle。但是Impala不支持UDF,能处理的问题有一定的限制。
Spark拥有Hadoop MapReduce所具有的特点,它将Job中间输出结果保存在内存中,从而不需要读取HDFS。Spark 启用了内存分布数据集,除了能够提供交互式查询外,它还可以优化迭代工作负载。Spark 是在 Scala 语言中实现的,它将 Scala 用作其应用程序框架。与 Hadoop 不同,Spark 和 Scala 能够紧密集成,其中的 Scala 可以像操作本地集合对象一样轻松地操作分布式数据集。
Nutch 是一个开源Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具,包括全文搜索和Web爬虫。
Solr用Java编写、运行在Servlet容器(如Apache Tomcat或Jetty)的一个独立的企业级搜索应用的全文搜索服务器。它对外提供类似于Web-service的API接口,用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Elasticsearch
Elasticsearch是一个开源的全文搜索引擎,基于Lucene的搜索服务器,可以快速的储存、搜索和分析海量的数据。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
还涉及到一些机器学习语言,比如,Mahout主要目标是创建一些可伸缩的机器学习算法,供开发人员在Apache的许可下免费使用;深度学习框架Caffe以及使用数据流图进行数值计算的开源软件库TensorFlow等,常用的机器学习算法比如,贝叶斯、逻辑回归、决策树、神经网络、协同过滤等。
数据可视化
对接一些BI平台,将分析得到的数据进行可视化,用于指导决策服务。主流的BI平台比如,国外的敏捷BI Tableau、Qlikview、PowrerBI等,国内的SmallBI和新兴的网易有数等。
在上面的每一个阶段,保障数据的安全是不可忽视的问题。
基于网络身份认证的协议Kerberos,用来在非安全网络中,对个人通信以安全的手段进行身份认证,它允许某实体在非安全网络环境下通信,向另一个实体以一种安全的方式证明自己的身份。
控制权限的ranger是一个Hadoop集群权限框架,提供操作、监控、管理复杂的数据权限,它提供一个集中的管理机制,管理基于yarn的Hadoop生态圈的所有数据权限。可以对Hadoop生态的组件如Hive,Hbase进行细粒度的数据访问控制。通过操作Ranger控制台,管理员可以轻松的通过配置策略来控制用户访问HDFS文件夹、HDFS文件、数据库、表、字段权限。这些策略可以为不同的用户和组来设置,同时权限可与hadoop无缝对接。
简单说有三大核心技术:拿数据,算数据,卖数据。
什么是大数据技术?大数据的概念
大数据技术是指大数据的应用技术,涵盖各类大数据平台、大数据指数体系等大数据应用技术。
大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合。是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
随着云时代的来临,大数据也吸引了越来越多的关注。分析师团队认为,大数据通常用来形容一个公司创造的大量非结构化数据和半结构化数据,这些数据在下载到关系型数据库用于分析时会花费过多时间和金钱。
大数据分析常和云计算联系到一起,因为实时的大型数据集分析需要像MapReduce一样的框架来向数十、数百或甚至数千的电脑分配工作。
扩展资料:
大数据的三个层面:
1、理论,理论是认知的必经途径,也是被广泛认同和传播的基线。在这里从大数据的特征定义理解行业对大数据的整体描绘和定性;从对大数据价值的探讨来深入解析大数据的珍贵所在;洞悉大数据的发展趋势;从大数据隐私这个特别而重要的视角审视人和数据之间的长久博弈。
2、技术,技术是大数据价值体现的手段和前进的基石。在这里分别从云计算、分布式处理技术、存储技术和感知技术的发展来说明大数据从采集、处理、存储到形成结果的整个过程。
3、实践,实践是大数据的最终价值体现。在这里分别从互联网的大数据,政府的大数据,企业的大数据和个人的大数据四个方面来描绘大数据已经展现的美好景象及即将实现的蓝图。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。