1 背景

使用场景:计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。

高并发下,为提高 频繁 查询 大量 可能常用的 数据库数据的 查询效率。

大部分情况下,单机用Google Guava(Cache/LoadCache) / ehcache,分布式用redismemcache,各有各的好处,现在企业都是应用很多种中间件供后端程序员选择。

2 缓存技术

什么是缓存?

1 - Cache是高速缓冲存储器 一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问

2 - 凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为 Cache

3 - 缓存技术设计思想: 典型的空间换时间

2-1 分类

  • 操作系统磁盘缓存(加速/减少磁盘机械操作) / 数据库缓存(加速/减少访问文件系统I/O) / 【应用程序缓存】(加快/减少对数据库的查询) / Web服务器缓存(加速/减少应用服务器请求) / 浏览器缓存(加速/减少对网站的访问)

  • 分布式缓存 / 本地缓存

  • 介质: 基于内存缓存 / 基于磁盘缓存 / 基于中间件[数据库]缓存(Redis/Memcache/...,本质:内存+磁盘) / 基于JVM缓存(本质:基于内存)

2-2 缓存开源组件

  1. OSCache / Java Caching System(JCS) / / JCache / ShiftOne / SwarmCache / TreeCache / JBossCache / WhirlyCache
  2. EHCache
  3. Google Guava(核心类: Cache/LoadingCache;内存/JVM/本地缓存; Spring5之后,官方放弃Guava改用Caffeine)
  4. Caffeine

2-3 缓存的指标

  • 命中率
  • 最大容量
  • 清空策略(过期策略)

先进先出算法(FIFO)

first in first out ,最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去

最不经常使用算法(LFU)

Less Frequently Used ,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存

最近最少使用算法(LRU)

Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存

最近最常使用算法(MRU)

这个缓存算法最先移除最近最常使用的条目。一个MRU算法擅长处理一个条目越久,越容易被访问的情况。

自适应缓存替换算法(ARC)

在IBM Almaden研究中心开发,这个缓存算法同时跟踪记录LFU和LRU,以及驱逐缓存条目,来获得可用缓存的最佳使用。

2-4 基于JVM缓存的实现方案

  • 方案1: HashMap / CocurrentHashMap
  • 方案2: 开源组件(Google Guava: Cache / LoadingCache)
  1. Cache/LoadingCache 均继承自 CocurrentHashMap

2-5 缓存产生的问题

  • Q1: 缓存数据与源数据一致性问题(数据同步)
  1. 解决方法
  2. 1) write back(写回策略): 更新数据源数据时,只更新缓存的数据。当缓存需要被替换(挤出)时,才将缓存中更新的值写回磁盘。
  3. 在写回策略中,为了减少写操作,缓存数据单元通常还设有1个脏位(dirty bit),用于标识该块在被载入后,是否发生过更新。
  4. 1个缓存数据单元在被置换回内存之前,从未被写入过,则:可以免去回写操作;
  5. 写回的优点是:节省了大量的写操作
  6. 2) write through(写通策略): 更新数据源数据时,同时更新缓存的数据。
  • Q2: 缓存数据存放时间问题
  • Q3: 缓存的多线程并发控制问题

3 基于Google Guava开源组件的JVM缓存实现

需求背景: 一项目中多个接口、频繁地批量查询数据库一类数据————发布的数据服务信息,又要求3s内立即做出响应。 (存在高并发问题)

关于 Google Guava 开源缓存组件: google guava中有cache包,此包提供内存缓存功能。内存缓存需要考虑很多问题,包括并发问题,缓存失效机制,内存不够用时缓存释放,缓存的命中率,缓存的移除等等。 当然这些东西guava都考虑到了。

guava中使用缓存需要先声明一个CacheBuilder对象,并设置缓存的相关参数,然后调用其build方法获得一个Cache接口的实例。请看下面的代码和注释,注意在注释中指定了Cache的各个参数。

3-0 Maven依赖

  1. <dependency>
  2. <groupId>com.google.guava</groupId>
  3. <artifactId>guava</artifactId>
  4. <version>28.0-jre</version>
  5. </dependency>

