Redis在项目中实战经验
来源:互联网 发布:双色球选号过滤软件 编辑:程序博客网 时间:2024/06/06 04:28
Redis在项目中实战经验
首先对于项目中Redis做缓存的一些思路,避免走一些弯路
1、对于会话缓存、全页缓存(FPC)的三种情况(单条缓存,对于一些不分页、不需要实时的列表,不需要实时的,需要分页的列表)
(1)单条数据:表名+id作为key永久保存到redis,在更新的地方都要更新缓存
缺点:不适用与需要经常更新的数据;
(2)不分页:我们可以将列表结果缓存到redis中,设定一定的缓存过期时间,获取该列表的方法名作为key,列表结果为value;
缺点:这种情况只适用于不经常更新且不需要实时的情况下;
(3)分页:分页列表,可以把分页结果列表放到一个map,然后将该map存到reids的list中,然后给list设置一个缓存存活时间(expire),这样通过lrange出来就能获取存有分页列表的数据,遍历该list,通过遍历list中map的key判断该分页数据是否在缓存内,是则返回,不存在rpush进去;
缺点:这种做法能解决1-5页的数据已经重新加载,而6-10页的数据依然是缓存的数据而导致脏数据的情况
适用场景1
什么样的情况才会用到缓存呢??一个项目中有些数据长时间不会发生变动,但是用户又访问特别频繁。我觉得这样的情况会用到缓存。从我们项目的使用情况,我总结出来了这一点。
我们项目的首页上会有一些大的广告位,而且大家都知道用户最先访问的都会是首页,所以访问特别频繁,广告更换也不会很频繁,所以我们觉得用在这里挺合适的。
如何让数据从缓存中取到
第一,我们首先会判断缓存中是否有该数据,如果没有就从数据库中获取,返回给前台的同时存入到缓存一份儿。如果缓存中有数据,则直接返回缓存中的数据。(只截取一段儿代码,主要给大家讲解思路)
[java] view plain copy@Override public List<TbContent> getContentList(Long cid) { // 查询数据时,先从缓存查询,有就直接返回 try { String json = jedisClient.hget(REDIS_CONTENT_KEY, cid + ""); if (!StringUtils.isEmpty(json)) { // 把json数据转换成List List<TbContent> jsonToList = JsonUtils.jsonToList(json, TbContent.class); return jsonToList; } } catch (Exception e1) { e1.printStackTrace(); } // 如果没有则查询数据库 // 根据cid查询内容列表 TbContentExample example = new TbContentExample(); Criteria criteria = example.createCriteria(); criteria.andCategoryIdEqualTo(cid); // 执行查询 List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example); // 查询之后再放入缓存 // 有关content的保存在一个hash中,key为REDIS_CONTENT_KEY // 其中每项的item为contentCid,value为list(list要转换成json) try { jedisClient.hset(REDIS_CONTENT_KEY, cid + "", JsonUtils.objectToJson(list)); } catch (Exception e) { e.printStackTrace(); } return list; }
如何让缓存中的数据与数据库中的保持同步
刚开始接触的时候,我有一个疑问,不知道大家有没有,就是在项目中如果使用redis做缓存的话,如果数据有变化的时候怎么让redis中的数据与数据库中的保持同步。应该会有很多种方法,我们项目中用的是下面这种方法。
在后台对大广告位的内容进行添加或修改或删除的同时删除缓存中大广告位的信息。
[java] view plain copy/** * 添加内容 * @param content * @return */ @RequestMapping("/save") @ResponseBody public TaotaoResult insertContent(TbContent content){ TaotaoResult result = null; try { //添加大广告位信息 result = contentService.insertContent(content); //删除缓存中的信息 jedisClient.hde(REST_BASE_URL+REST_CONTENT_SYNC_URL+content.getCategoryId()); } catch (Exception e) { e.printStackTrace(); } return result; }
2、队列
适用场景2
聊天内容进行存储,考虑到数据库查询的io连接数高、连接频繁的因素,决定利用缓存做;
redis可以对所有二进制的存储,java可以对所有对象进行序列化的,
你需要先测试一下环境是否可以运行,端口是否可以,保证这些是前提条件
JedisPoolConnect.java
package com.zhijin.coding.test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;/** * Created by User on 2017/11/18. */public class JedisPoolConnect { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); JedisPool jedisPool = (JedisPool) context.getBean("jedisPool"); Jedis client = jedisPool.getResource();// client.select(0); client.set("dddd","ssss"); System.out.println(client.get("dddd")); jedisPool.returnBrokenResource(client); }}
config.xml
<?xml version="1.0" encoding="UTF-8" ?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><property name="maxTotal" value="32"></property><property name="maxIdle" value="6"></property><property name="maxWaitMillis" value="15000"></property><property name="minEvictableIdleTimeMillis" value="300000"></property><property name="numTestsPerEvictionRun" value="3"></property><property name="timeBetweenEvictionRunsMillis" value="60000"></property><property name="blockWhenExhausted" value="1"></property> </bean><bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy"> <!--config --><constructor-arg ref="jedisPoolConfig"></constructor-arg> <!-- host --><constructor-arg value="127.0.0.1"></constructor-arg> <!-- port --><constructor-arg value="6379"></constructor-arg> <!-- timeout --><constructor-arg value="150000"></constructor-arg> <!-- password --> <!-- <constructor-arg value="wb"></constructor-arg> database index <constructor-argvalue="12"></constructor-arg> 本人redis未设置密码 --> </bean> </beans>
如果这些是可以的,那么进行下一步
Message.java
package com.zhijin.coding.test;/** * Created by User on 2017/11/18. */import java.io.Serializable;/**定义消息类接收消息内容和设置消息的下标 * @author lenovo * */public class Message implements Serializable{ private static final long serialVersionUID = 7792729L; private int id; private String content; public Message(int id, String content) { this.id = id; this.content = content; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}
Configuration.java
package com.zhijin.coding.test;/** * Created by User on 2017/11/18. */import java.io.IOException;import java.io.InputStream;import java.util.Properties;/** * Created by Kinglf on 2016/10/17. */public class Configuration extends Properties { private static final long serialVersionUID = -2296275030489943706L; private static Configuration instance = null; public static synchronized Configuration getInstance() { if (instance == null) { instance = new Configuration(); } return instance; } public String getProperty(String key, String defaultValue) { String val = getProperty(key); return (val == null || val.isEmpty()) ? defaultValue : val; } public String getString(String name, String defaultValue) { return this.getProperty(name, defaultValue); } public int getInt(String name, int defaultValue) { String val = this.getProperty(name); return (val == null || val.isEmpty()) ? defaultValue : Integer.parseInt(val); } public long getLong(String name, long defaultValue) { String val = this.getProperty(name); return (val == null || val.isEmpty()) ? defaultValue : Integer.parseInt(val); } public float getFloat(String name, float defaultValue) { String val = this.getProperty(name); return (val == null || val.isEmpty()) ? defaultValue : Float.parseFloat(val); } public double getDouble(String name, double defaultValue) { String val = this.getProperty(name); return (val == null || val.isEmpty()) ? defaultValue : Double.parseDouble(val); } public byte getByte(String name, byte defaultValue) { String val = this.getProperty(name); return (val == null || val.isEmpty()) ? defaultValue : Byte.parseByte(val); } public Configuration() { InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("config.xml"); try { this.loadFromXML(in); in.close(); } catch (IOException ioe) { } }}
JedisUtils.java //封装的方法
package com.zhijin.coding.test;/** * Created by User on 2017/11/18. */import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;import java.util.List;import java.util.Map;import java.util.Set;public class JedisUtils { private static String JEDIS_IP; private static int JEDIS_PORT; private static String JEDIS_PASSWORD; //private static String JEDIS_SLAVE; private static JedisPool jedisPool; static { Configuration conf = Configuration.getInstance(); JEDIS_IP = conf.getString("jedis.ip", "127.0.0.1"); JEDIS_PORT = conf.getInt("jedis.port", 6379); JEDIS_PASSWORD = conf.getString("jedis.password", null); JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(5000); config.setMaxIdle(256);//20 config.setMaxWaitMillis(5000L); config.setTestOnBorrow(true); config.setTestOnReturn(true); config.setTestWhileIdle(true); config.setMinEvictableIdleTimeMillis(60000l); config.setTimeBetweenEvictionRunsMillis(3000l); config.setNumTestsPerEvictionRun(-1); jedisPool = new JedisPool(config, JEDIS_IP, JEDIS_PORT, 60000); } /** * 获取数据 * @param key * @return */ public static String get(String key) { String value = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); value = jedis.get(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return value; } public static void close(Jedis jedis) { try { jedisPool.returnResource(jedis); } catch (Exception e) { if (jedis.isConnected()) { jedis.quit(); jedis.disconnect(); } } } /** * 获取数据 * * @param key * @return */ public static byte[] get(byte[] key) { byte[] value = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); value = jedis.get(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return value; } public static void set(byte[] key, byte[] value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.set(key, value); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static void set(byte[] key, byte[] value, int time) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.set(key, value); jedis.expire(key, time); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static void hset(byte[] key, byte[] field, byte[] value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.hset(key, field, value); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static void hset(String key, String field, String value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.hset(key, field, value); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } /** * 获取数据 * * @param key * @return */ public static String hget(String key, String field) { String value = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); value = jedis.hget(key, field); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return value; } /** * 获取数据 * * @param key * @return */ public static byte[] hget(byte[] key, byte[] field) { byte[] value = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); value = jedis.hget(key, field); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return value; } public static void hdel(byte[] key, byte[] field) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.hdel(key, field); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } /** * 存储REDIS队列 顺序存储 * @param byte[] key reids键名 * @param byte[] value 键值 */ public static void lpush(byte[] key, byte[] value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.lpush(key, value); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } /** * 存储REDIS队列 反向存储 * @param byte[] key reids键名 * @param byte[] value 键值 */ public static void rpush(byte[] key, byte[] value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.rpush(key, value); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } /** * 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端 * @param byte[] key reids键名 * @param byte[] value 键值 */ public static void rpoplpush(byte[] key, byte[] destination) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.rpoplpush(key, destination); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } /** * 获取队列数据 * @param byte[] key 键名 * @return */ public static List<byte[]> lpopList(byte[] key) { List<byte[]> list = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); list = jedis.lrange(key, 0, -1); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return list; } /** * 获取队列数据 * @param byte[] key 键名 * @return */ public static byte[] rpop(byte[] key) { byte[] bytes = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); bytes = jedis.rpop(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return bytes; } public static void hmset(Object key, Map<String, String> hash) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.hmset(key.toString(), hash); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static void hmset(Object key, Map<String, String> hash, int time) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.hmset(key.toString(), hash); jedis.expire(key.toString(), time); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static List<String> hmget(Object key, String... fields) { List<String> result = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); result = jedis.hmget(key.toString(), fields); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return result; } public static Set<String> hkeys(String key) { Set<String> result = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); result = jedis.hkeys(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return result; } public static List<byte[]> lrange(byte[] key, int from, int to) { List<byte[]> result = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); result = jedis.lrange(key, from, to); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return result; } public static Map<byte[], byte[]> hgetAll(byte[] key) { Map<byte[], byte[]> result = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); result = jedis.hgetAll(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return result; } public static void del(byte[] key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.del(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } } public static long llen(byte[] key) { long len = 0; Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.llen(key); } catch (Exception e) { //释放redis对象 jedisPool.returnBrokenResource(jedis); e.printStackTrace(); } finally { //返还到连接池 close(jedis); } return len; }}
RedisQueneTest.java//测试方法
package com.zhijin.coding.test;/** * Created by User on 2017/11/18. */public class RedisQueneTest { public static void main(String[] args) { pop(); } private static void pop() { byte[] bytes = JedisUtils.rpop(redisKey); Message msg = null; try { msg = (Message) Utils.bytesToObject(bytes); } catch (Exception e) { e.printStackTrace(); } if (msg != null) { System.out.println(msg.getId() + " " +msg.getContent()); } } public static byte[] redisKey = "key".getBytes(); static { try { init(); } catch (Exception e) { e.printStackTrace(); } } private static void init() throws Exception { Message m1 = new Message(1,"消息推送——发布1"); System.out.println(redisKey.toString()); JedisUtils.lpush(redisKey,Utils.objectToBytes(m1)); Message m2 = new Message(2,"消息推送——发布2"); JedisUtils.lpush(redisKey,Utils.objectToBytes(m2)); }}
他们之间都是耦合关系,如果出现错误,你要自行解决,这样才能转化为你自己的东西
tips:加载config.xml可能会出现路径错误
3、排行榜/计数器
使用场景3
用redis实现计数器
社交产品业务里有很多统计计数的功能,比如:
用户: 总点赞数,关注数,粉丝数
帖子: 点赞数,评论数,热度
消息: 已读,未读,红点消息数
话题: 阅读数,帖子数,收藏数
统计计数的特点
实时性要求高
写的频率很高
写的性能对MySQL是一个挑战
可以采用redis来优化高频率写入的性能要求。
redis优化方案一
对于每一个实体的计数,设计一个hash结构的counter:
//用户counter:user:{userID}
-> praiseCnt: 100 //点赞数
-> hostCnt: 200 //热度
-> followCnt: 332 //关注数
-> fansCnt: 123 //粉丝数
//帖子counter:topic:{topicID}
-> praiseCnt: 100 //点赞数
-> commentCnt: 322 //评论数
//话题counter:subject:{subjectID}
-> favoCnt: 312 //收藏数
-> viewCnt: 321 //阅读数
-> searchCnt: 212 //搜索进入次数
-> topicCnt: 312 //话题中帖子数
类似这种计数器,随着产品功能的增加,也会越来越多,比如回复数,踩数,转发数什么的。
redis相关的命令
//获取指定userID的所有计数器
HGETALL counter:user:{userID}
//获取指定userID的指定计数器
HMGET counter:user:{userID} praiseCnt hostCnt
//指定userID点赞数+1
HINCRBY counter:user:{userID} praiseCnt
缺点:
这样设计,如果要批量查询多个用户的数据,就比较麻烦,例如一次要查指定20个userID的计数器?只能循环执行 HGETALL counter:user:{userID}。
优点:
以实体聚合数据,方便数据管理
redis优化方案二
方案二是用来解决方案一的缺点的,依然是采用hash,结构设计是这样的:
counter:user:praiseCnt
-> userID_1001: 100
-> userID_1002: 200
-> userID_1003: 332
-> userID_1004: 123
…….
-> userID_9999: 213
counter:user:hostCnt
-> userID_1001: 10
-> userID_1002: 290
-> userID_1003: 322
-> userID_1004: 143
…….
-> userID_9999: 213
counter:user:followCnt
-> userID_1001: 21
-> userID_1002: 10
-> userID_1003: 32
-> userID_1004: 203
…….
-> userID_9999: 130
获取多个指定userID的点赞数的命令变成这样了:
HMGET counter:user:praiseCnt userID_1001 userID_1002
上面命令可以批量获取多个用户的点赞数,时间复杂度为O(n),n为指定userID的数量。
优点:
解决了批量操作的问题
缺点:
当要获取多个计数器,比如同时需要praiseCnt,hostCnt时,要读多次,不过要比第一种方案读的次数要少。
一个hash里的字段将会非常宠大,HMGET也许会有性能瓶颈。
用redis管道(Pipelining)来优化方案一
对于第一种方案的缺点,可以通过redis管道来优化,一次性发送多个命令给redis执行:
$userIDArray = array(1001, 1002, 1003, 1009);
foreach (
}
print_r($replies);
4、发布/订阅
发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!
PublishRedis.java
package com.zhijin.coding.controller;import redis.clients.jedis.Jedis;/** * Created by User on 2017/11/17. */public class PublishRedis { public static void main(String[] args) { Jedis jedis = new Jedis("localhost",6379); jedis.publish("ch1","hello redis"); jedis.close(); }}
SubscribeRedis.java
package com.zhijin.coding.controller;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPubSub;/*** Created by User on 2017/11/17.*/public class SubscribeRedis extends JedisPubSub{Jedis jedis = new Jedis("127.0.0.1",6379);JedisPubSub jedisPubSub = new JedisPubSub() {@Overridepublic void onMessage(String channel, String message) {System.out.println("收到消息" + message);if("unsubscribe".equals(message)) {this.unsubscribe();}}@Overridepublic void onPMessage(String pattern, String channel, String message) {super.onPMessage(pattern, channel, message);}@Overridepublic void onPSubscribe(String pattern, int subscribedChannels) {super.onPSubscribe(pattern, subscribedChannels);}@Overridepublic void onPUnsubscribe(String pattern, int subscribedChannels) {super.onPUnsubscribe(pattern, subscribedChannels);}@Overridepublic void onSubscribe(String channel, int subscribedChannels) {super.onSubscribe(channel, subscribedChannels);}@Overridepublic void onUnsubscribe(String channel, int subscribedChannels) {super.onUnsubscribe(channel, subscribedChannels);}};public static void main(String[] args) {Jedis jedis = null;try {jedis = new Jedis("127.0.0.1",6379);SubscribeRedis sr = new SubscribeRedis();sr.onPUnsubscribe("ch1", 1);sr.subscribe();}catch (Exception e){e.printStackTrace();}finally {if(jedis!=null){jedis.disconnect();}}}}
tips:必须要开启redis本地服务才能运行啊!
参考文章:https://www.cnblogs.com/JockChou/p/4647973.html
- Redis在项目中实战经验
- 【Redis基础】Redis在项目中实战
- 【Redis基础】Redis在项目中实战
- JAVA中DWR开发项目实战经验
- 在项目中使用redis
- linux实战-redis(1) -- 在centos中安装redis
- 【Redis】redis实战:在业务中添加缓存机制
- JAVA中DWR开发项目简单聊天实战经验
- JAVA中DWR开发项目实战经验(转载)
- 在项目中开发中的一些经验
- EasyCMS在幼儿园视频直播项目实战中以redis操作池的方式应对高并发的redis操作问题
- Redis实战经验及使用场景
- Redis实战经验及使用场景
- spring boot项目实战:redis
- 大学缺少项目教学,他在传智播客郑州校区获得实战经验
- 项目经验不多时如何在简历中包装自己?
- 关于在android项目中使用fragment的个人经验
- 关于在android项目中使用fragment的个人经验
- Bone Collector
- API
- 9.1 在函数上添加包装器
- 指针数组与数组指针
- 机器学习基石-Training versus Testing
- Redis在项目中实战经验
- jqgrid 时间日期格式转换问题NaN,date类型年份异常
- 集合类
- Java NIO学习笔记一(IO VS NIO)
- 浅谈go语言 以及收集相关社区资料网站作为整理
- Maven环境搭建和介绍
- 【云星数据---Apache Flink实战系列(精品版)】:Apache Flink高级特性与高级应用007-Slot和Parallelism的深入分析002
- 博弈论(2):智猪博弈
- TLPI-Chapter 20 信号