PS:所有的代码使用的是Ignite2.0

#1.数据的基本存储操作

public static void main(String[] args) {//Ignition.start(...)启动一个节点try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){//创建一个Cache实例try(IgniteCache<Long, String> defaultCache = ignite.getOrCreateCache("DEFAULT_CACHE")){//1.简单插入与查询//insert插入long currentTimeMillis = System.currentTimeMillis();defaultCache.put(currentTimeMillis, "piemon");//查询String value = defaultCache.get(currentTimeMillis);System.out.println("查询操作  key:" + currentTimeMillis + "  value:" + value);//2.简单替换、boolean replaceFlag1 = defaultCache.replace(currentTimeMillis, "piemon", "anokata");boolean replaceFlag2 = defaultCache.replace(currentTimeMillis, "piemon", "anokata");assert replaceFlag1 == Boolean.TRUE;assert replaceFlag1 == Boolean.FALSE;//entryProcessordefaultCache.invoke(currentTimeMillis, (entry,argus) -> {Long eKey = entry.getKey();String eValue = entry.getValue();System.out.println("entityProcessor操作  key:" + eKey + "   value:" + eValue);//modify entryentry.setValue("default");return null;});//检查下最终的key值defaultCache.getAsync(currentTimeMillis).listen((future) -> {String endingValue = future.get();System.out.println("最终  key:"+ currentTimeMillis + "   value:" + endingValue);});}}}
//日志
[10:42:25] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB]
查询操作  key:1498617745690  value:piemon
entityProcessor操作  key:1498617745690   value:anokata
最终  key:1498617745690   value:default
[10:42:25] Ignite node stopped OK [uptime=00:00:00:327]

1.1 Ignite实例

注:这里主要讲的是对于数据网格的操作,重点不是Ignite的启动,我之后会在另外的一篇里,Debug追踪Ignite启动。
先用工具类Ignition的cache(name)获取可以操作所有API的Ignite实例,他是所有ignite API的主入口点。注意,在同一个虚拟机上,你可以指定不同的名字,而获取到多个Ignite实例。

1.2 缓存实例

获取一个IgniteCache实例,你可以将这个对象想象成我们在关系型数据库中的table这一层结构,即表的概念。

1.3 插入/查询

获取缓存实例的时候,我们已经指定了该缓存知识存储键(Integer)值(String)的"表结构".

方法 介绍
put(key,value) 向缓存中加入键值对,其实这里你可以将这个缓存想象为一个Map结构,其实底层也是Entry来存储的键值对。
get(key) 从缓存中取出该键对应的值
getAsync(key) 异步获取,返回值是IgniteFuture,可以为其绑定监听器,该监听器会在调用成功后执行。

1.4 替换操作

方法 介绍
replace(key,oldValue,newValue) 这个方法有几个多态版本,比较容易看懂,这个方法好处是可以实现乐观锁所要达到的目的,只有当当前值是oldValue时候,才以newValue进行替换,并返回True,否则,返回False.

1.5 EntryProcessor

如果有学过网格数据库的,应该不陌生,

方法 介绍
invoke( K key, CacheEntryProcessor<K, V, T> entryProcessor, Object… arguments) throws TransactionException; 官方的解释比较生硬,通俗而言:我们在对某个缓存中的值做修改的时候,一般是先查询,然后将其改成新的值,然后再交给缓存,也就是说通过网络发送完整的对象状态。但是EntryProcessor则不同。参数一:Key,帮助定位该值所在的节点。 参数二:CacheEntryProcessor<K,V,T>:真正的执行函数,即想对根据参数一找到的缓存条目Entry,做什么操作。参数三:附加参数。最终的方法执行的结果就是针对传入的键,在保存该键的服务器上,执行你传入的参数二的方法。

2. 批量操作