3-1 IDataServiceInfoCacheService

  1. package xxx.service;
  2. import com.google.common.cache.LoadingCache;
  3. import com.yyy.DataServiceInfo;
  4. import java.util.Map;
  5. /**
  6. * @date: 2020/11/12 16:58:05
  7. * @description: 缓存数据服务信息
  8. */
  9. public interface IDataServiceInfoCacheService {
  10. /**
  11. * 从缓存中 获取 数据服务信息
  12. * 若缓存中不存在该信息,将自动从数据库中加载,再返回
  13. * @param serviceId
  14. * @return
  15. * @throws Exception
  16. */
  17. public DataServiceInfo get(String serviceId) throws Exception;
  18. //public void put(String serviceId, DataServiceInfo dataServiceInfo);
  19. //public void putAll(Map<? extends String, ? extends DataServiceInfo> dataServiceInfoMap);
  20. public long size();
  21. public void remove(String serviceId);
  22. public void removeAll(Iterable<Long> serviceIds);
  23. public void removeAll();
  24. }

3-2 DataServiceCacheServiceImpl

  1. package xxx.service.impl;
  2. import com.xxx..Dept;
  3. import com.xxx.ServerSystem;
  4. import com.xxx.BusinessException;
  5. import com.google.common.cache.*;
  6. import com.xxx.BmsCacheService;
  7. import com.xxx.LoggerUtil;
  8. import com.xxx.Tools;
  9. import com.xxx.DataServiceInfo;
  10. import com.xxx.DataServiceInfoCacheService;
  11. import com.xxx.ServiceInfoMapper;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Service;
  14. import org.springframework.transaction.annotation.Transactional;
  15. import javax.annotation.PostConstruct;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. import java.util.concurrent.TimeUnit;
  19. /**
  20. * @date: 2020/11/12 17:28:03
  21. * @description: ...
  22. */
  23. @Service
  24. public class DataServiceCacheServiceImpl implements IDataServiceInfoCacheService {
  25. private static LoadingCache<String, DataServiceInfo> DATA_SERVICE_INFO_CACHE;
  26. @Autowired
  27. private ServiceInfoMapper serviceInfoMapper;
  28. @Autowired
  29. private BmsCacheService bmsCacheService;
  30. @PostConstruct // 解决 【静态变量】初始化时调用【实例方法】问题
  31. public void init() {
  32. DATA_SERVICE_INFO_CACHE = CacheBuilder
  33. .newBuilder() ////CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
  34. .concurrencyLevel(8) // //8个segment,分段锁8;设置并发级别为8,并发级别是指可以同时写缓存的线程数
  35. .expireAfterWrite(120, TimeUnit.SECONDS)// 设置写缓存后120秒钟过期 (缓存在写缓存后的指定时间内没有被新的值覆盖时,将失效) 【expire是指定时间过后,expire是remove该key,下次访问是发起同步请求以返回获取到新值】
  36. //.expireAfterAccess(120, TimeUnit.SECONDS)// 设置读缓存后120秒钟过期 (缓存在读缓存后的指定时间内没有被读写时,将失效)
  37. .refreshAfterWrite(120, TimeUnit.SECONDS) // 设置写缓存后120秒钟刷新 【refresh是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值】
  38. //.refreshAfterAccess(120, TimeUnit.SECONDS) // 设置读缓存后120秒钟刷新
  39. //使用弱引用存储键。当没有(强或软)引用到该键时,相应的缓存项将可以被垃圾回收。由于垃圾回收是依赖==进行判断,因此这样会导致整个缓存也会使用==来比较键的相等性,而不是使用equals()
  40. .weakKeys()
  41. //使用弱引用存储缓存值。当没有(强或软)引用到该缓存项时,将可以被垃圾回收。由于垃圾回收是依赖==进行判断,因此这样会导致整个缓存也会使用==来比较缓存值的相等性,而不是使用equals()
  42. .weakValues()
  43. .initialCapacity(1000)// 设置缓存容器的初始容量为1000
  44. .maximumSize(10000)// 设置缓存最大容量为10000,超过10000之后就会按照LRU最近虽少使用算法来移除缓存项
  45. .recordStats()// 设置要统计缓存的命中率
  46. .removalListener(getRemovalListener())// 设置缓存的移除通知(移除时的触发操作)
  47. .build(getCacheLoader());// build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
  48. }
  49. @Transactional(readOnly = true)
  50. protected Map<String, Object> getSourceSystemInfo(String serviceId) throws Exception {
  51. Map<String, Object> data = new HashMap<String, Object>();
  52. Map<String, Object> provides = serviceInfoMapper.getServiceProvideSystems(serviceId);
  53. if (provides != null) {
  54. if (provides.get("provideSystemId") != null) {
  55. String provideSystemIds = provides.get("provideSystemId").toString();
  56. String[] provideIds = provideSystemIds.split(",");
  57. StringBuilder systemNames = new StringBuilder();
  58. for (String id : provideIds) {
  59. ServerSystem sys = bmsCacheService.getSysById(id);
  60. if (sys != null) {
  61. systemNames.append(sys.getSystemName()).append(",");
  62. }
  63. }
  64. data.put("provideSystemIds", provideSystemIds);
  65. if (provideIds.length > 0) {
  66. data.put("provideSystemNames", systemNames.deleteCharAt(systemNames.length() - 1));
  67. }
  68. }
  69. if (provides.get("deptId") != null) {
  70. String deptIds = provides.get("deptId").toString();
  71. String[] provideIds = deptIds.split(",");
  72. StringBuilder departNames = new StringBuilder();
  73. for (String id : provideIds) {
  74. Dept dept = bmsCacheService.getDeptById(id);
  75. if (dept != null) {
  76. departNames.append(dept.getDeptName()).append(",");
  77. }
  78. }
  79. data.put("provideDepartIds", deptIds);
  80. if (provideIds.length > 0) {
  81. data.put("provideDepartNames", departNames.deleteCharAt(departNames.length() - 1));
  82. }
  83. }
  84. }
  85. return data;
  86. }
  87. @Transactional(readOnly = true)
  88. protected DataServiceInfo loadDataServiceInfo(String serviceId) throws Exception {
  89. DataServiceInfo dataServiceInfo = new DataServiceInfo();
  90. Map<String, Object> sourceSystemInfo = null;
  91. sourceSystemInfo = this.getSourceSystemInfo(serviceId);
  92. Map<String, String> serviceAndCatalogInfoMap = null;
  93. serviceAndCatalogInfoMap = serviceInfoMapper.getServiceInfoAndCatalogInfoById(serviceId);
  94. if (Tools.isNull(sourceSystemInfo) && Tools.isNull(serviceAndCatalogInfoMap)) {//通过 serviceId,均未查找到 数据服务信息
  95. String errorMsg = "根据所提供的数据服务编号,未能查找到数据服务信息!";
  96. LoggerUtil.error(LoggerUtil.DATASERVICE_MNG_CORE_LOGGER_INSTANCE, String.format(errorMsg + " [serviceId: %s]", serviceId));
  97. throw new BusinessException(errorMsg);
  98. }
  99. dataServiceInfo.setServiceId(serviceId);
  100. if (Tools.isNotNull(sourceSystemInfo)) {
  101. dataServiceInfo.setProvideDepartIds(Tools.isNotNull(sourceSystemInfo.get("provideDepartIds")) ? sourceSystemInfo.get("provideDepartIds").toString() : "");
  102. dataServiceInfo.setProvideDepartNames(Tools.isNotNull(sourceSystemInfo.get("provideDepartNames")) ? sourceSystemInfo.get("provideDepartNames").toString() : "");
  103. dataServiceInfo.setProvideSystemIds(Tools.isNotNull(sourceSystemInfo.get("provideSystemIds")) ? sourceSystemInfo.get("provideSystemIds").toString() : "");
  104. dataServiceInfo.setProvideSystemNames(Tools.isNotNull(sourceSystemInfo.get("provideSystemNames")) ? sourceSystemInfo.get("provideSystemNames").toString() : "");
  105. }
  106. if (Tools.isNotNull(serviceAndCatalogInfoMap)) {
  107. dataServiceInfo.setCatalogId(Tools.isNotNull(serviceAndCatalogInfoMap.get("catalogId")) ? serviceAndCatalogInfoMap.get("catalogId").toString() : "");
  108. dataServiceInfo.setCatalogName(Tools.isNotNull(serviceAndCatalogInfoMap.get("catalogName")) ? serviceAndCatalogInfoMap.get("catalogName").toString() : "");
  109. dataServiceInfo.setServiceName(Tools.isNotNull(serviceAndCatalogInfoMap.get("serviceName")) ? serviceAndCatalogInfoMap.get("serviceName").toString() : "");
  110. dataServiceInfo.setTableUnicode(Tools.isNotNull(serviceAndCatalogInfoMap.get("tableUnicode")) ? serviceAndCatalogInfoMap.get("tableUnicode").toString() : "");
  111. }
  112. return dataServiceInfo;
  113. }
  114. private RemovalListener<Object, Object> getRemovalListener() {
  115. return new RemovalListener<Object, Object>() {
  116. public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
  117. String removeLog = removalNotification.getKey() + " was removed, cause is " + removalNotification.getCause();
  118. LoggerUtil.info(LoggerUtil.DATASERVICE_MNG_CORE_LOGGER_INSTANCE, removeLog);
  119. }
  120. };
  121. }
  122. private CacheLoader getCacheLoader() {
  123. return new CacheLoader<String, DataServiceInfo>() {
  124. @Override
  125. public DataServiceInfo load(String serviceId) throws Exception {// 处理缓存键不存在缓存值时的重新获取最新缓存值的处理逻辑
  126. LoggerUtil.info(LoggerUtil.DATASERVICE_MNG_CORE_LOGGER_INSTANCE, "[dataServiceInfoCache] loading dataService is: " + serviceId);
  127. return loadDataServiceInfo(serviceId);
  128. }
  129. };
  130. }
  131. @Override
  132. public DataServiceInfo get(String serviceId) throws Exception {
  133. DataServiceInfo dataServiceInfo = null;
  134. dataServiceInfo = DATA_SERVICE_INFO_CACHE.get(serviceId);
  135. if (Tools.isNull(dataServiceInfo)) {
  136. dataServiceInfo = loadDataServiceInfo(serviceId);
  137. if (Tools.isNotNull(dataServiceInfo)) {
  138. DATA_SERVICE_INFO_CACHE.put(serviceId, dataServiceInfo);
  139. }
  140. return dataServiceInfo;
  141. }
  142. return dataServiceInfo;
  143. }
  144. /**
  145. * @Override public void put(String serviceId, DataServiceInfo dataServiceInfo) {
  146. * DATA_SERVICE_INFO_CACHE.put(serviceId, dataServiceInfo);
  147. * }
  148. * @Override public void putAll(Map<? extends String, ? extends DataServiceInfo> dataServiceInfoMap) {
  149. * DATA_SERVICE_INFO_CACHE.putAll(dataServiceInfoMap);
  150. * }
  151. */
  152. @Override
  153. public long size() {
  154. return DATA_SERVICE_INFO_CACHE.size();
  155. }
  156. @Override
  157. public void remove(String serviceId) {
  158. DATA_SERVICE_INFO_CACHE.invalidate(serviceId);
  159. }
  160. @Override
  161. public void removeAll(Iterable<Long> serviceIds) {
  162. DATA_SERVICE_INFO_CACHE.invalidateAll(serviceIds);
  163. }
  164. @Override
  165. public void removeAll() {
  166. DATA_SERVICE_INFO_CACHE.invalidateAll();
  167. }
  168. }

