Redis: 从入门到精通(二)

Redis: 从入门到精通(二)

高级数据结构

HyperLogLog

HyperLogLog 是一种概率数据结构,用于估算唯一值的数量。相比于存储所有唯一值,HyperLogLog 的内存占用要小得多。

# 添加元素到 HyperLogLog
PFADD myhyperloglog element1 element2 element3

# 获取 HyperLogLog 的基数估计值
PFCOUNT myhyperloglog

Bitmaps

Bitmaps 是一种可以高效存储和操作二进制数据的数据结构。可以用于用户签到等场景。

# 设置位值
SETBIT mybitmap 1001 1

# 获取位值
GETBIT mybitmap 1001

# 统计位值为 1 的数量
BITCOUNT mybitmap

Geospatial Indexes

Redis 支持地理空间索引,允许存储和查询地理位置数据。


```bash
# 添加地理位置
GEOADD mygeo 13.361389 38.115556 "Palermo"
GEOADD mygeo 15.087269 37.502669 "Catania"

# 获取位置的经纬度
GEOPOS mygeo "Palermo"

# 计算两个位置之间的距离
GEODIST mygeo "Palermo" "Catania"

# 查找半径范围内的地理位置
GEORADIUS mygeo 15 37 200 km

redis + node.js

常见应用案例

  1. 实现分布式锁,以保证多个进程或线程之间对共享资源的互斥访问。
const redis = require('redis');
const client = redis.createClient();

async function acquireLock(lockKey, lockValue, lockTimeout) {
  return new Promise((resolve, reject) => {
    client.set(lockKey, lockValue, 'PX', lockTimeout, 'NX', (err, reply) => {
      if (err) return reject(err);
      resolve(reply === 'OK');
    });
  });
}

async function releaseLock(lockKey, lockValue) {
  return new Promise((resolve, reject) => {
    client.get(lockKey, (err, value) => {
      if (err) return reject(err);
      if (value === lockValue) {
        client.del(lockKey, (err, reply) => {
          if (err) return reject(err);
          resolve(true);
        });
      } else {
        resolve(false);
      }
    });
  });
}

// 使用示例
(async () => {
  const lockKey = 'lock:key';
  const lockValue = 'lock:value';
  const lockTimeout = 30000; // 30 秒

  if (await acquireLock(lockKey, lockValue, lockTimeout)) {
    try {
      // 获得锁,执行任务
      console.log('Lock acquired');
    } finally {
      await releaseLock(lockKey, lockValue);
      console.log('Lock released');
    }
  } else {
    console.log('Unable to acquire lock');
  }

  client.quit();
})();

  1. 使用 Redis 的 INCR 和 EXPIRE 命令实现简单的限流器
const redis = require('redis');
const client = redis.createClient();

async function rateLimiter(key, limit, windowSize) {
  return new Promise((resolve, reject) => {
    client.multi()
      .incr(key)
      .expire(key, windowSize)
      .exec((err, replies) => {
        if (err) return reject(err);
        const count = replies[0];
        if (count > limit) {
          resolve(false);
        } else {
          resolve(true);
        }
      });
  });
}

// 使用示例
(async () => {
  const limitKey = 'limit:key';
  const limit = 10;
  const windowSize = 60; // 60 秒

  if (await rateLimiter(limitKey, limit, windowSize)) {
    console.log('Request allowed');
  } else {
    console.log('Rate limit exceeded');
  }

  client.quit();
})();

  1. 使用 Redis 存储会话数据,提高会话管理的效率和可靠性
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const client = redis.createClient();

const app = express();

app.use(session({
  store: new RedisStore({ client }),
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: false,
  cookie: { secure: false, maxAge: 60000 } // 1 分钟
}));

app.get('/', (req, res) => {
  if (!req.session.views) {
    req.session.views = 1;
  } else {
    req.session.views += 1;
  }
  res.send(`Number of views: ${req.session.views}`);
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

  1. 使用 Redis 实现简单的任务队列。
const redis = require('redis');
const client = redis.createClient();

async function pushTask(queue, task) {
  return new Promise((resolve, reject) => {
    client.rpush(queue, JSON.stringify(task), (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function popTask(queue) {
  return new Promise((resolve, reject) => {
    client.lpop(queue, (err, reply) => {
      if (err) return reject(err);
      resolve(JSON.parse(reply));
    });
  });
}

// 使用示例
(async () => {
  const queue = 'task:queue';

  await pushTask(queue, { task: 'Task 1' });
  await pushTask(queue, { task: 'Task 2' });

  const task1 = await popTask(queue);
  console.log('Popped task:', task1);

  const task2 = await popTask(queue);
  console.log('Popped task:', task2);

  client.quit();
})();

  1. 使用 Redis 实现数据缓存,提升读取性能。
const redis = require('redis');
const client = redis.createClient();

async function cacheGet(key) {
  return new Promise((resolve, reject) => {
    client.get(key, (err, reply) => {
      if (err) return reject(err);
      resolve(JSON.parse(reply));
    });
  });
}

async function cacheSet(key, value, ttl) {
  return new Promise((resolve, reject) => {
    client.setex(key, ttl, JSON.stringify(value), (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

// 使用示例
(async () => {
  const key = 'cache:key';
  const value = { data: 'Cached data' };
  const ttl = 60; // 60 秒

  await cacheSet(key, value, ttl);

  const cachedValue = await cacheGet(key);
  console.log('Cached value:', cachedValue);

  client.quit();
})();

  1. 使用 Redis 实现简单的计数器。
const redis = require('redis');
const client = redis.createClient();

async function incrementCounter(key) {
  return new Promise((resolve, reject) => {
    client.incr(key, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function getCounter(key) {
  return new Promise((resolve, reject) => {
    client.get(key, (err, reply) => {
      if (err) return reject(err);
      resolve(parseInt(reply, 10));
    });
  });
}

// 使用示例
(async () => {
  const counterKey = 'counter:key';

  await incrementCounter(counterKey);
  await incrementCounter(counterKey);

  const counterValue = await getCounter(counterKey);
  console.log('Counter value:', counterValue);

  client.quit();
})();

  1. 使用 Redis 进行实时统计,例如网站访问量统计。
const redis = require('redis');
const client = redis.createClient();

async function trackVisit(key) {
  const currentMinute = Math.floor(Date.now() / 60000);
  const visitKey = `${key}:${currentMinute}`;
  client.incr(visitKey);
  client.expire(visitKey, 3600); // 1 小时
}

async function getVisits(key) {
  return new Promise((resolve, reject) => {
    client.keys(`${key}:*`, (err, keys) => {
      if (err) return reject(err);
      if (keys.length === 0) return resolve(0);
      client.mget(keys, (err, values) => {
        if (err) return reject(err);
        const visits = values.reduce((acc, value) => acc + parseInt(value, 10), 0);
        resolve(visits);
      });
    });
  });
}

// 使用示例
(async () => {
  const visitKey = 'visit:count';

  await trackVisit(visitKey);
  await trackVisit(visitKey);

  const visitCount = await getVisits(visitKey);
  console.log('Visit count:', visitCount);

  client.quit();
})();

  1. 使用 Redis 进行数据自动过期处理。
const redis = require('redis');
const client = redis.createClient();

async function setExpiringData(key, value, ttl) {
  return new Promise((resolve, reject) => {
    client.setex(key, ttl, value, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function getData(key) {
  return new Promise((resolve, reject) => {
    client.get(key, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

// 使用示例
(async () => {
  const key = 'expiring:key';
  const value = 'This is expiring data';
  const ttl = 10; // 10 秒

  await setExpiringData(key, value, ttl);

  setTimeout(async () => {
    const data = await getData(key);
    console.log('Data after 5 seconds:', data);
}, 5000);

setTimeout(async () => {
const data = await getData(key);
console.log('Data after 15 seconds:', data);
client.quit();
}, 15000);
})();

  1. 利用 Redis 的发布/订阅功能实现实时聊天应用的消息广播。
const redis = require('redis');

const client = redis.createClient();

async function subscribeToChat(channel, callback) {
  const subscriber = redis.createClient();
  subscriber.subscribe(channel);
  subscriber.on('message', (ch, message) => {
    callback(message);
  });
  return subscriber;
}

async function publishMessage(channel, message) {
  client.publish(channel, message);
}

// 使用示例
(async () => {
  const chatChannel = 'chatroom';

  const subscriber = await subscribeToChat(chatChannel, (message) => {
    console.log('Received message:', message);
  });

  await publishMessage(chatChannel, 'Hello, World!');
  await publishMessage(chatChannel, 'How are you?');

  setTimeout(() => {
    subscriber.unsubscribe();
    subscriber.quit();
    client.quit();
  }, 5000);
})();
  1. 使用 Redis 的数据结构(如哈希表)和发布/订阅功能实现实时数据更新。
const redis = require('redis');
const client = redis.createClient();

async function updateData(key, field, value) {
  return new Promise((resolve, reject) => {
    client.hset(key, field, value, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function subscribeToUpdates(key, callback) {
  const subscriber = redis.createClient();
  subscriber.subscribe(key);
  subscriber.on('message', (ch, message) => {
    callback(JSON.parse(message));
  });
  return subscriber;
}

// 使用示例
(async () => {
  const dataKey = 'realtime:data';

  await updateData(dataKey, 'temperature', '25°C');

  const subscriber = await subscribeToUpdates(dataKey, (data) => {
    console.log('Updated data:', data);
  });

  setTimeout(() => {
    subscriber.unsubscribe();
    subscriber.quit();
    client.quit();
  }, 5000);
})();
  1. 使用 Redis 的列表结构实现简单的消息队列,支持生产者-消费者模式
const redis = require('redis');
const client = redis.createClient();

async function enqueue(queue, message) {
  return new Promise((resolve, reject) => {
    client.lpush(queue, message, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function dequeue(queue) {
  return new Promise((resolve, reject) => {
    client.brpop(queue, 0, (err, reply) => {
      if (err) return reject(err);
      resolve(reply[1]);
    });
  });
}

// 使用示例
(async () => {
  const queueName = 'message:queue';

  await enqueue(queueName, 'Message 1');
  await enqueue(queueName, 'Message 2');

  const message1 = await dequeue(queueName);
  console.log('Dequeued message:', message1);

  const message2 = await dequeue(queueName);
  console.log('Dequeued message:', message2);

  client.quit();
})();

  1. 利用 Redis 的地理空间索引功能存储和查询地理位置数据
const redis = require('redis');
const client = redis.createClient();

async function addLocation(place, longitude, latitude) {
  return new Promise((resolve, reject) => {
    client.geoadd('locations', longitude, latitude, place, (err, reply) => {
      if (err) return reject(err);
      resolve(reply);
    });
  });
}

async function getLocationsNearby(longitude, latitude, radius) {
  return new Promise((resolve, reject) => {
    client.georadius('locations', longitude, latitude, radius, 'km', 'WITHDIST', 'ASC', (err, locations) => {
      if (err) return reject(err);
      resolve(locations);
    });
  });
}

// 使用示例
(async () => {
  await addLocation('New York', -74.005974, 40.712776);
  await addLocation('Los Angeles', -118.243683, 34.052235);

  const locationsNearby = await getLocationsNearby(-73.9857, 40.7484, 100);
  console.log('Locations nearby:', locationsNearby);

  client.quit();
})();

  1. 使用 Redis 缓存穿透解决方案,通过设置空值或者布隆过滤器防止缓存穿透问题

缓存穿透是指恶意访问者或者用户请求不存在于缓存中的数据,导致每次请求都需要访问数据库或者其他存储,给系统带来巨大的压力和性能问题。为了解决这个问题,可以使用以下几种方法:
1.空值缓存:
当数据库查询结果为空时,将空结果(如 null 或者特定的空对象)也缓存起来,并设置一个较短的过期时间。这样,下次相同的请求就会命中缓存,而不会直接穿透到数据库。
2.布隆过滤器:
布隆过滤器是一种数据结构,用于快速检查某个元素是否存在于集合中。可以在缓存层前部署布隆过滤器,如果请求的数据在布隆过滤器中不存在,可以直接返回,避免对后端存储的查询。
3.缓存预热:
在系统启动或者数据更新时,预先将常用的数据加载到缓存中,避免在高流量时因为缓存为空而直接请求数据库。
4.数据库层面的优化:
在数据库层面设置合适的索引,降低查询的成本,同时可以使用数据库缓存(如 MySQL 的查询缓存)来减轻数据库负担。

const redis = require('redis');
const client = redis.createClient();

//空值缓存
async function getDataFromCache(key) {
  return new Promise((resolve, reject) => {
    client.get(key, (err, reply) => {
      if (err) return reject(err);
      if (reply === null) {
        resolve(null);
      } else {
        resolve(JSON.parse(reply));
      }
    });
  });
}

async function fetchDataAndCache(key) {
  // Simulate fetching data from database
  const data = { /* ... */ };

  // Cache the data with a short TTL to prevent cache staleness
  await client.setex(key, 60, JSON.stringify(data));

  return data;
}

// 使用示例
(async () => {
  const cacheKey = 'cached:data';

  let cachedData = await getDataFromCache(cacheKey);
  if (!cachedData) {
    cachedData = await fetchDataAndCache(cacheKey);
  }
  console.log('Cached data:', cachedData);

  client.quit();
})();
//布隆过滤器
const { BloomFilter } = require('bloomfilter');

// Example of using Bloom Filter to check existence
const filter = new BloomFilter(32, 16); // 32 bits and 16 hash functions

async function checkExistence(key) {
  const exists = filter.test(key);
  if (!exists) {
    // Data likely doesn't exist, skip cache and database
    return null;
  }
  // Proceed with normal cache and database operations
}

//缓存预热
async function warmUpCache() {
  const popularData = await fetchPopularData();
  for (const data of popularData) {
    client.setex(data.key, 3600, JSON.stringify(data.value));
  }
}

总结

Redis 不仅仅是一个简单的缓存系统,而是一个功能强大的数据存储和处理平台,适用于各种需要高性能和实时性的应用场景。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值