大数据正式京淘6
Redis基础命令
- set key value【修改值】
- incr key【自增】
- decr key【自减】
- incrby key 数字【按步数增长】
- decrby key 数字【按步数减退】
- append key value【追加数据】
- mset key1 value1 key2 value3【设置一批】--无法进行分片和集群计算,早期的Redis遗留功能
- mget key1 key2 key3【获得一批】--无法进行分片和集群计算,早期的Redis遗留功能
- ttl key【查看key-value的】存活时间
- -1代表永久
- -2代表过期
- expire key 时间(秒)【设置key-value的存活时间】
- 倒计时
- pexpire key 时间(毫秒)【设置key-value的存活时间】
- 秒杀
Redis的数据结构
- key-value的形式
- Hash结构
- heset
- hset key_all key1 value1
- hset key_all key2 value2
- hset key_all key3 value3
- hset key_all key4 value4
- ...
- hget
- hget key_all key1
- hget key_all key2
- hget key_all key3
- hget key_all key4
- ...
- hexist
- hdel
- hkeys
- hvals
- list【双向列表:左上右下】
- lpush
- rpush
- lrange key start end
- rrange key start end
- linsert key before key1
- linsert key after key1
- lset key index value【负数的时候倒着数】
- lrem key index【0的时候都删除】
- ltrim key start stop【从start到stop】
- lpop key【删除列表的第一个并返回】
- rpoplpush key1 key2【头换位置】
- lindex key index【返回index位置的值】
- heset
数据的分布存储
- 要完成数据的分片存储,需要至少多个redis实例
- 启动第二个和第三个redis服务器【不同的配置文件--复制】
- 注意改变端口
- 图示
- 登录指定的端口
- 默认redis-cli -p 6379
- redis-cli -p 端口号
- redis-cli -p 6380
- redis-cli -p 6381
- 如何存储数据和读取--代码操作
- 利用jedis来控制redis的命令功能
// 测试单个节点的连接 @Test public void jedis() { // 创建连接 Jedis jedis = new Jedis("117.50.2.181", 6379); jedis.set("name", "kungfupeng"); }
- 利用jedis来控制redis的命令功能
缓存逻辑
- 通过key获取缓存
- 有获取缓存的
- 没有:获取数据库的数据并添加到缓存
- 例子
// 缓存逻辑 @Test public void jedis() { // 创建连接 Jedis jedis = new Jedis("117.50.2.181", 6379); String temp = jedis.get("name"); if (StringUtils.isNotEmpty(temp)) {// 没有缓存,进行缓存 jedis.set("name", "kungfupeng"); } else { System.out.println(temp);// 有缓存,直接从缓存中取数据 } }
分片逻辑
- 循环遍历Redis的端口【太慢】
- 弊端:扩展的时候比较麻烦
- 例子
// 循环遍历 @Test public void jedis() { // Jedis对象 Jedis jedis = null; // 模拟数据 List<String> keys = new ArrayList<String>(); keys.add("key1"); keys.add("key2"); keys.add("key3"); keys.add("key4"); // 循环次数【1-2-3】【6379-6380-6381】 int index = 1; // 循环数据分配 for (String temp : keys) { if (index == 1) { jedis = new Jedis("192.168.12.23", 6379); } else if (index == 2) { jedis = new Jedis("192.168.12.23", 6380); } else if (index == 3) { jedis = new Jedis("192.168.12.23", 6381); index = 0; } index++; System.out.println(jedis.get(temp)); } }
- hashCode取余【还可以】
- 优势:一个模块通用
- hash的散列特性
- 例子
@Test public void jedis() { // Jedis对象列表 List<Jedis> list_jedis = new ArrayList<Jedis>(); list_jedis.add(new Jedis("12.12.23.44", 6379)); list_jedis.add(new Jedis("12.12.23.44", 6380)); list_jedis.add(new Jedis("12.12.23.44", 6381)); // 模拟数据 List<String> keys = new ArrayList<String>(); keys.add("key1"); keys.add("key2"); keys.add("key3"); keys.add("key4"); // 循环数据分配 for (String temp : keys) { int num = temp.hashCode() % list_jedis.size(); for (int i = 0; i < list_jedis.size(); i++) { if (i == num) { System.out.println(list_jedis.get(i).get("key")); } } } }
- 节点信息【较好】
List<JedisSharedInfo> infoList=new ArrayList<JedisSharedInfo>();//分片信息 infoList.add(new JedisSharedInfo("104.43.23.4",6380)); ... ShardedJedis jedis=new ShardedJedis(infoList);//分片对象
- 实例
@Test public void jedis() { List<JedisShardInfo> jlist = new ArrayList<JedisShardInfo>(); // 创建节点信息 JedisShardInfo jsi1 = new JedisShardInfo("123.12.13.31", 6379); JedisShardInfo jsi2 = new JedisShardInfo("123.12.13.31", 6380); JedisShardInfo jsi3 = new JedisShardInfo("123.12.13.31", 6381); // 添加节点信息 jlist.add(jsi1); jlist.add(jsi2); jlist.add(jsi3); // 将节点信息放入分片 ShardedJedis sj = new ShardedJedis(jlist); // 添加信息 sj.set("key", "value");// 自动判断,加入相应的Jedis }
- 实例
- Jedis池
- 减少创建和关闭的次数
- 实例
@Test public void jedis() { List<JedisShardInfo> jlist = new ArrayList<JedisShardInfo>(); // 创建节点信息 JedisShardInfo jsi1 = new JedisShardInfo("12.123.44.66", 6379); JedisShardInfo jsi2 = new JedisShardInfo("12.123.44.66", 6380); JedisShardInfo jsi3 = new JedisShardInfo("12.123.44.66", 6381); // 添加节点信息 jlist.add(jsi1); jlist.add(jsi2); jlist.add(jsi3); // 获取池的配置 JedisPoolConfig config = new JedisPoolConfig(); // 配置池中的连接个数 config.setMaxTotal(100); // 创建一个池对象 ShardedJedisPool sjp = new ShardedJedisPool(config, jlist); // 获得ShardedJedis对象 ShardedJedis resource = sjp.getResource(); // 存取缓存 resource.set("naem", "kungfupeng"); // 释放资源 sjp.returnResource(resource); }
hash一致性和hash取余
- 都属于散列算法
- hash取余
- 容易产生大规模的数据倾斜【散列必定倾斜】
- 当redis服务器增加或减少时,n的值变了,数据的命中变化非常大,数据迁移的量也是相应的增加
- hash一致性
- 一定程度上解决了数据倾斜,主要解决数据迁移
- 【ip+端口】-->哈希散列-->整数值======对应=====43亿整数环的一个点
- 【key】-->哈希散列-->整数值======对应=====43亿整数环的一个点
- 保存:key顺时针找节点(IP+端口)------越密集越迁移少
- 图解
- 归属
- 节点I
- key3
- 节点II
- key1
- 节点III
- key2
- key4
- 节点I
- 继续深入解决数据的平衡性
- 引入虚拟节点
- 图解
- 归属
- 节点I
- 【key3】
- 虚拟节点I_1
- 【key2】
- 虚拟节点I_2
- 【key1】
- 节点II
- 虚拟节点II_1
- 虚拟节点II_2
- 节点III
- 虚拟节点III_1
- 虚拟节点III_2
- 【key4】
- 节点I
- 结论虚拟节点越多,平衡越好
京淘中的缓存
- 商品分类树【可以】
- 商品列表【可以:注意分页参数】
- 商品新增【可以】
- 商品修改【可以】
- 商品删除【不需要】
- 商品详情【可以】