3-3 补充

Guava的其它API

另外Guava还提供了下面一些方法,来方便各种需要:

  1. /**
  2. * 该接口的实现被认为是线程安全的,即可在多线程中调用
  3. * 通过被定义单例使用
  4. */
  5. public interface Cache<K, V> {
  6. /**
  7. * 通过key获取缓存中的value,若不存在直接返回null
  8. */
  9. V getIfPresent(Object key);
  10. /**
  11. * 一次获得多个键的缓存值
  12. */
  13. ImmutableMap<K, V> getAllPresent(Iterable<?> var1);
  14. /**
  15. * 获得缓存数据的ConcurrentMap<K, V>快照
  16. */
  17. ConcurrentMap<K, V> asMap()
  18. /**
  19. * 通过key获取缓存中的value,若不存在就通过valueLoader来加载该value
  20. * 整个过程为 "if cached, return; otherwise create, cache and return"
  21. * 注意valueLoader要么返回非null值,要么抛出异常,绝对不能返回null
  22. */
  23. V get(K key, Callable<? extends V> valueLoader) throws ExecutionException;
  24. /**
  25. * 添加缓存,若key存在,就覆盖旧值
  26. */
  27. void put(K key, V value);
  28. /**
  29. * 刷新缓存,即重新取缓存数据,更新缓存
  30. */
  31. void refresh(K key)
  32. /**
  33. * 从缓存中移除缓存项;删除该key关联的缓存
  34. */
  35. void invalidate(Object key);
  36. /**
  37. * 从缓存中移除缓存项;删除所有缓存
  38. */
  39. void invalidateAll();
  40. /**
  41. * 清理缓存 。执行一些维护操作,包括清理缓存
  42. */
  43. void cleanUp();
  44. }

