龙游手机网站制作/seo免费外链工具
Redis穿透
缓存穿透是指查询一个数据库一定不存在的数据。
我们以前正常的使用Redis缓存的流程大致是:
1、数据查询首先进行缓存查询
2、如果数据存在则直接返回缓存数据
3、如果数据不存在,就对数据库进行查询,并把查询到的数据放进缓存
4、如果数据库查询数据为空,则不放进缓存
例如我们的数据表中主键是自增产生的,所有的主键值都大于1。此时如果用户传入的参数为-1,会是怎么样?这 个1,就是一定不存在的对象。程序就会每次都去查询数据库,而每次查询都是空,每次又都不进行缓存。假如 有人恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮我们的数据库。
为了防止有人利用这个漏洞恶意攻击我们的数据库,我们可以采取如下措施:
如果从数据库查询的对象为空,也放入缓存,key为用于提交过来的主键值,value为null,只是设定的缓存过期时 间较短比如设置为60秒。这样下次用户再根据这个key查询redis缓存就可以查询到值了(当然值为null),从而 保护我们的数据库免遭攻击。
Redis雪崩
缓存雪崩,是指在某一个时间段,缓存集中过期失效。在缓存集中失效的这个时间段对数据的访问查询,都落到了 数据库上,对于数据库而言,就会产生周期性的压力波峰。
为了避免缓存雪崩的发生,我们可以将缓存的数据设置不同的失效时间,这样就可以避免缓存数据在某个时间段集中 失效。例如对于热门的数据(访问频率高的数据)可以缓存的时间长一些,对于冷门的数据可以缓存的时间短一 些。甚至对于一些特别热门的数据可以设置永不过期。
Redis击穿
缓存击穿,是指一个key非常热点(例如双十一期间进行抢购的商品数据),在不停的扛着大并发,大并发集中对 这一个点进行访问。当这个key在失效的瞬间,持续的大并发就穿透缓存,直接请求到数据库上,就像在一个屏障 上凿开了一个洞。
我们同样可以将这些热点数据设置永不过期就可以解决缓存击穿的问题了。
连接
package org.example;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;public class Main {public static void main(String[] args) {try (JedisPool pool = new JedisPool("localhost", 6379);Jedis jedis = pool.getResource();// Store & Retrieve a simple stringjedis.set("foo", "bar")) {System.out.println(jedis.get("foo")); // prints bar// Store & Retrieve a HashMapMap<String, String> hash = new HashMap<>();;hash.put("name", "John");hash.put("surname", "Smith");hash.put("company", "Redis");hash.put("age", "29");jedis.hset("user-session:123", hash);System.out.println(jedis.hgetAll("user-session:123"));// Prints: {name=John, surname=Smith, company=Redis, age=29}}}
}
连接到Redis集群
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;//...Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7380));
JedisCluster jedis = new JedisCluster(jedisClusterNodes);
与Redis数据库建立安全连接
package org.example;import redis.clients.jedis.*;import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;public class Main {public static void main(String[] args) throws GeneralSecurityException, IOException {HostAndPort address = new HostAndPort("my-redis-instance.cloud.redislabs.com", 6379);SSLSocketFactory sslFactory = createSslSocketFactory("./truststore.jks","secret!", // use the password you specified for keytool command"./redis-user-keystore.p12","secret!" // use the password you specified for openssl command);JedisClientConfig config = DefaultJedisClientConfig.builder().ssl(true).sslSocketFactory(sslFactory).user("default") // use your Redis user. More info https://redis.io/docs/management/security/acl/.password("secret!") // use your Redis password.build();JedisPooled jedis = new JedisPooled(address, config);jedis.set("foo", "bar");System.out.println(jedis.get("foo")); // prints bar}private static SSLSocketFactory createSslSocketFactory(String caCertPath, String caCertPassword, String userCertPath, String userCertPassword)throws IOException, GeneralSecurityException {KeyStore keyStore = KeyStore.getInstance("pkcs12");keyStore.load(new FileInputStream(userCertPath), userCertPassword.toCharArray());KeyStore trustStore = KeyStore.getInstance("jks");trustStore.load(new FileInputStream(caCertPath), caCertPassword.toCharArray());TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");trustManagerFactory.init(trustStore);KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX");keyManagerFactory.init(keyStore, userCertPassword.toCharArray());SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);return sslContext.getSocketFactory();}
}
Demo
1.创建User数据类
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.search.*;
import redis.clients.jedis.search.aggr.*;
import redis.clients.jedis.search.schemafields.*;class User {private String name;private String email;private int age;private String city;public User(String name, String email, int age, String city) {this.name = name;this.email = email;this.age = age;this.city = city;}//...
}
2.使用 连接到 Redis 数据库
JedisPooled jedis = new JedisPooled("localhost", 6379);
3.添加一些测试数据
User user1 = new User("Paul John", "paul.john@example.com", 42, "London");
User user2 = new User("Eden Zamir", "eden.zamir@example.com", 29, "Tel Aviv");
User user3 = new User("Paul Zamir", "paul.zamir@example.com", 35, "Tel Aviv");
4.创建索引
jedis.ftCreate("idx:users",FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix("user:"),TextField.of("$.name").as("name"),TagField.of("$.city").as("city"),NumericField.of("$.age").as("age")
);
5.使用JSON
jedis.jsonSetWithEscape("user:1", user1);
jedis.jsonSetWithEscape("user:2", user2);
jedis.jsonSetWithEscape("user:3", user3);
6.找到用户并按年龄过滤结果
var query = new Query("Paul @age:[30 40]");
var result = jedis.ftSearch("idx:users", query).getDocuments();
System.out.println(result);
7.返回city字段
var city_query = new Query("Paul @age:[30 40]");
var city_result = jedis.ftSearch("idx:users", city_query.returnFields("city")).getDocuments();
System.out.println(city_result);
8.统计同一城市的所有用户
AggregationBuilder ab = new AggregationBuilder("*").groupBy("@city", Reducers.count().as("count"));
AggregationResult ar = jedis.ftAggregate("idx:users", ab);for (int idx=0; idx < ar.getTotalResults(); idx++) {System.out.println(ar.getRow(idx).getString("city") + " - " + ar.getRow(idx).getString("count"));
}