Вроде сначала, а потом читай, Java продвинута более чем наполовину.
В интервью по проектированию системы Google есть вопрос о том, как спроектировать архитектуру флэш-продаж.,ИностранецБратданный5метод,На фото ниже один из них.
Меня зовут брат Нэн, я думаю, вам будет полезно пройти собеседование и получить предложение.
Постучите по доске: Интервьюер спросит вас о следующем функциональном дизайне!
Я давно хотел написать такую статью о функциональном дизайне продукта, и за последние несколько дней я заполнил пробелы, чтобы дать друзьям ссылку на «основные моменты проекта».
Интервьюер: Как вы спроектировали таблицу базы данных?
Брат Нэн сначала предлагает несколько основных конструкций столов для бизнеса в сфере электронной коммерции. Поскольку число пользователей увеличивается, очевидно, что сложность бизнеса будет увеличиваться с каждым днем. Вы обнаружите, что в простую таблицу добавлено много странных полей.
(1) Список продуктов
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0,
category_id INT,
status ENUM('active', 'inactive', 'deleted') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES product_categories(category_id)
);
(2) Таблица классификации продукции
CREATE TABLE product_categories (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
parent_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (parent_id) REFERENCES product_categories(category_id)
);
(3) Таблица корзины покупок пользователя
CREATE TABLE shopping_carts (
cart_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT DEFAULT 1,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
(4) Форма заказа
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
total_price DECIMAL(10, 2) NOT NULL,
status ENUM('pending', 'completed', 'cancelled') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
Интервьюер: Как обеспечить удобство использования интерфейса списка товаров?
В приложениях электронной коммерции существует множество форм списков продуктов, например: списки популярных продуктов, списки продуктов с условиями запроса и списки рекомендованных пользователем продуктов.
(1) Список популярных продуктов
В первую очередь список популярных продуктов должен быть сосредоточен на кэшировании. В конце концов, этот список должен отображаться, когда все пользователи открывают приложение. Этот интерфейс можно классифицировать как интерфейс проектирования с высоким уровнем параллелизма.
Одной из характеристик популярных продуктов является то, что они быстро обновляются. Определенный продукт может быть популярен в течение получаса, но в следующую секунду внезапно исчезнуть.
Здесь мы используем распределенный кеш Redis. Распределенный кеш обновляется при настройке популярных продуктов в фоновом режиме, а интерфейс списка популярных продуктов напрямую запрашивает Redis, не оказывая нагрузки на базу данных.
// Распределенный кеш обновляется при настройке популярных товаров в фоновом режиме, а популярный Список Интерфейс продуктов напрямую запрашивает Redis
public List<Product> getHotProductList() {
// Получить популярный Список из кеша Redis продуктов
List<Product> hotProducts = redisTemplate.opsForList().range("hot_products", 0, -1);
if (hotProducts == null || hotProducts.isEmpty()) {
// Если кеш пуст, выполните запрос из базы данных и обновите кеш.
hotProducts = productService.fetchHotProductsFromDB();
redisTemplate.opsForList().rightPushAll("hot_products", hotProducts);
}
return hotProducts;
}
Кроме того, список популярных товаров необходимо кэшировать на стороне приложения, чтобы запрос интерфейса не вызывался каждый раз, когда вы возвращаетесь на главную страницу. Настройка интерфейса кэша на стороне приложения должна быть короче, например, 1 минута. В конце концов, как упоминалось выше, скорость обновления популярных продуктов относительно высока!
(2) Список продуктов по условиям запроса
Условия запросов пользователей разнообразны. Мы можем записывать ключевые слова запроса пользователя через скрытые точки и просить операцию предоставить наиболее популярные ключевые слова запроса продукта.
Поиск по популярным ключевым словам,Кэшировать результаты запроса. Конечно, весь результат запроса будет очень большим.,Мы установилипервые несколько страницКэширование。
Где находится кэш?
Здесь мы все равно помещаем его в распределенный кеш Redis. Кто-то может сказать, поместите это в локальный кэш бэкэнда? Возможно, он не сталкивался с недостатками кэшей первого и второго уровня MyBatis. Кэш первого уровня MyBatis действует на объект SqlSession, а кэш второго уровня — на объект Mapper. Это приводит к тому, что у каждой серверной службы создаются разные локальные кэши, а результаты каждого запроса различны.
Конечно, его можно использовать в некоторых компаниях. Например, такие данные, как объем чтения, которые не интересуют пользователей, можно кэшировать локально.
// Условия запроса Список продуктов
public List<Product> getProductsByQuery(String query, int page) {
String cacheKey = "query_products:" + query + ":page" + page;
List<Product> products = redisTemplate.opsForList().range(cacheKey, 0, -1);
if (products == null || products.isEmpty()) {
// Если кеш пуст, выполните запрос из базы данных и обновите кеш.
products = productService.fetchProductsByQueryFromDB(query, page);
redisTemplate.opsForList().rightPushAll(cacheKey, products);
// Кэш действителен в течение 10 минут.
redisTemplate.expire(cacheKey, 10, TimeUnit.MINUTES);
}
return products;
}
Еще вопрос, что делать, если результаты запроса изменятся?
Здесь мы настраиваем запланированную задачу для периодического обновления кэшированных результатов «списка продуктов по условиям запроса».
// Кэш обновлений запланированных задач
@Scheduled(fixedRate = 600000)
public void updateProductsCache() {
// Повторно получить данные из базы данных и обновить кеш
List<String> hotQueries = analyticsService.getHotQueries();
for (String query : hotQueries) {
List<Product> products = productService.fetchProductsByQueryFromDB(query, 1); // Только пример: обновить первую страницу данных.
String cacheKey = "query_products:" + query + ":page1";
redisTemplate.delete(cacheKey);
redisTemplate.opsForList().rightPushAll(cacheKey, products);
// Сбросить срок действия кэша
redisTemplate.expire(cacheKey, 10, TimeUnit.MINUTES);
}
}
Интервьюер: Почему сведения о продукте необходимо кэшировать?
Подробности о продукте характеризуются низкой частотой обновления.,другого пользователяОперационные привычкида:Будет продолжать выходить и снова входить,Просматривайте страницу сведений о продукте несколько раз.
Угадайте, что они делают, пользователи неоднократно сравнивают разные товары, чтобы убедить себя, какой купить. Ведь обсессивно-компульсивное расстройство есть у каждого.
Основываясь на вышеуказанном поведении пользователя и характеристиках сведений о продукте, мы можем кэшировать сведения о продукте в приложении.
Интервьюер: Как обеспечить безопасность логики размещения заказов?
Таблица записи заказов и интерфейс заказа продукции в сфере электронной коммерции являются наиболее важными основными модулями. В конце концов, эта часть включает в себя основу зарабатывания денег в бизнесе.
(1) Функция проверки
Когда пользователь нажимает кнопку заказа в приложении, какой процесс проходит серверная служба? Сначала нам нужно это проверить.
(2) Предотвращение дублирования заявок
Кроме того, необходимо добавить ограничения на повторную отправку в интерфейс заказа. Здесь доступно множество решений. Например, при использовании решения распределенной блокировки Redis параметр ключа распределенной блокировки Redis связан с идентификатором пользователя и продукта.
# Ключ распределенной блокировки Redis
lock:order:{uid}:{product_id}
Когда пользователь размещает заказ на определенный продукт, он или она получает распределенную блокировку Redis. Для того же продукта следующий запрос заказа не может быть выполнен до тех пор, пока не будет обработана логика предыдущего продукта.
Функция предотвращения дублирования отправки в основном предназначена для предотвращения случайного касания пользователей или аномалий данных, вызванных несколькими повторными запросами заказов одновременно.
(3) Контроль транзакций
Весь процесс заказа, включая сокращение запасов, вычеты пользователей и создание таблицы заказов, должен быть включен в одну транзакцию MySQL. Если какая-либо логика в процессе пойдет не так, она будет отменена.
(4) Асинхронная обработка
Для других операций после успешного размещения заказа, таких как уведомление пользователя об успешном размещении заказа и т. д., вы можете использовать очередь задач для выполнения асинхронного выполнения, чтобы сократить время, затрачиваемое на интерфейс размещения заказа.
// Пользовательский интерфейс заказа
public Order placeOrder(int userId, int productId, int quantity) throws Exception {
// Получить распределенную блокировку
String lockKey = "lock:order:" + userId + ":" + productId;
if (!redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS)) {
throw new Exception("Заказы размещаются слишком часто, повторите попытку позже");
}
try {
// Проверьте пользователей, продукты и инвентарь
userService.verifyUser(userId);
Product product = productService.verifyProduct(productId);
inventoryService.checkInventory(productId, quantity);
// начать транзакцию
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// Сокращайте запасы, вычитайте комиссии и генерируйте заказы
inventoryService.decreaseInventory(productId, quantity);
userService.debitUserAccount(userId, product.getPrice().multiply(new BigDecimal(quantity)));
Order order = orderService.createOrder(userId, productId, product.getPrice(), quantity);
transactionManager.commit(status); // совершить транзакцию
return order;
} catch (Exception e) {
transactionManager.rollback(status); // Откат транзакции
throw e;
}
} finally {
redisTemplate.delete(lockKey); // разблокировать замок
}
}
Интервьюер: Как бы вы разработали функцию мгновенной продажи?
Мы можем рассматривать флэш-продажи как особый сценарий размещения заказов на продукцию. Flash-продажи имеют высокую степень параллелизма и ограниченный запас, а страницы продуктов Flash-продаж будут независимыми и не будут связаны с другими страницами продуктов.
Основываясь на приведенной выше простой сортировке, мы можем спроектировать ее, чтобы обеспечить стабильность сцены флэш-продажи.
(1) Статическая флэш-страница продажи
Установите статическую страницу продукта мгновенной распродажи. Когда пользователь обновляет страницу, ему нужно только получить основные серверные данные с сервера для заполнения. Кроме того, когда пользователь нажимает кнопку мгновенной продажи, на интерфейсе кнопка становится серой, чтобы уменьшить количество запросов пользователей.
(2) Ограничения заказа
Первоначальные проекты многих программистов включают все запросы в процесс заказа интерфейса.,Совершенно ненужно! ! !
Если количество флэш-продаж составляет всего 10, мы можем настроить перехват фильтрации перед интерфейсом заказа. Только первые 50 пользователей войдут в процесс заказа, а запросы других пользователей на заказ будут даже не отклонены. пройти процедуру заказа.
Впоследствии эти 50 пользователей захватят эти 10 товарных запасов.
// Решите, разрешать ли пользователям участвовать в процессе срочной покупки.
public class SeckillController {
@Autowired
private KafkaTemplate<String, SeckillOrderRequest> kafkaTemplate;
public ResponseEntity<String> placeSeckillOrder(int userId, int productId) {
String queueName = "seckill_orders";
String lockKey = "seckill:availability:" + productId;
// Проверьте, имеете ли вы право на флэш-распродажу
Long rank = redisTemplate.opsForValue().increment(lockKey);
if (rank == null || rank > 50) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("К сожалению, квота флэш-продажи заполнена.");
}
// Создать запрос на флэш-продажу
SeckillOrderRequest request = new SeckillOrderRequest(userId, productId);
// Отправить в очередь Kafka
kafkaTemplate.send(queueName, request);
return ResponseEntity.ok("Ваш запрос на продажу флэш-памяти получен и обрабатывается. Пожалуйста, терпеливо ждите результата.");
}
}
(3) Запрос заказа на основе задачи
Каждый запрос заказа абстрагируется в задачу очереди Kafka, и задачи выполняются одна за другой, чтобы снизить мгновенную нагрузку на систему.
// Выйдите и разместите задачу в очереди заказов
@Service
public class SeckillOrderConsumer {
@Autowired
private OrderService orderService;
@Autowired
private ProductService productService;
@Autowired
private InventoryService inventoryService;
@KafkaListener(topics = "seckill_orders", groupId = "seckill_group")
public void consume(SeckillOrderRequest request) {
try {
// Проверить инвентарь
if (!inventoryService.checkInventory(request.getProductId(), 1)) {
throw new Exception("Недостаточно запасов");
}
// Обработка заказа
Order order = orderService.createSeckillOrder(request.getUserId(), request.getProductId(), 1);
// Другая логическая обработка
notifyUser(order);
} catch (Exception e) {
// Обработка логики ошибок
System.out.println("Не удалось обработать флэш-продажу:" + e.getMessage());
}
}
private void notifyUser(Order order) {
// Уведомляйте пользователей о результатах флэш-распродажи
}
}
Я Брат Нэн, Нэн - это Нэн. Мне нравятся ваши лайки, лайки и лайки на Get.
Творить непросто, поэтому вы можете ставить лайки, собирать и подписываться, чтобы поддержать его. Ваша поддержка — самая большая мотивация для моего творчества.❤️