性能测试

  • 配置信息
  1. jdk: 1.8
  2. 本机电脑测试:
  3. database: mysql 5.7 / 物理表 32条数据
  4. Guava-Config
  5. int concurrencyLevel = 8;
  6. long expireAfterWriteTime = 300;
  7. long refreshAfterWriteTime = 600;
  8. int initialCapacity = 100;
  9. int maximumSize = 100;
  • 查询性能测试

    0.125000s --> 0.015000s (0.125/0.015≈8.3)
  1. 【首次查询(无缓存)】
  2. Johnny@LAPTOP-RFPOFJM7 MINGW64 /xx/share-portal (dev)
  3. $ curl -so /tmp/sdc-tmp-data/tmpfile.json -w ' namelookup: %{time_namelookup}
  4. > connect: %{time_connect}
  5. > appconnect: %{time_appconnect}
  6. > pretransfer: %{time_pretransfer}
  7. > redirect: %{time_redirect}
  8. > starttransfer: %{time_starttransfer}
  9. > -------
  10. > total: %{time_total}
  11. > ' http://localhost:18181/backend/access-log/v1/accessStatisticOverview
  12. namelookup: 0.016000
  13. connect: 0.016000
  14. appconnect: 0.000000
  15. pretransfer: 0.016000
  16. redirect: 0.000000
  17. starttransfer: 0.125000
  18. -------
  19. total: 0.125000
  20. 【第二/三/四/五/六/...次查询(有缓存)】
  21. Johnny@LAPTOP-RFPOFJM7 MINGW64 /xx/share-portal (dev)
  22. $ curl -so /tmp/sdc-tmp-data/tmpfile.json -w ' namelookup: %{time_namelookup}
  23. > connect: %{time_connect}
  24. > appconnect: %{time_appconnect}
  25. > pretransfer: %{time_pretransfer}
  26. > redirect: %{time_redirect}
  27. > starttransfer: %{time_starttransfer}
  28. > -------
  29. > total: %{time_total}
  30. > ' http://localhost:18181/backend/access-log/v1/accessStatisticOverview
  31. namelookup: 0.015000
  32. connect: 0.015000
  33. appconnect: 0.000000
  34. pretransfer: 0.015000
  35. redirect: 0.000000
  36. starttransfer: 0.015000
  37. -------
  38. total: 0.015000 (第二/三/四/五/六/...次查询均为此结果)