try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){try(IgniteCache<Integer, String> defaultCache = ignite.getOrCreateCache("DEFAULT_CACHE");){//putAllMap<Integer,String> caches = new TreeMap<>();for(int i = 0;i < 10;i++){caches.put(i, String.valueOf(i));}defaultCache.putAll(caches);//getAllMap<Integer, String> values = defaultCache.getAll(caches.keySet());values.entrySet().stream().forEach((entry) -> {System.out.println("key:" + entry.getKey() + "   value:" + entry.getValue());});}}
//日志
[14:23:03] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB]
key:0   value:0
key:1   value:1
key:2   value:2
key:3   value:3
key:4   value:4
key:5   value:5
key:6   value:6
key:7   value:7
key:8   value:8
key:9   value:9

2.1 批量添加

方法 介绍
putAll(Map) 将Map传递给该方法,批量的添加
putAllAsync(Map) 异步复制指定的到缓存的映射到的所有条目

2.2 批量获取

方法 介绍
getAll(Set<>) 传递键的Set集合,批量的查询

3. 事务操作

注意:Ignite默认情况下是以CacheAtomicityMode.ATOMIC模式,其实是不支持事务的,但是Ignite是会维护最终一致性的。所以,这时的系统其实是CAP下的AP模式,与绝大多数的内存数据库一样。

我们可以在CacheConfiguration中通过setAtomicityMode设置为TRANSACTIONAL事务模式,此时的系统就是CAP下的CP模式,它具有事务功能,Ignite也维护其最终一致性。

     try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){//配置缓存CacheConfiguration<String, Account> cfg = new CacheConfiguration<>("TRANSACTION_CACHE");//设置事务模式cfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);try(IgniteCache<String, Account> transactionCache = ignite.getOrCreateCache(cfg);){//0.先给缓存添加两个账户transactionCache.put("piemon", new Account(1,20000));transactionCache.put("anokata", new Account(2,20000));//专账//1.启动事务try(Transaction transaction = Ignition.ignite().transactions().txStart(TransactionConcurrency.OPTIMISTIC, TransactionIsolation.SERIALIZABLE, 3000, 10)){Account pAccount = transactionCache.get("piemon");Account aAccount = transactionCache.get("anokata");pAccount.setBalance(pAccount.getBalance() - 200);aAccount.setBalance(aAccount.getBalance() + 200);transactionCache.put("piemon", pAccount);transactionCache.put("anokata", aAccount);//提交事务transaction.commit();}//检查结果Account pAccount = transactionCache.get("piemon");Account aAccount = transactionCache.get("anokata");System.out.println("事务提交后-> piemon :余额为:¥" + pAccount.getBalance());System.out.println("事务提交后-> anokata :余额为:¥" + aAccount.getBalance());}}

3.1 配置缓存与事务

方法 介绍
CacheConfiguration#setAtomicityMode(CacheAtomicityMode) 通过该方法,设置缓存以事务模式。

3.2 以配置了事务的配置,获取Cache对象

方法 介绍
getOrCreateCache(CacheConfiguration) 以指定的缓存配置,来获取自定义的Cache对象

3.3 启动事务/提交事务

方法 介绍
transactions(). 获取事务对象
txStart(TransactionConcurrency,TransactionIsolation,timeout,txSize). 启动事务,参数一: TransactionConcurrency,enum有两个值,分别是OPTIMISTIC乐观,PESSIMISTIC悲观。参数二:TransactionIsolation,表示隔离级别,该enum有三个值,分别是READ_COMMITTED读已提交,REPEATABLE_READ可重复读,SERIALIZABLE序列化。参数三:超时时间。参数四:参与事务的条目数(可能是近似的)
commit() 事务提交

3.4 事务并发模式

并发模式 介绍
乐观 与我们的数据库操作类似,在最终进行提交时候才获取锁,这就是说,在我们启动事务后,提交之前,用户是允许查询或者修改的。
悲观 与我们的数据库操作类似,在事务开始的时候就获取锁,我们事务操作中的某个时间点,所有与事务相关的数据都被锁了。这也是说着期间,其他用户是无法对这些被锁数据进行操作的

3.5 隔离级别

事务等级 介绍
READ_COMMITTED 悲观并发模式下:数据被读取而没有锁,并且永远不会被缓存到事务本身中。如果在缓存配置中允许,可以从备份节点读取数据。在这种隔离中,您可以使用所谓的不可重复读取,因为并发事务可以在您在事务中两次读取数据时更改数据。锁在第一次写入访问时才被获取(包括EntryProcessor调用)。这意味着在事务提交时,在事务中读取的条目可能具有不同的值。在这种情况下不会抛出异常。乐观并发模式下:应该应用到缓存的更改,在原始节点上被收集,并应用于事务提交。事务数据是没有锁读取的,并且永远不会在事务中缓存。如果在缓存配置中允许,可以从备份节点读取数据。在这种隔离中,您可以使用所谓的不可重复读取,因为并发事务可以在您在事务中两次读取数据时更改数据。这个模式组合不检查是否条目值以来第一次读取或写入访问权限已被修改,并且永远不会抛出一个乐观的异常
REPEATABLE_READ 悲观并发模式:第一个读或写访问时候,获取到条目的锁,数据从主节点获取,并存储在本地事务映射中。对相同数据的所有连续访问都是本地的, 并将返回上一次读取或更新的事务值。这意味着没有其他并发事务可以对锁定的数据进行更改,并且您将为您的事务获取可重复的读取。 乐观并发按模式:这个隔离级别的事务工作类似于乐观的READ_COMMITTED (就是上面这个),只有一点不同-----读取值被缓存到原始节点上,所有后续的读取都保证是本地的。这种模式组合不检查自第一次读或写访问之后是否已经修改了条目值,也不会增加一个乐观的异常。
SERIALIZABLE 悲观并发模式:在悲观模式下,这种隔离级别的工作方式与可重复读取相同。 乐观并发模式:在第一次读取访问时存储一个条目版本。如果ignite引擎检测到至少在发起事务中使用的一个条目已经被修改,ignite将会在提交阶 段失败。这是通过内部检查在提交时实际在网格中的一个条目的版本的内部检查来实现的。简而言之,这意味着,如果点燃检测到有一个冲突在事务提交阶段,我们会将这个事务失败,并扔出TransactionOptimisticException &回滚任何更改。用户应该处理这 个异常并重试事务。

4.事件操作

//启动ignite
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {//创建或者获取一个缓存实例try (IgniteCache<Integer, String> cache = ignite.getOrCreateCache(CACHE_NAME)) {//对于通过远程谓词侦听器的每个事件通知,都调用这个可选的本地回调。IgniteBiPredicate<UUID, CacheEvent> locLsnr = new IgniteBiPredicate<UUID, CacheEvent>() {@Override public boolean apply(UUID uuid, CacheEvent evt) {System.out.println("Received event [evt=" + evt.name() + ", key=" + evt.key() +", oldVal=" + evt.oldValue() + ", newVal=" + evt.newValue());return true; // Continue listening.}};//远程侦听器只接受大于10或大于10的键的事件,如果事件节点是该键的主键。IgnitePredicate<CacheEvent> rmtLsnr = new IgnitePredicate<CacheEvent>() {@Override public boolean apply(CacheEvent evt) {System.out.println("Cache event [name=" + evt.name() + ", key=" + evt.key() + ']');int key = evt.key();return key >= 10 && ignite.affinity(CACHE_NAME).isPrimary(ignite.cluster().localNode(), key);}};ClusterGroup group = ignite.cluster().forCacheNodes(CACHE_NAME);Collection<ClusterNode> nodes = group.nodes();nodes.stream().forEach((ClusterNode n) -> {UUID id = n.id();System.out.println("**********" + id);});//订阅所有具有缓存运行的节点的指定缓存事件。//在examples/config/example-ignite.xml中显式地启用了缓存事件ignite.events(ignite.cluster().forCacheNodes(CACHE_NAME)).remoteListen(locLsnr, rmtLsnr,EVT_CACHE_OBJECT_PUT, EVT_CACHE_OBJECT_READ, EVT_CACHE_OBJECT_REMOVED);// 生成缓存事件.for (int i = 0; i < 20; i++)cache.put(i, Integer.toString(i));// 等待一段时间,而回调被通知剩余的放置。Thread.sleep(2000);}finally {//只有通过#destroyCache()调用才可以从集群中删除分布式缓存ignite.destroyCache(CACHE_NAME);}}

一共启动了4台服务器,下面你是这四台的打印日志,前三台是一致的,第四台是上面代码启动的

//第一台
[15:34:57] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB]
[15:35:49] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, heap=1.7GB]
[15:36:30] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]
[15:40:00] Topology snapshot [ver=4, servers=4, clients=0, CPUs=4, heap=3.5GB]
Cache event [name=CACHE_OBJECT_PUT, key=3]
Cache event [name=CACHE_OBJECT_PUT, key=5]
Cache event [name=CACHE_OBJECT_PUT, key=8]
Cache event [name=CACHE_OBJECT_PUT, key=11]
Cache event [name=CACHE_OBJECT_PUT, key=18]
//第二台
[15:35:50] Ignite node started OK (id=e40e8ec4)
[15:35:50] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, heap=1.7GB]
[15:36:30] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]
[15:39:59] Topology snapshot [ver=4, servers=4, clients=0, CPUs=4, heap=3.5GB]
Cache event [name=CACHE_OBJECT_PUT, key=4]
Cache event [name=CACHE_OBJECT_PUT, key=9]
Cache event [name=CACHE_OBJECT_PUT, key=13]
Cache event [name=CACHE_OBJECT_PUT, key=16]
[15:40:09] Topology snapshot [ver=5, servers=3, clients=0, CPUs=4, heap=2.6GB]
//第三台
[15:36:31] Ignite node started OK (id=fd12e5e1)
[15:36:31] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]
[15:39:59] Topology snapshot [ver=4, servers=4, clients=0, CPUs=4, heap=3.5GB]
Cache event [name=CACHE_OBJECT_PUT, key=1]
Cache event [name=CACHE_OBJECT_PUT, key=6]
Cache event [name=CACHE_OBJECT_PUT, key=7]
Cache event [name=CACHE_OBJECT_PUT, key=14]
Cache event [name=CACHE_OBJECT_PUT, key=15]
Cache event [name=CACHE_OBJECT_PUT, key=19]
[15:40:09] Topology snapshot [ver=5, servers=3, clients=0, CPUs=4, heap=2.6GB]
//第四台
[15:40:01] Ignite node started OK (id=de8be574)
[15:40:01] Topology snapshot [ver=4, servers=4, clients=0, CPUs=4, heap=3.5GB]>>> Cache events example started.
**********691e1696-2387-438f-9fd7-30727b7317cb
**********e40e8ec4-9e7f-4a67-9835-a6429e59860b
**********fd12e5e1-e3f8-4bac-995b-754d8d3515ba
**********de8be574-62ad-47e9-bdb6-42c669daf5ca
Cache event [name=CACHE_OBJECT_PUT, key=0]
Cache event [name=CACHE_OBJECT_PUT, key=2]
Cache event [name=CACHE_OBJECT_PUT, key=10]
Received event [evt=CACHE_OBJECT_PUT, key=10, oldVal=null, newVal=10
Cache event [name=CACHE_OBJECT_PUT, key=12]
Received event [evt=CACHE_OBJECT_PUT, key=12, oldVal=null, newVal=12
Received event [evt=CACHE_OBJECT_PUT, key=11, oldVal=null, newVal=11
Received event [evt=CACHE_OBJECT_PUT, key=13, oldVal=null, newVal=13
Cache event [name=CACHE_OBJECT_PUT, key=17]
Received event [evt=CACHE_OBJECT_PUT, key=17, oldVal=null, newVal=17
Received event [evt=CACHE_OBJECT_PUT, key=14, oldVal=null, newVal=14
Received event [evt=CACHE_OBJECT_PUT, key=15, oldVal=null, newVal=15
Received event [evt=CACHE_OBJECT_PUT, key=18, oldVal=null, newVal=18
Received event [evt=CACHE_OBJECT_PUT, key=16, oldVal=null, newVal=16
Received event [evt=CACHE_OBJECT_PUT, key=19, oldVal=null, newVal=19
[15:40:10] Ignite node stopped OK [uptime=00:00:08:655]

上述一共四段日志,其中,第1-第3段日志,是以Ignition.start(“examples/config/example-ignite.xml”)启动的服务器节点,再加上我们上述执行代码的服务器节点,一共是四台服务器节点,共同组成集群。

在我们的执行代码里,我们是将0~20的键值对存储进名字叫“CACHE_NAME”的缓存里,我们是采用的默认分区配置,一共是1024个分区,由这四个节点均分,我们观察打印的报告(因为开启了消息机制,所以在存储的时候会发送事件),类似于:

Cache event [name=CACHE_OBJECT_PUT,key=**]

通过日志,我们不难知道,是将某个值存在了哪个节点之上:

  • 节点一[3,5,8,11,18]
  • 节点二[4,9,13,16]
  • 节点三[1,6,7,14,15,19]
  • 节点四[0,2,10,17]

然后我们实例化的消息门户对象,监听了所有节点的某些事件,事件类型的指定是在remoteListen()方法中以第三个参数的形式展现的。当满足条件的事件产生,那么就会传给该监听器,它会做一系列的处理。

4.1 事件门户对象

方法 介绍
public IgniteEvents events(); 获取集群中所有节点的事件的事件门户对象
public IgniteEvents events(ClusterGroup grp); 监听集群中指定集群组的事件的事件门户对象

4.2 远程事件监听

方法 介绍
public < T extends Event> UUID remoteListen( locLsnr, rmtFilter, int… types) 该方法用于堆事件进行监听、过滤以及处理。该方法有三个参数。参数一:locLsnr,它是IgniteBiPredicate对象,它是一个函数接口,对于通过远程谓词侦听器(即第二个参数)的每个事件通知,都调用这个可选的本地回调。第二个参数:rmtFilter,它是IgnitePredicate对象,俗称远程谓词过滤器,及对于远程节点发生的事件,进行过滤,过滤掉不满足谓词条件的,满足条件的递交给回调函数,即参数一对象。第三个参数: int… types,标识这个事件门户兑现所监听的事件类型,Ignite将事件类型做了封装,在EventType这个enum类中定义,注意,默认情况下事件机制是关闭的,因为影响性能,当然我们可以开启事件,但是只监听指定的事件。

4.3 缓存存储

关于我们操作缓存,存储了0~·9的值这一部分,请参考DEBUG部分截图解释。

5. 内存策略

//下面是样例所需的xml配置,当然如果不走xml配置,也是可以自己实例化IgniteConfigurate对象,将他交给Ignition辅助类来启动ignite对象<bean class="org.apache.ignite.configuration.IgniteConfiguration"><!-- Set to true to enable distributed class loading for examples, default is false. --><property name="peerClassLoadingEnabled" value="true"/><property name="memoryConfiguration"><bean class="org.apache.ignite.configuration.MemoryConfiguration"><!-- Setting a name of the default memory policy --><property name="defaultMemoryPolicyName" value="Default_Region"/><!-- Setting the page size to 4 KB --><property name="pageSize" value="4096"/><property name="systemCacheInitialSize" value="#{40 * 1024 * 1024}"/><property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/><!-- Defining several memory policies for different memory regions --><property name="memoryPolicies"><list><!--Default memory region that grows endlessly. A cache is bound to this memory regionunless it sets another one in its CacheConfiguration.--><bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration"><property name="name" value="Default_Region"/><!-- 100 MB memory region with disabled eviction --><property name="initialSize" value="#{100 * 1024 * 1024}"/></bean><!--Memory region of 40 MBs in size with an eviction enabled.--><bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration"><property name="name" value="40MB_Region_Eviction"/><!-- Memory region of 20 MB initial size. --><property name="initialSize" value="#{20 * 1024 * 1024}"/><!-- Maximum size is 40 MB. --><property name="maxSize" value="#{40 * 1024 * 1024}"/><!-- Enabling eviction for this memory region --><property name="pageEvictionMode" value="RANDOM_2_LRU"/></bean><!--This memory region is backed by a memory-mapped file which names is passed via'swapFilePath' parameter.--><bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration"><property name="name" value="30MB_Region_Swapping"/><!-- Memory region of 15 MB initial size. --><property name="initialSize" value="#{15 * 1024 * 1024}"/><!-- Maximum size is 30 MB. --><property name="maxSize" value="#{30 * 1024 * 1024}"/><!-- Setting a name of the swapping file. --><property name="swapFilePath" value="memoryPolicyExampleSwap"/></bean></list></property></bean></property><!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. --><property name="discoverySpi"><bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi"><property name="ipFinder"><!--Ignite provides several options for automatic discovery that can be usedinstead os static IP based discovery. For information on all options referto our documentation: http://apacheignite.readme.io/docs/cluster-config--><!-- Uncomment static IP finder to enable static-based discovery of initial nodes. --><!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">--><bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder"><property name="addresses"><list><!-- In distributed environment, replace with actual host IP address. --><value>127.0.0.1:47500..47509</value></list></property></bean></property></bean></property></bean>
public static final String M40_MEMORY = "40MB_Region_Eviction";public static final String M30_MEMORY = "30MB_Region_Swapping";public static final String DEFAULT_MEMORY = "Default_Region";public static <V> void main(String[] args) {try(Ignite ignite = Ignition.start("examples/config/example-memory-policies.xml")){//以40M的内存策略创建缓存:复制模式,原子操作CacheConfiguration<Long, String> cacheConfiguration40Atomic = new CacheConfiguration<Long, String>("cacheConfiguration40Atomic");cacheConfiguration40Atomic.setMemoryPolicyName(M40_MEMORY);cacheConfiguration40Atomic.setAtomicityMode(CacheAtomicityMode.ATOMIC);cacheConfiguration40Atomic.setCacheMode(CacheMode.REPLICATED);//以40M的内存策略创建缓存:分区模式,事务操作CacheConfiguration<Long, String> cacheConfiguration40Transaction = new CacheConfiguration<Long, String>("cacheConfiguration40Transaction");cacheConfiguration40Transaction.setMemoryPolicyName(M40_MEMORY);cacheConfiguration40Transaction.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);cacheConfiguration40Transaction.setCacheMode(CacheMode.PARTITIONED);//启动上述的两个缓存实例IgniteCache<Long, String> firstCache = ignite.createCache(cacheConfiguration40Atomic);IgniteCache<Long, String> secondCache = ignite.createCache(cacheConfiguration40Transaction);//创建内存映射文件方式的缓存策略:CacheConfiguration<Long, String> cacheConfiguration30Mapped = new CacheConfiguration<Long, String>("cacheConfiguration30Mapped");cacheConfiguration30Mapped.setMemoryPolicyName(M30_MEMORY);IgniteCache<Long, String> thirdCache = ignite.createCache(cacheConfiguration30Mapped);//默认内存策略CacheConfiguration<Long, String> cacheConfigurationDefault = new CacheConfiguration<Long, String>("Default_Region");cacheConfigurationDefault.setMemoryPolicyName(DEFAULT_MEMORY);IgniteCache<Long, String> fourthCache = ignite.createCache(cacheConfigurationDefault);}}

解释下代码,小白会烦。在xml中,我们配置了三个缓存策略,分别是

  • Default_Region(这也是默认策略)
  • 30MB_Region_Swapping
  • 40MB_Region_Eviction

这三种缓存策略,我们在下面的main方法里,以指定xml启动ignite,然后,在创建缓存实例之前,我们分别通过CacheConfiguration#setMemoryPolicyName(这里填上面我们在xml里配置的内存策略名),以指定该内存以某个指定的内存策略来启动。

5.1 内存策略配置对象MemoryPolicyConfiguration

一个单独的Apache ignite节点的全部虚拟内存,可以由一个或者多个内存区域所构成。一个内存区域就是一个由内存策略配置了的可扩张的逻辑的区域。这个内存区域的大小可以各不相同。

首先,该类允许用户自定义内存策略,而内存策略对象,其实也是作为MemoryConfiguration的内存策略配置项的参数。先介绍这个

内存策略对象的几个常用的方法:

方法 介绍
setInitialSize(long initialSize) 设置由该内存策略定义的初始内存区域大小,当使用的内存大小超过这个值时,将分配新的内存块。
setMaxSize(long maxSize) 设置由该内存策略定义的最大内存区域大小。由于内部数据结构的开销,总大小不应该小于10 MB
setPageEvictionMode(DataPageEvictionMode evictionMode) 设置内存页回收模式。参数是enum类DataPageEvictionMode,它的值只有三个:DISABLED(回收失效)、RANDOM_LRU(LRU回收策略)、RANDOM_2_LRU(Random-2-LRU算法回收),具体的看架构篇,有讲解
setEvictionThreshold(…) 内存分页回收启动的阈值。例如,如果阈值为0.9,这意味着只有在90%的内存区域(由该策略定义)之后,页内存才会启动回收。
setEmptyPagesPoolSize(…) 指定用于此内存策略的重用列表中出现的最少空白页。这个参数确保Apache ignite在一个(键值)对的大小略大于页大小的1/2时能够成功地回收驱逐旧的数据条目。 如果缓存可以包含非常大的条目,则增加此参数(这个池中页面的总大小应该足以包含最大的缓存条目。)
setMetricsEnabled(…) 为该区域启用内存指标聚合。有关更多细节,请参考内存和缓存指标
setSwapFilePath(…) 到memory-mapped文件的路径,由这个内存策略定义的内存区域将会被映射到这里。有了这个设置的路径,就可以允许依赖这个区域所在的底层操作系统的交换功能

5.2 内存配置对象MemoryConfiguration

apache ignite的页缓存配置。我们上面说的MemoryPolicyConfiguration对象,它负责划分内存分区,而在分区之下,则是内存页,具体可以包括数据页,索引页,B-tree页。可以根据我们配置的MemoryConfiguration而对内存页进行配置。页内存是一种可管理的基于堆的内存架构,它将所有可扩展的内存区域划分为固定大小的页面,具体的大小,是通过setPageSize方法可以设置。一个单独的页面可以存储一个或多个缓存键值条目,这就可以用最有效的方式重新利用内存,避免内存碎片问题。默认情况下,页内存可以通过MemoryConfiguration#createDefaultPolicyConfig()方法分配一个可扩展内存区域。将在应用程序中配置的所有缓存将在默认情况下映射到这个内存区域,因此,所有的缓存数据将驻留在该内存区域中。

如果默认内存区域的初始大小不满足需求,或者需要有多个具有不同属性的内存区域,那么MemoryPolicyConfiguration可以被用于两个场景。例如,使用内存策略可以定义最大的内存不同,回收策略不同,交互选项不同的区域。一旦定义了新的内存区域,就可以将ignite缓存绑定给指定的区域。

方法 介绍
setSystemCacheInitialSize(long sysCacheInitSize) 设置用于系统缓存的内存区域的初始大小。
setSystemCacheMaxSize(long sysCacheMaxSize) 设置为系统缓存保留的最大内存区域大小。由于内部数据结构的开销,总大小不应该小于10 MB
setPageSize(int pageSize) 改变页内存大小。注意,页大小介于1kb到16kb之间,且必须是2Kb的指数
setMemoryPolicies(MemoryPolicyConfiguration… memPlcs) 设置内存策略,就是我们上面讲的内存策略
setConcurrencyLevel(int concLvl) 在ignite内部页面映射表中设置并发段的数量
setDefaultMemoryPolicySize(long dfltMemPlcSize) 覆盖自动创建的默认内存策略的大小。
setDefaultMemoryPolicyName(String dfltMemPlcName) 为初始化默认内存区域的默认内存策略设置名称。

5.3 缓存配置对象CacheConfiguration

这个类定义了网格缓存配置。这个类的对象通过IgniteConfiguration#getCacheConfiguration()方法传递给网格。它配置了再网格内启动缓存实例所需的所有的参数。你可以在一个网格内有不同名字的缓存配置对象。

方法 介绍
setMemoryPolicyName(String memPlcName) 为这个缓存指定一个内存策略,那么该缓存就会映射到这个指定内存策略的分区上。

6. affinity Collocation 数据并置

如果不同的缓存一起被访问,那么将其组合在一起,则是有益的。您的业务逻辑会经常需要访问多个缓存键。通过将它们组合在一起,可以确保使用相同的affinityKey的所有键都被缓存到相同的处理节点上,从而避免昂贵的网络访问,以从远程节点获取数据。

通俗而言,就是将某些相互关联的值,存储到一起,这样方便我们在后续的查询(可以减少网络跳转)。比如:Person(人)和Company(公司)的关系,一个人属于一家公司,不同的人,可能有的人是同事,有的人是陌路行人。

6.1 代码方式

//Person类
public class PersonCodeing {public Long persinId;public String name;//注意,这里不写这个属性也没关系。public AffinityKey<Long> key;public PersonCodeing() {super();// TODO Auto-generated constructor stub}public PersonCodeing(Long persinId, String name, AffinityKey<Long> key) {super();this.persinId = persinId;this.name = name;this.key = key;}
}
//company
public class CompanyCodeing {public Long companyId;public String companyName;public CompanyCodeing() {super();// TODO Auto-generated constructor stub}public CompanyCodeing(Long companyId, String companyName) {super();this.companyId = companyId;this.companyName = companyName;}}
//操作
IgniteCache<Object, Object> cache = ignite.getOrCreateCache("DEFAULT");
//使用 AffinityKey的版本
AffinityKey<Long> personKey1 = new AffinityKey(2001L, 2000L);
AffinityKey<Long> personKey2 = new AffinityKey(2002L, 2000L);PersonCodeing p1 = new PersonCodeing(2001L, "piemon", personKey1);
PersonCodeing p2 = new PersonCodeing(2002L, "anokata", personKey2);cache.put(2000L, new CompanyCodeing(2000L,"yihe"));
cache.put(personKey1, p1);
cache.put(personKey2, p2);
CompanyCodeing cc = (CompanyCodeing) cache.get(2000L);
System.out.println(cc.companyName);
PersonCodeing pp = (PersonCodeing) cache.get(personKey2);
System.out.println(pp.name);
 //截取了一点Debug的属性//分区号  以上三个均是在976分区(我再这里额外又启动了两个服务器节点)
GridDhtLocalPartition [rmvQueueMaxSize=256, rmvdEntryTtl=10000, id=976, store=org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl@1fad87ce, lastApplied=0, shouldBeRenting=false, state=OWNING, reservations=1, empty=false, createTime=07/10/2017 20:17:37]//追踪三次put操作,他们所存储在的节点也是一致的(其实分区一致了,节点应该也会一致,几十某一个分区由多个节点组成,那么也会取主节点)
[TcpDiscoveryNode [id=9a25b684-7d2e-4af1-a2d4-0ae181cc0853, addrs=[0:0:0:0:0:0:0:1, 10.10.18.75, 127.0.0.1, 172.18.103.1], sockAddrs=[/10.10.18.75:47500, /0:0:0:0:0:0:0:1:47500, /127.0.0.1:47500, /172.18.103.1:47500], discPort=47500, order=1, intOrder=1, lastExchangeTime=1499689054748, loc=false, ver=2.0.0#20170430-sha1:d4eef3c6, isClient=false]]

6.1.1AffinityKey 并值键

该类是一个泛型类,AffinityKey,包含两个属性:

  • private K key;类的泛型即表示这个键的类型,而这个键,就是我们需要缓存的键
  • private Object affKey;这里填写的是我们希望并置的那个数据的键,比如上面的例子,我们希望将person放在company所在的节点分区上,那么这里的affKey就是company的键。

affinityKey是一个缓存键的包装类,用于提供自定义的关联映射。该类的affinityKey(…)方法,其实就是设置上面所讲的affKey,而这个值则可以用于并值映射,如果这个值不提供,那么key属性就会被返回以做并值映射。

注意:这个类并不是必须类。他只是在我们希望使用自动以的并值映射时候,该类是可以为我们提供便利,上面的例子就是实现。

6.1.2 AffinityKeyMapper

并值键映射就是映射将缓存键映射到并值键上。并值键是一个被用于决定键将会被缓存在哪个节点上。每一个缓存将首先被传给AffinityKeyMapper#affinityKey(Object)方法,该方法的返回值将会送到AffinitFunction的实现类上,以完成键到节点的并值关联。

如果在缓存的配置对象里,没有显式的指定并值映射,那么默认的AffinityKeyMapper实现的affinityKey(Object key)方法,会首先扫描这个key对象里是否有被@AffinityKeyMapped注解的属性或者方法,有的话就直接返回,没有的话将会把键作为并值键返回(我们在6.1.1说过,一个affinityKey包含本体键key和并值键affKey),这也意味着,当所有对象都有相同的缓存键的话,那么会存储在相同的节点上。

当然我们可以自定义并值键映射,通过org.apache.ignite.configuration.CacheConfiguration#setAffinityMapper()方法可以指定使用自定义的映射。

6.1.3 AffinityFunction

将缓存得劲键映射到节点上。这个接口被分区模式和复制模式的缓存通用。你可以使用org.apache.ignite.configuration.CacheConfiguration#getAffinity()来为指定缓存配置缓存并值性。

当一个键被传给缓存时候,先回到上面讲的AffinityKeyMapper对象里,它可能会将你传来键映射到另一个用于并值关联的键上。当缓存键传给了AffinityKeyMapper#affinityKey()方法后,会再将这个方法的返回值交给partition(Object)方法,参数就是affinityKey()方法的返回值,而这个partition(Object)则是用于找出缓存键所属于的的分区。每个拓扑变化时候,分区到节点的映射使用assignPartitions(AffinityFunctionContext)方法进行计算,该方法向每个分区分配节点的集合。

此节点集合用于节点关联。REPLICATED复制缓存模式下,key将被缓存到所有返回的节点上,通常,在复制模式下,所有的缓存节点参与每一个键的缓存。而在PARTITIONED分区缓存模式下,只有主节点和备份节点才会参与进该键的缓存,且返回的节点的集合,主节点是在第一位上,后面的是备份节点。比如:如果有一个备份节点,那么返回的节点的集合是两个节点,第一个节点是主节点,第二个是备份节点。

我在复制下,四个服务器节点,默认分区是512个。我们存储缓存,经过源码查看,在每一个分区行,都分有一个4个大小的列表。里面是我们的这四个服务器节点,每一个分区都是如此,但是唯一区别的是,顺序基本都不一致。

而在分区模式下,四个节点被1024个分区瓜分,这四个节点均匀分布于各个分区上。其实一个分区也就一个,当然默认情况下是没有开启备份节点的,所以我们会发现每个分区下只有一个节点。当我将分区数设为2时候,这时还是4个节点,不开启备份节点,发现一个分区下还是只有一个节点,而不是预想中的两个,所有,在分区模式下,并不是节点数/分区数的简单除法,而是有自己的算法。当我开启了备份节点,会发现每个分区又会额外的多出几个节点,多出的数量就是配置的备份节点数量。
复制模式

服务器其节点数 现象与结论
4 四个服务器节点,默认分区是512个。我们存储缓存,经过源码查看,在每一个分区行,都分有一个4个大小的列表。里面是我们的这四个服务器节点,每一个分区都是如此,但是唯一区别的是,顺序基本都不一致

分区模式

服务器节点数量 备份节点数 分区数 结论
4 0 2(默认) 一个分区下还是只有一个节点,而不是预想中的两个,所有,在分区模式下,并不是节点数/分区数的简单除法,而是有自己的算法。
4 3 2 每个分区由四个节点,第一个主节点,其他三个为备份节点,不重复
4 大于3 2 当备份节点大于3时候(因为一共四个节点,抛去每个分区的主节点,那么就只剩下3个服务器节点可作为备份节点了),系统将所有的剩余的非主节点的节点,都拿来做备份节点,即使当你设置为5时候,也只有3个备份节点。但是,如果你动态的又启动一个服务器节点进集群,那么新的节点也会作为备份几点,因为设置的是5,可以想象成系统知道还欠着我们两个备份节点,慢慢会还给我们的。
4(默认的) 0 1024(默认) 一个分区下就一个节点,四个服务器节点均匀分布

6.2 注解方式

//company
public class CompanyAnnotation {public Long companyId;public String companyName;public CompanyAnnotation() {super();// TODO Auto-generated constructor stub}public CompanyAnnotation(Long companyId, String companyName) {super();this.companyId = companyId;this.companyName = companyName;}
}
//personKey
public class PersonKey {public Long personId;@AffinityKeyMappedpublic Long companyId;public Long getPersonId() {return personId;}public void setPersonId(Long personId) {this.personId = personId;}public Long getCompanyId() {return companyId;}public void setCompanyId(Long companyId) {this.companyId = companyId;}}
//person
public class PersonAnnotation {public PersonKey personKey;public String name;public PersonKey getPersonKey() {return personKey;}public void setPersonKey(PersonKey personKey) {this.personKey = personKey;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
//执行代码public static void main(String[] args) {try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){IgniteCache<Object, Object> cache = ignite.getOrCreateCache("DEFAULT");CompanyAnnotation company = new CompanyAnnotation();cache.put(1000L, company);PersonKey pk1 = new PersonKey();pk1.setPersonId(2000L);pk1.setCompanyId(1000L);PersonAnnotation p1 = new PersonAnnotation();p1.setPersonKey(pk1);p1.setName("piemon");cache.put(pk1, p1);}}

结果是,公司对象由于其键的原因,分配在1000分区上。而我们的person对象的键,被@AffinityKeyMapped注解标识了,在插入数据缓存时候,ignite会对键扫描的,判断您是否被该注解标识。显而易见,他也会被分配在1000分区上,因为他提供了并值键嘛。

7.affinity Collocation

准备工作:像第6小节里面讲的并置数据,实体Bean还是相同的

     try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){try(IgniteCache<Object,Object> cache = ignite.getOrCreateCache("DEFAULT_CACHE")){CompanyAnnotation c1 = new CompanyAnnotation(1L, "1公司");CompanyAnnotation c2 = new CompanyAnnotation(1L, "2公司");CompanyAnnotation c3 = new CompanyAnnotation(1L, "3公司");CompanyAnnotation c4 = new CompanyAnnotation(1L, "4公司");cache.put(1L, c1);cache.put(2L, c2);cache.put(3L, c3);cache.put(4L, c4);PersonKey pk1 = new PersonKey(11L, 1L);PersonKey pk2 = new PersonKey(22L, 2L);PersonKey pk3 = new PersonKey(33L, 3L);PersonKey pk4 = new PersonKey(44L, 4L);PersonAnnotation p1 = new PersonAnnotation(pk1, "1号");PersonAnnotation p2 = new PersonAnnotation(pk2, "2号");PersonAnnotation p3 = new PersonAnnotation(pk3, "3号");PersonAnnotation p4 = new PersonAnnotation(pk4, "4号");cache.put(pk1, p1);cache.put(pk2, p2);cache.put(pk3, p3);cache.put(pk4, p4);//igniteCompute,这个方法是测affinityRun的igniteComputeEnjoy(ignite);//igniteCluster,这个方法是测试mapKeysToNodes的igniteClusterEnjoy(ignite);}

插入数据之前,手动启动多个服务器节点(根据个人电脑情况来),我是启动了3个服务器节点,测试程序也启动了一个服务器节点,一共是4个服务器节点。

下面介绍两种并置计算的方式:

7.1 IgniteCompute#affinityRun()

我们在上面的准备代码里看到,igniteComputeEnjoy方法就是我们这一部分的样例方法了。
private static void igniteComputeEnjoy(Ignite ignite) {//获取计算对象IgniteCache<Object, Object> cache = ignite.getOrCreateCache("DEFAULT_CACHE");IgniteCompute compute = ignite.compute();for(int i = 1; i < 5;i++){final Long key = (long) i;compute.affinityRun("DEFAULT_CACHE", key, new IgniteRunnable(){@Overridepublic void run() {System.out.println("====>" + key);CompanyAnnotation peekValue = (CompanyAnnotation) cache.localPeek(key,CachePeekMode.ALL);System.out.println("peekValue" + peekValue);System.out.println("获取到了公司:" + peekValue.getCompanyName());PersonAnnotation person = (PersonAnnotation) cache.localPeek(new PersonKey(key*11, key), CachePeekMode.ALL);System.out.println("获取了" + peekValue.getCompanyName() + "的员工" + person.getName());}});}}

输出日志:

//节点1
====>3
peekValuecom.ultra.ignite.datagrid.my.CompanyAnnotation@30bb0c5b
获取到了公司:3公司
获取了3公司的员工3号
//节点2
====>4
peekValuecom.ultra.ignite.datagrid.my.CompanyAnnotation@5126d644
获取到了公司:4公司
获取了4公司的员工4号
//节点3
====>1
peekValuecom.ultra.ignite.datagrid.my.CompanyAnnotation@4a428f3d
获取到了公司:1公司
获取了1公司的员工1号
//节点4
====>2
peekValuecom.ultra.ignite.datagrid.my.CompanyAnnotation@26ee227
获取到了公司:2公司
获取了2公司的员工2号

通过日志,我们很容易相信:确实将计算,并置到了数据所在的节点上。而且我们使用的是peekValue方法,该方法只在本地获取数据,并不会去其他节点或者数据存储中获取数据,因此我们可以相信,数据确实存在本节点上。

代码并不难,重点讲下方法

7.1.1 计算门户对象

方法 结论
compute() 注意:该方法有重载方法,而这个无参的方法,则是获取所有集群中节点的计算门户对象
compute(ClusterGroup grp) 重载方法之二,参数是一个集群组,该方法是获取指定集群组内的节点的计算门户对象

7.1.2 affinityRun方法

在提供affinity key的节点上执行给定的作业,说白了,就是数据在哪里,计算就在哪里。该方法共有三个参数:

  • cacheName:缓存的名字,我们这里是“DEFAULT_CACHE”
  • affKey:并值键,该方法是在数据所在节点上执行计算,那么键的摸底,就是定位节点的
  • job:任务,该方法只有一个方法要我们实现,你可以将其想像成Runnable。

7.1.3 localPeek

方法声明:

 public V localPeek(K key, CachePeekMode... peekModes);

Peek是一个本地内存查找,该方法并不会从持久化存储中加载数据,也不会从一个远程节点上接在数据。同样,这个方法不会参与到事务中来。

该方法一共是两个参数:

  • key:缓存的键
  • peekModes:它是一个CachePeekMode Enum类,且可以配置多个。

7.2 IgniteCluster#mapKeysToNodes()

private static void igniteClusterEnjoy(Ignite ignite) {ClusterGroup group = ignite.cluster().forCacheNodes("DEFAULT_CACHE");Affinity<Object> affinity = ignite.<Object>affinity("DEFAULT_CACHE");Map<ClusterNode, Collection<Object>> mappings = affinity.mapKeysToNodes(Arrays.asList(1L,2L,3L,4L));for (Entry<ClusterNode, Collection<Object>> mapping : mappings.entrySet()) {ClusterNode node = mapping.getKey();final Collection<Object> mappedKeys = mapping.getValue();if (node != null) {// 将计算带到数据所在的节点ignite.compute(ignite.cluster().forNode(node)).run(new IgniteRunnable() {@Override public void run() {IgniteCache<Object, Object> cache = ignite.cache("DEFAULT_CACHE");for (Object key : mappedKeys) {long parseLong = Long.parseLong(key.toString());System.out.println("原生键 [key= " + key +", value=" + cache.localPeek(key) + ']');PersonAnnotation ppp = (PersonAnnotation)cache.localPeek(new PersonKey(parseLong*11, parseLong));System.out.println("并值键  [key= " + key +", value=" + ppp.getName()  + ']');}}});}}}

输入日志:

//节点1
原生键 [key= 3, value=com.ultra.ignite.datagrid.my.CompanyAnnotation@36f75613]
并值键  [key= 3, value=3号](PS:这里的3号,是PersonAnnotation的名字,看代码)
//节点2
原生键 [key= 4, value=com.ultra.ignite.datagrid.my.CompanyAnnotation@71e1daa7]
并值键  [key= 4, value=4号]
//节点3
原生键 [key= 1, value=com.ultra.ignite.datagrid.my.CompanyAnnotation@750e70a9]
并值键  [key= 1, value=1号]
//节点4
原生键 [key= 2, value=com.ultra.ignite.datagrid.my.CompanyAnnotation@390e012f]
并值键  [key= 2, value=2号]

虽然代码不同,但是目的是一致的,即将数据计算发送到数据所在的节点上进行。

介绍下用到的方法:

7.2.1 Affinity

提供并值信息来检测在分区模式的缓存中,哪个节点是主节点,那个节点是备份节点。我们可以通过Ignite.affinity(cacheName)来获取该接口的实例。

将键映射到节点是一个三步操作:

  1. 1.首先使用AffinityKeyMapper,根据给定的键,获取一个并值键。就是调用它的affinityKey(Object key)方法。如果没有指定映射器,那么就会使用原始的键来作为并值键。我们在上面的6里有讲过。
  2. 通过AffinityFunction#partition(Object)方法,将某一个并值键映射到某个分区上。
  3. 第三步,根据当前的网格拓扑版本,将包含的分区映射到节点上。

该接口提供了各式各样的mapKeysToNodes(…),该方法根据传入的键,提供给我们该键所映射到的节点。所有的mapKeysToNodes方法都不是事务性的,也不会将键注册到正在进行的事务中。

方法 结论
public ClusterNode mapKeyToNode(K key); 该方法提供了检测给定键所映射到的是哪个节点的能力。我们可以在将任务发送到节点之前,通过这个方法,找到键所对应的节点,然后再决定将计算任务发送给哪个节点。这个方法的工作原理如下:①对于本地缓存,它只返回本地节点ID②对于完全复制缓存,该方法仅返回AffinityFunction所返回的集合中的首个节点③对于分区缓存,将返回给定键的主节点
public Collection< ClusterNode> mapKeyToPrimaryAndBackups(K key); 该方法返回给定键的锁映射到的主节点和备份节点,且主节点在第一位,它与上面方法不同的是,上面的方法只返回一个,而这个方法返回的是一个节点的集合
public Map<\ClusterNode, Collection<\K>> mapKeysToNodes(Collection<? extends K> keys); 该方法提供了一种检测键所能映射到那个节点的能力。我们可以在将任务发送到节点之前,通过这个方法,找到键所对应的节点,然后再决定将计算任务发送给哪个节点。这个方法的工作原理如下:①对于本地缓存,它只返回映射到所有键的本地节点②对于完全复制缓存,使用AffinityFunction来决定键所映射到的节点们③对于分区缓存,返回的map代表了node-to-key的亲和/并值力,即键是节点,值是本节点有哪些键(不是这个节点上的所有的缓存键,而是我们作为参数传入进来的键)。
public ClusterNode mapPartitionToNode(int part); 获取给定分区获取主节点
public Map<\Integer, ClusterNode> mapPartitionsToNodes(Collection<\Integer> parts); 获取给定分区的主节点
public Collection<\ClusterNode> mapPartitionToPrimaryAndBackups(int part); 获取指定分区的节点集合,包括主节点和备份节点
public int partitions(); 分区总数
public int partition(K key); 某个键所对应的分区
public boolean isPrimary(ClusterNode n, K key); 某个节点是否是某个缓存键的主节点
public boolean isBackup(ClusterNode n, K key); 某个节点是都是给定缓存键的备份节点
public boolean isPrimaryOrBackup(ClusterNode n, K key); 给定的节点是否是给定的缓存键的主节点或者备份节点
public int[] primaryPartitions(ClusterNode n); 获取给定的群集节点是那些分区的主节点,将这些分区号,返回
public int[] backupPartitions(ClusterNode n); 给定节点对那些分区具有备份权的分区ID集合
public int[] allPartitions(ClusterNode n); 给定的节点对那些分区,具有一定的权利,包括主权利和备份权利。

7.2.2 IgniteCompute#run

在底层集群组中的一个节点上执行提供的作业。

我们在获取IgniteCompute的时候,是获取的指定的集群组的计算门户对象,因此该计算是可以在这个集群组内任意节点上执行的。

文章太长了,转到快速学习二见更多新知识

Ignite 数据网格快速学习(一)相关推荐

  1. 用Apache Ignite实现可扩展的数据网格

    在本文中,我们将先介绍数据网格(Data Grid)的基本概念.属性.以及能够提供的服务,然后讨论如何设计可扩展的数据网格,以满足实际场景的业务需求. 什么是数据网格? 数据网格是一组能够提供共享数据 ...

  2. 【软件开发底层知识修炼】十六 快速学习GDB调试三 使用GDB的数据断点监测变量是否改变

    上一篇文章我们学习了如何使用GDB进行软件断点调试和硬件断点调试:[软件开发底层知识修炼]十五 快速学习GDB调试二 使用GDB进行断点调试 本篇文章继续上一篇文章的学习,如何使用GDB的数据断点监测 ...

  3. 学习笔记(五)——数据适配器、数据表、数据网格视图控件的综合应用。

    学习笔记(五)--数据适配器.数据表.数据网格视图控件的综合应用. 1.  批量修改 修改包括增加,删除以及更新3个操作,所以声明实力化3个SQL命令分别应用于插入,删除以及修改 将声明的SQL命令连 ...

  4. 交通流分析2:《基于公共交通大数据的上海市居民出行时空特征研究_王宇》和《面向交通拥堵预测大数据的神经网络群组快速学习_沈晴》阅读总结

    上一篇的地址:https://blog.csdn.net/qq_43012160/article/details/103313749 基于公共交通大数据的上海市居民出行时空特征研究_王宇 这篇论文内容 ...

  5. Scientific Reports|利用实时搜索引擎数据快速学习地震震感区域及烈度分布

    你和"懂AI"之间,只差了一篇论文 很多读者给芯君后台留言,说看多了相对简单的AI科普和AI方法论,想看点有深度.有厚度.有眼界--以及重口味的专业论文. 为此,在多位AI领域的专 ...

  6. 数据科学学习心得_如何快速学习数据科学

    数据科学学习心得 Learning R can take a lot of time. But while it's impossible to become an expert overnight, ...

  7. 基于三维数据的深度学习综述

    众所周知,计算机视觉的目标是对图像进行理解.我们从图像中获取视觉特征,从视觉特征中对图像.场景等进行认知,最终达到理解.感知.交互.目前,比较主流的计算机视觉基本是基于二维数据进行的,但是回顾计算机视 ...

  8. 一文入门基于三维数据的深度学习

    本文转载自北京智源人工智能研究院. 这是一篇三维数据深度学习的入门好文,兼顾基础与前沿,值得收藏!为方便大家学习,本文PDF版本和所列出的所有文献提供下载,(2020年7月27日11点后)在我爱计算机 ...

  9. 高性能计算系统——大数据与快速数据分析对高性能分析的需求

    大数据与快速数据分析对高性能分析的需求 智能家居的设备的产生必然使下一代家居服务概念化,社交网站和知识社区的日益普及,科学实验和技术计算的激增,高度可编程以及软件定义IT基础设施(服务器.存储装置.网 ...

最新文章

  1. 【Java_基础】Java中Native关键字的作用
  2. 002_SpringIOC
  3. 春节书单:优秀的产品经理们都在读什么?
  4. 我居然手写了Spring框架
  5. 【蓝桥杯 - 练习】k倍区间(思维,数组)
  6. 零基础30分钟开启你的快速开发之旅
  7. 其实没事做,写写博客也不错的
  8. linux php生产环境搭建,linux php 环境搭建
  9. 《工程师文化行为自我分享》_文化对于鼓励创新行为至关重要
  10. 操作系统和Python的发展历程
  11. 如何对一个普通的Java项目进行打包,打成jar包,idea操作
  12. SpreadJS v14.1.5 Crack
  13. Android 使用gson完成Json转map,json转单个对象,json转数组
  14. Hinduja Global Solutions借助OpManager一年节省300万美元
  15. 实验四 使用CANVAS API画图
  16. python中的exifread库只要一张图片就能获取你的精确位置
  17. 拓嘉启远:定制类的商品如何处理退款
  18. 推荐下载Adobe Reader(PDF阅读器)9.0 简体中文版
  19. 香港银行开户资料和香港银行开户多少钱和渣打银行开户流程
  20. oracle spa性能测试,SPA for 11g 分析性能

热门文章

  1. TCP、UDP网络编程面试题
  2. 如何光明正大地学习KISS?当然是用这个DL接吻检测器了
  3. centos6如何添加阿里云centos和epel源
  4. 2019牛客暑期多校训练营(第一场)(B、C、E、F、H、I题待补、J)
  5. sping boot 笔记 哎呦不错哦
  6. Python之Flask框架(一)
  7. 重装ubuntu18.04笔记
  8. 前端开发:如何正确地跨端?
  9. kodi与hdplayer_将Kodi中的电影收藏与电影集合并
  10. [转载]ExtJs4 笔记(2) ExtJs对js基本语法扩展支持