跳至主要內容
黑马点评6

黑马点评6

达人探店

发布探店笔记

tb_blog表 :

create table tb_blog
(
    id          bigint unsigned auto_increment comment '主键'
        primary key,
    shop_id     bigint                                   not null comment '商户id',
    user_id     bigint unsigned                          not null comment '用户id',
    title       varchar(255) collate utf8mb4_unicode_ci  not null comment '标题',
    images      varchar(2048)                            not null comment '探店的照片,最多9张,多张以","隔开',
    content     varchar(2048) collate utf8mb4_unicode_ci not null comment '探店的文字描述',
    liked       int unsigned default '0'                 null comment '点赞数量',
    comments    int unsigned                             null comment '评论数量',
    create_time timestamp    default CURRENT_TIMESTAMP   not null comment '创建时间',
    update_time timestamp    default CURRENT_TIMESTAMP   not null on update CURRENT_TIMESTAMP comment '更新时间'
)
    collate = utf8mb4_general_ci
    row_format = COMPACT;

全民制作人ikun大约 11 分钟项目实战黑马点评项目实战黑马点评
黑马点评7

黑马点评7

附近商户

GEO数据结构

GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

  • GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
  • GEODIST:计算指定的两个点之间的距离并返回
  • GEOHASH:将指定member的坐标转为hash字符串形式并返回
  • GEOPOS:返回指定member的坐标
  • GEORADIUS:指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.以后已废弃
  • GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能
  • GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key。 6.2.新功能

全民制作人ikun大约 6 分钟项目实战黑马点评项目实战黑马点评
黑马点评4

黑马点评4

分布式锁

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
1653374296906.png
常见的分布式锁有三种
Mysql:mysql本身就带有锁机制,但是由于mysql性能本身一般,所以采用分布式锁的情况下,其实使用mysql作为分布式锁比较少
Redis:redis作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都使用redis或者zookeeper作为分布式锁,利用setnx这个方法,如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁,利用这套逻辑来实现分布式锁
Zookeeper:zookeeper也是企业级开发中较好的一个实现分布式锁的方案。
1653382219377.png


全民制作人ikun大约 12 分钟项目实战黑马点评项目实战黑马点评
黑马点评5

黑马点评5

秒杀优化

用户模拟

模拟1000个用户同时发送请求,也就是需要使用jmeter进行压力测试,需要编写代码获取1000个用户的token

生成1000个登录用户并生成token


@SpringBootTest
class HmDianPingApplicationTests {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private IUserService userService;

    @Test
    @Transactional
    public void insertUser() {
        final String filePath = "src/main/resources/user.txt";
        final int count = 1000;
        BufferedWriter writer;
        try {
            writer = new BufferedWriter(new FileWriter(filePath));
            for (int i = 0; i < count; i++) {
                String phone = "13" + RandomUtil.randomNumbers(9);
                String token = this.login(phone);
                writer.write(token);
                writer.newLine();
            }
            writer.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("生成用户token完毕");
        }
    }

    public String login(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        userService.save(user);
        //生成token
        String token = UUID.randomUUID().toString();
        //将User对象转为HashMap存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldvalue) -> fieldvalue.toString())
        );
        //存储
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        //设置有效期
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return token;
    }


}


全民制作人ikun大约 9 分钟项目实战黑马点评项目实战黑马点评
黑马点评2

黑马点评2

商品查询缓存

为什么要使用缓存?
缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力

添加商品缓存

查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis。
1653322097736.png


全民制作人ikun大约 16 分钟项目实战黑马点评项目实战黑马点评
黑马点评3

黑马点评3

优惠卷秒杀

自增的id存在一些问题:id规律太明显,受单表数据量限制,因此需要全局ID生成器

全局ID生成器

符号位:0
时间戳:31bit,以秒为单位,可以使用69年
序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID

1653363172079.png
具体代码实现:
获取2022-1-1的时间戳为1640995200L


全民制作人ikun大约 9 分钟项目实战黑马点评项目实战黑马点评
黑马点评1

黑马点评1

短信登录

基于Session实现登录流程

发送验证码:
用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号
如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户
短信验证码登录、注册:
用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息
校验登录状态:
用户在请求时候,会从cookie中携带者JsessionId到后台,后台通过JsessionId从session中拿到用户信息,如果没有session信息,则进行拦截,如果有session信息,则将用户信息保存到threadLocal中,并且放行
1653066208144.png


全民制作人ikun大约 5 分钟项目实战黑马点评项目实战黑马点评