X 参考与推荐文献

[Java EE]缓存技术初探的更多相关文章

  1. Java EE开发技术课程

    新的学期开始了,j2e已经上了两节课,接下来就是对该课程的一些作业以及相关的认识: 一.课程目标: Java EE是java的企业级应用,所以在我看来在学习这门课程之前肯定要对java有一个具体的认识 ...

  2. 《Java EE 开发技术与案例教程》 这是一本好书啊:简洁精辟(相见恨晚)

    第一章:Java EE 概述 1.get:JPA:Java Persistence API, 数据持久化API: JPA是一种ORM规范,它的实现实例:Hibernate.mybatis 2.Web ...

  3. java动态缓存技术:WEB缓存应用(转)

    可以实现不等待,线程自动更新缓存 Java动态缓存jar包请下载. 源代码: CacheData.java 存放缓存数据的Bean /** *  */package com.cari.web.cach ...

  4. Java EE开发技术课程第六周(jsf、facelets)

    1.jsf(java sever faces) 1.1 jsf的定义: jsf是一种用于构建java web应用程序的框架.它提供了一种以组件为中心的用户界面(UI)构建方法,从而简化了Java服务器 ...

  5. Java EE开发技术课程第五周(Applet程序组件与AJAX技术)

    1.Applet程序组件 1.1.定义: Applet是采用Java编程语言编写的小应用程序,该程序可以包含在HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同.含有Apple ...

  6. Java EE开发技术课程第三周

    一.分析Filter例子: @WebFilter(filterName="log",urlPatterns={"/*"})//创建一个LOgFilter类pub ...

  7. Java EE会话技术Cookie和Session

    会话技术 一.定义 会话技术是帮助服务器记住客户端状态的(区分客户端的).将客户访问的信息存在本地的叫Cookie技术,存在服务器上的叫Session技术. 注意: 一次会话何时开始?从打开一个浏览器 ...

  8. Java EE开发技术课程第七周(json)

    JSON: https://baike.baidu.com/item/JSON/2462549?fr=aladdin JSON指JavaScript对象表示法(JavaScript Object No ...

  9. 最重要的 Java EE 最佳实践

    參考:IBM WebSphere 开发人员技术期刊: 最重要的 Java EE 最佳实践 IBM WebSphere 开发人员技术期刊: 最重要的 Java EE 最佳实践 2004 年 IBM® W ...

  10. 各种容器与服务器的区别与联系:Servlet容器、WEB容器、Java EE容器、应用服务器、WEB服务器、Java EE服务器

    1.容器与服务器的联系 如上图,我们先来看下容器与服务器的联系:容器是位于应用程序/组件和服务器平台之间的接口集合,使得应用程序/组件可以方便部署到服务器上运行. 2.各种容器的区别/联系 2-1.容 ...

