版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
电商第七天:
第一,商品详情优化、
对于新款商品,热度高的商品,查看商品详情的用户非常多!
10000人访问 {100002749549}
@RequestMapping("{skuId}.html")
访问数据库10000次。
新款商品上市的时候可能会产生高并发,对数据库造成冲击!
解决方案:
第一种:优化sql语句
select fieldName,fieldName from skuInfo where id = skuId;
索引的优化 spuId create index skuInfo on(spuId);
第二种:加缓存 {redis}
了解redis的数据类型
每种数据类型的使用场景
如何整合redis 到项目中!
springredis redisTemplate 对象操作的命令,与原生态的命令不一致!redisTemplate有自己的一套api。
原生的Jedis Jedis jedis = new Jedis("host",port); jedis.set() ; jedis.get();
采用Jedis 原生态
1. 导入相应的jar 包
查询数据在service 层,查询之后,将数据放入redis 。 所以我们需要将 redis 相应的jar 放入访问service。后续可能还有很多的service层
所以将redis 放入service-util工具类
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2. 需要获取Jedis 对象。
2.1 以前spring的xml 形式 配置jedisPool beans.xml
// 连接池JedisPool
// JedisPool jedisClient = new JedisPool(host,port,poolConfig);
<bean id="jedisClient" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.26.128"/>
<constructor-arg name="port" value="6379"/>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
</bean>
// 连接池的配置参数poolConfig
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
2.2 现在项目基于spring boot 推荐使用无配置所有的配置都推荐使用注解方式!
在service-util 项目中创建RedisUtil RedisConfig
2.2.1 需要创建一个工具类 RedisUtil
2.2.2 需要创建一个工具类 的配置类 RedisConfig
RedisConfig 作用是给initJedisPool 方法进行初始化参数设置 host,port,database
3. redis 整合业务
3.1 数据以什么方式来存储!用哪种数据类型来存储!
String :
set(skuId,skuInfo);
get(skuId);
skuInfo{
33
小米手机
1999
}
商品详情:
set(33,skuInfoJson);
String skuInfoJson = get(33)
skuInfoJson 转换为对象 skuInfo
List :
Set :
Hash :
hset(key,field,value);
key: 唯一 skuId
skuInfo{
33
小米手机
1999
....
}
field: 字段名
33
hset(33,id,33);
hset(33,skuName,小米手机);
hset(33,price,1999);
hset(skuId,字段名);
hset(skuId,字段名);
hset(skuId,字段名);
hset(skuId,字段名);
hget(key,field);
hget(skuId,字段名)
String Id = hget(33,id);
String SkuName = hget(33,skuName);
String price = hget(33,price);
hget(skuId,字段名);
hget(skuId,字段名);
hget(skuId,字段名);
hget(skuId,字段名);
ZSet :
推荐使用String!
key:sku:skuId:info
3.2 注意获取数据的流程!
如果缓存中有数据,则从缓存中获取,
如何缓存中没有数据,则从数据库获取并放入缓存!
3.3 redis 做缓存的时候,需要解决的一些问题!
a. 如果redis 宕机了,如何处理!
try-catch-finally
最后只能从数据库查询!
b. 面试redis时,常用的三个问题
缓存击穿:
指当缓存中的一个key 失效的时候。此时如果有大量用户进行访问! 则会对数据库造成冲击!
解决方案:
setnx();
使用分布式锁: set(key,value,nx,px,timeout);
redisson
https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
缓存穿透:
指查询一个根本不存在的数据,每次都会去查询数据库,则会造数据库压力。
if(key){
emptiy redis.get(key);
}else{
Object obj = finddb();
if(obj!=null){
redis.set(key,value);
}else{
// 如果是null ,则不需要放入数据
jedis.set(key ,null)
}
}
将缓存中放入一个空的值 null
缓存雪崩:
是在同一个时间点,缓存中的所有key 同时失效。此时当用户量庞大的时候,会直接对数据库造成一个冲击!
flushall
解决方案: 将key的过期时间调整,
第二,es 回顾,搭建一个全文检索项目
全文检索,三级分类检索:数据来自于哪里?
直接来源:elasticsearch
间接来源:mysql
mysql ----> elasticsearch 该过程叫商品上架!
es---安装
elsaticsearch
面试题:
为什么要使用?
select * from skuInfo where skuName like "%华为%" 索引失效!
elsaticsearch 、 solr
solr:基于数据固定不变的情况下使用。数据节点是不变,速度慢!
elsaticsearch: 数据节点变化的。速度快。
为什么会快?
因为:倒排索引!
怎么使用?
1. 有分词器:
通过分词器将查询的内容进行分词,组成不同的ids 。通过内容匹配找到对应的数据!
结合项目中:
***** ELK 框架
elsaticsearch :全文检索
logstash :收集日志
在es 中配置一下logstash!
es 中的检索日志都可以在kibana 中得到!
kibana :查询数据
1. kibana。 编写dsl 语句查询es中的数据
首页:
1. 将资料中的
css"
img"
js"
json"
放入nginx 做静态代理:
2. 在nginx中配置 nginx.conf
回顾:nginx.conf
location / {
root html;
index index.html index.htm;
}
root: nginx安装目录中的文件夹
index:表示访问这个文件夹html目录下的index.html index.htm
/:表示html文件下的根目录
server {
listen 80;
server_name www.gmall.com;
location / {
root front;
index index.htm;
}
}
3. 制作域名:
修改hosts文件
192.168.67.217 www.gmall.com
4. 重启nginx
/usr/local/nginx/sbin/nginx -s reload
表示重新加载配置文件
5. 开始访问www.gmall.com
http://www.gmall.com/
6. 制作商品详情页的域名
// 代理一个域名 【item.gmall.com】表示返回当前本地的商品详情
192.168.67.1 :vmnet8的ip地址
本机的ip地址
127.0.0.1
localhost
upstream item.gmall.com{
server 192.168.67.1:8084;
}
server {
listen 80;
# server_name item.gmall.com;
location / {
proxy_pass http://item.gmall.com;
}
}
elsaticsearch 知识储备:
安装es
注意:elasticsearch.yml 中的配置!
调整一下虚拟机内存的大小!
安装kibanna
nohup ./kibana &
将kiban 运行的所有日志文件写入nohup
INDEX: 索引库
TYPE: 表
DOCUMENT: 行
FIELD: 字段
PUT: 新建
GET: 查询
DELETE: 删除
POST: 修改
POST /INDEX/TYPE/ID/_UPDATE
{
"doc" :{
"doubanScore":"7.0"
}
}
dsl语句
put /index
get /index/type/_search
post /index/type/id/_update
delete /index
分页
排序
聚合
过滤
高亮
自定义mapping {自定义一个数据库中的表结构}
create table stu(
id int primary key,
name varchar(20),
age int,
sex varchar(1)
)
自定义mapping 属于自定义index下的type
默认只有text会进行分词,keyword是不会分词的字符串。
安装中文词库:
直接将中文词库放入插件文件夹中,并解压,然后将其压缩包删除即可!
安装完成重启:
service elasticsearch restart
使用java 语言来操作es!
a. 先导入操作es 的jar 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
b. 操作api ,利用api 对es 中的数据进行查询!
从es 中将数据查询出来,能够在应用程序中打印出来!
gmall-list-service 检索的服务项目
回顾商品详情:
@RequestMapping("{skuId}.html")
public String item(@PathVariable String skuId){
// 调用服务层查询数据
// 作用域保存!
return "item"
}
总结:
*1. 分布式锁:
**2. java 整合es !
@Test
public void testES() throws IOException {
// 回顾jestClient 可以操作dsl 语句执行!
// 定义dsl 语句
String query = "{\n" +
" \"query\": {\n" +
" \"match\": {\n" +
" \"actorList.name\": \"张译\"\n" +
" }\n" +
" }\n" +
"}";
// 定义查询动作
Search search = new Search.Builder(query).addIndex("movie_chn").addType("movie").build();
// 执行dsl 语句 执行一个操作! jestResult
SearchResult searchResult = jestClient.execute(search);
// 返回结果jestResult 遍历里面的数据
List<SearchResult.Hit<Map, Void>> hits = searchResult.getHits(Map.class);
// 循环当前结合
if (hits!=null && hits.size()>0){
for (SearchResult.Hit<Map, Void> hit : hits) {
Map map = hit.source;
System.out.println(map.get("name")); // 红海行动
}
}
}