随机推荐

  1. python基于word模板批量生成word文件

    1.需要用到docxtpl库,用于操作word模板 安装:pip insatll docxtpl 处理之前的word模板 处理后的word 下面直接上代码揭开它的神秘面纱:第一步,读取excel中的内 ...

  2. Long类型转换为IP String

    package com.barry.iputil.util; public class IPFormat { public static String toIPStr(Long LongIP) { i ...

  3. nodejs中router的使用

    一.划分文件使用Router 创建一个routes目录,专门用于放置路由文件,通过module.exports导出供外部使用. // 引入类 const Koa =require('koa'); // ...

  4. 华为服务器修改ibmc账号密码、配置raid5、安装系统

    修改ibmc账号密码 转载自:https://www.cnblogs.com/mtactor/p/2288V5.html  昵称: mtactor 方法一:采用网线直连管理口 1.使用网线直接连接服务 ...

  5. win10启动和安装nacos服务

    https://blog.csdn.net/tbmingzhao/article/details/113276845

  6. CF1422

    CF1422 那个博客搭好遥遥无期. A: 看代码就行. #include<bits/stdc++.h> using namespace std; void work() { int a, ...

  7. toLua文件夹结构

    写在前面 本文是我对toLua(1.0.8.591版本)文件夹内容理解的记录. 文件夹结构 总览 下图是toLua的Unity工程视图: BaseType 基础类型的Wrap文件,有些是自动生成(即用 ...

  8. MySql5.7基础配置

    MySql5.7基础配置 [client] #设置mysql客户端的字符集 default-character-set=utf8 [mysqld] #设置mysql端口为3306 port = 330 ...

  9. Glinux 1395 build ENV setup

    1.安装ubuntu 14.04.05 LTS 64bit 2.初始化root 密码 sudo passwd  Password: <--- 输入安装时那个用户的密码  Enter new UN ...

  10. Apache Ranger系列七:Hive 和 Spark 执行过程中的文件路径配置

    背景:在使用Ranger鉴权的过程中,要求必须开启impersonation功能(即执行用户与提交用户保持一致,而不是统一代理的hive/spark).但是在执行的过程中,会需要在hdfs存储临时的文 ...