校招Java後端不知道做什麼項目放到簡歷上?電商支付實戰項目與相關面試題萬字總結一條龍服務

来源:https://www.cnblogs.com/hanlinyuan/archive/2023/07/10/17541026.html
-Advertisement-
Play Games

電商支付實戰項目與相關面試題總結 接下來我將用一篇萬字長文,總結好這個項目以達到可以正面硬鋼面試官的水平,如果作為一個毫無頭緒的大學生的你,簡歷中需要一個還算拿得出手的項目,那麼在2023年的今天,足矣作為一個還算OK的項目寫進你的簡歷。當然,這隻能算簡歷中的第一個項目,你還需要一個更好一些的項目作 ...


電商支付實戰項目與相關面試題總結

接下來我將用一篇萬字長文,總結好這個項目以達到可以正面硬鋼面試官的水平,如果作為一個毫無頭緒的大學生的你,簡歷中需要一個還算拿得出手的項目,那麼在2023年的今天,足矣作為一個還算OK的項目寫進你的簡歷。當然,這隻能算簡歷中的第一個項目,你還需要一個更好一些的項目作為重頭戲。(具體視頻教程聯繫我)

因本人寫下這篇文章時依舊才學疏淺,在本項目中暫時不考慮軟體工程中所謂的各種可行性,需求分析,以及E-R圖之類的東西。或者是所謂的企業級開發流程。但是還是會帶你慢慢分析本項目的思路,這些思路也都是你現在所考慮不到的,本項目應當作為一個學習過程,不必過多焦慮,利用好本項目所學到的思路,以後再去分析你自己要做的項目。

該項⽬使用的是SpringBoot的SSM框架,資料庫採⽤MySQL+Redis,消息中間件採⽤ RabbitMQ的一個 仿電商系統+通⽤型⽀付系統 的雙系統項⽬,實現了支付、購物車、商品管理、用戶管理、訂單管理、地址管理等。

註意,建議學完除了Redis和RabbitMQ 的其他技術棧,若有不會,請打開我主頁的其他文章,比如說Mybatis和Mysql的體系筆記,以及SpringBoot的註解筆記。或者找我獲取筆記配套視頻教程 or 自己在b站學也可。

簡曆書寫

微電商支付系統(自己命名,不要和課程名字一樣) 2021.9 ~ 2021.12(時間不能太短,至少3+個月)

項目介紹: 使用 SpringBoot + MyBatis + MySQL + Redis + RabbitMQ 進行系統的搭建,該項目是高仿微電 商系統+通用支付系統的雙系統項目,實現了支付、購物車、商品管理、用戶管理、訂單管理、地址管理等。 (項目介紹,如果你很熟悉,也可以包裝成參加比賽,畢業課設等)

我的職責:負責後端的實現,具體如下: 1、對後端各模塊業務的邏輯進行實現,如購物車模塊、訂單模塊的增刪改查等。 2、使用 session 對用戶登錄狀態進行保持,解決 HTTP 的無狀態以及保證信息安全。 3、使用 Redis 實現了高性能購物車,減少 MySQL 資料庫的壓力。(購物車這裡有很大概率被問) 4、使用 RabbitMQ 消息隊列消費支付消息,非同步處理消息,實現高性能。(MQ也是很大概率被問) 5、將支付模塊獨立成系統,實現與電商系統的解耦。 學習收穫(可有可無)自己寫吧,主要突然自己解決問題能力得到提升,對 xx 知識的理解更加深刻,對對接第三方平臺介面怎麼樣怎麼樣,反正自己寫吧,根據你自己的學習收穫來

1.項目總體問題:

這個項目是做什麼的?

該項⽬使用的是SpringBoot的SSM框架,資料庫採⽤MySQL+Redis,消息中間件採⽤ RabbitMQ的一個 仿電商系統+通⽤型⽀付系統 的雙系統項⽬。

完整的業務流程包括:用戶登錄或註冊 → 查看商品列表 → 點擊查看商品詳細信息 → 點擊加入購物車 → 想結算時點擊購物車 → 可以選取購物車內的最終需要購買商品之後點擊購買 → 選擇收貨地址(可以新增、刪除)→ 確定提交下單(此時已經是提交下單成功了)→ 跳轉支付界面(選擇微信or支付寶)→ 顯示實時生成的付款二維碼 → 10分鐘之內掃碼付款(若未付款,需要重新生成訂單) → 收到來自微信官方發來的支付結果通知 → 支付系統獲取支付結果後通過消息隊列MQ發給電商系統 → 電商系統接受MQ的消息再對資料庫中的訂單狀態進行更改。

為什麼要做這個項目?

因為我們學校的Javaweb期末大作業需要完成⼀個後端項目,然後我自己對微信支付這塊比較感興趣,並且這也是⼀個非常好的思考學習技術的機會,所以就想通過這個項目鍛煉自己的業務開發的能力,並藉此提升⾃⼰使⽤Java、資料庫、框架和中間件的⽔平。

項⽬是不是跟著視頻做的?

不是跟著視頻,因為自己已經體系的學過相應的知識,當時也跟著視頻做過一個簡單的項目了,所以基本的開發流程已經知曉。這個項目一開始就是打算完整的自己寫出來的而不是跟視頻。所以就想著再真真正正的自己設計並實現⼀個項目,以此來鍛煉自己學過的⼀些知識,不過說實話也確實也就做過一個很簡單的項目,所以我通過 GitHub 和一些技術平臺稀土掘金什麼的,搜索⼀些項目教程,看看別⼈都是怎麼做的,瞭解了一些基本的流程。然後再基於自己的一個理解,做出了這個項目。

2.資料庫問題:

在設計資料庫時我們可以從這2點開始梳理一下自己的思路。

  • 表關係

  • 表結構

    • 唯一索引(不可重覆)

    • 單索引,組合索引(加快查詢速度)

    • 時間戳(方便業務、排查問題使用)

    對於大多數企業級應用系統而言,資料庫是整個應用系統的基石,因此資料庫的表設計(也被稱為schema設計)也就成為整個系統設計的重中之重。表設計的不好或者不合理,不僅會影響系統性能,而且會增加開發和集成的複雜性,甚至埋下隱患,最終會導致一系列的問題,例如數據不一致性問題等。概括而言,需要根據業務需求和系統功能,採用如下步驟設計表。

    1. 確定資料庫類型。

    2. 判斷表的類型,並據此構建表的列,確定表名。

    3. 設計表的主鍵以及表之間的關聯關係。

    4. 優化設計,評估訪問性能並根據查詢模式添加索引。

    應用系統往往涉及很多表,這些表的設計存在先後順序,而表之間也具有各種關聯和依賴關係。因此,表的設計並不是一蹴而就的,步驟2、3和4在大多數時候也不是清晰可分的,甚至整個設計也是一個迴圈迭代的過程,需要在多個步驟之間反覆多次,以逐步的細化和優化。

表關係是怎麼設計的?

資料庫無非是表關係和表結構,都是需要結合電商⽀付的場景結合自己⽣活中購買商品的場景。不過之前一直沒有分析過電商支付的業務邏輯,所以我特意去淘寶上逛了一下,具體分析了一下一個商品下單的一個流程,構思了一下大概需要幾張表,之間有什麼關係。那麼我接下來具體說說表關係有哪些。

表關係分析:

我們打開淘寶,肯定是先跳出來登錄界面,所以要存儲登錄和註冊的信息 → 用戶表

用戶登錄之後做什麼事?→ 開始瀏覽各個商品,這些商品從哪來?→ 需要商品表

每個商品有各自的類別,可以點擊不同的品牌,不同的材質,尺寸選取符合條件的商品出來?→ 需要商品分類表

用戶最後選好商品之後,進行下單,訂單是不是要存儲好?→ 所以需要一個訂單表

訂單生成好之後?是不是要開始支付了 → 同理,還需要一個支付表

誒我們發現,支付前是不是還需要填寫下地址?→ 收貨地址表

支付時我們發現,訂單一張表還不夠,是不是還需要訂單之中更細緻的每個商品詳細信息?→ 訂單詳情表

其次想⼀下表與表之間的關係

⽤戶和收貨地址。⼀個⽤戶可以有多個收貨地址 —— ⼀對多的關係。

分類和商品。⼀個分類,下⾯有多個商品 —— ⼀對多的關係。

訂單和訂單詳情。⼀個訂單有多個訂單詳情 —— ⼀對多的關係。

訂單和⽀付。⼀個訂單隻能⽀付⼀次 —— ⼀對⼀的關係。

⽤戶和商品,商品和訂單他們有什麼對應關係嗎?其實沒什麼對應關係。

⽤戶和訂單是有對應關係的,⼀個⽤戶可以有多個訂單 —— 一對多的關係。

商品和訂單的關係,⼀個商品可以被多個⽤戶下多個訂單。那⼀個訂單它⼜可以買多個商品,相當於是多對多關係,所以我們沒必要把它強⾏關聯。

在設計的時候其實一邊畫圖一遍想的,看一下總體的圖吧。

總體關係圖:

image-20230528164228578

接下來就是具體每張表的欄位和結構設計了

表結構是怎麼設計的?

問起來就說:打個比方,比如說我們的什麼什麼表,然後講講索引怎麼設計,時間戳怎麼設計,具體的業務邏輯需要存儲什麼欄位之類的。還有一個點就說表與表之間是要關聯的,所以要想到數據是不是會變化,如果會變化,是不是在其他的表中進行存儲?比如說我們的訂單詳情表。

用戶表:

設一個用戶⾃增id作為主鍵,然後是⽤戶名,密碼(使⽤MD5加密),郵箱,手機。⽤戶可能會忘記密碼,所以我們設置了⼀個找回密碼的問題和答案。還有⼀個欄位是角色,他是管理員還是普通⽤戶。最後兩個欄位是創建和更新時間。主要是為了以後排查業務問題,出現了差錯到時候可以查看修改時間來debug之類的

CREATE TABLE `mall_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
  `username` varchar(50) NOT NULL COMMENT '用戶名',
  `password` varchar(50) NOT NULL COMMENT '用戶密碼,MD5加密',
  `email` varchar(50) DEFAULT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `question` varchar(100) DEFAULT NULL COMMENT '找回密碼問題',
  `answer` varchar(100) DEFAULT NULL COMMENT '找回密碼答案',
  `role` int(4) NOT NULL COMMENT '角色0-管理員,1-普通用戶',
  `create_time` datetime NOT NULL COMMENT '創建時間',
  `update_time` datetime NOT NULL COMMENT '最後一次更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_name_unique` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

收貨地址表:

收貨地址這個就很普通了,省份城市郵政編碼詳細地址,這個沒有什麼特別要註意的。

CREATE TABLE `mall_shipping` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用戶id',
  `receiver_name` varchar(20) DEFAULT NULL COMMENT '收貨姓名',
  `receiver_phone` varchar(20) DEFAULT NULL COMMENT '收貨固定電話',
  `receiver_mobile` varchar(20) DEFAULT NULL COMMENT '收貨行動電話',
  `receiver_province` varchar(20) DEFAULT NULL COMMENT '省份',
  `receiver_city` varchar(20) DEFAULT NULL COMMENT '城市',
  `receiver_district` varchar(20) DEFAULT NULL COMMENT '區/縣',
  `receiver_address` varchar(200) DEFAULT NULL COMMENT '詳細地址',
  `receiver_zip` varchar(6) DEFAULT NULL COMMENT '郵編',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分類表:

主鍵設計為自增的類別id,然後是欄位parentid⽗類id,當ID等於0的時候說明是根結點。分類我們這⾥是⽀持多級分類的,這個分類其實是⼀個樹狀的結構。

為什麼需要 parent_id ,可以想想我們的分類是不是一個大類別有很多小類別,其實長得很像一個樹,所以我們需要一個值來區分 根 和 葉子 節點。然後還要考慮一下展示順序的問題,比如說(一)(二)(三)要按照順序來排列,所以用個值來標註一下哪個先展示。

CREATE TABLE `mall_category` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '類別Id',
  `parent_id` int(11) DEFAULT NULL COMMENT '父類別id當id=0時說明是根節點,一級類別',
  `name` varchar(50) DEFAULT NULL COMMENT '類別名稱',
  `status` tinyint(1) DEFAULT '1' COMMENT '類別狀態1-正常,2-已廢棄',
  `sort_order` int(4) DEFAULT NULL COMMENT '排序編號,同類展示順序,數值相等則自然排序',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

商品表:

一些商品該有的東西

CREATE TABLE `mall_product` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id',
  `category_id` int(11) NOT NULL COMMENT '分類id,對應mall_category表的主鍵',
  `name` varchar(100) NOT NULL COMMENT '商品名稱',
  `subtitle` varchar(200) DEFAULT NULL COMMENT '商品副標題',
  `main_image` varchar(500) DEFAULT NULL COMMENT '產品主圖,url相對地址',
  `sub_images` text COMMENT '圖片地址,json格式,擴展用',
  `detail` text COMMENT '商品詳情',
  `price` decimal(20,2) NOT NULL COMMENT '價格,單位-元保留兩位小數',
  `stock` int(11) NOT NULL COMMENT '庫存數量',
  `status` int(6) DEFAULT '1' COMMENT '商品狀態.1-在售 2-下架 3-刪除',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

訂單表:

各個id都是同理,可以看到最下⾯paymenttime,starttime,endtime,closetime很多個時間,⽀付時間、發貨時間、交易完成,交易關閉好多個時間。

搞這麼多時間是原因可能是要求前端界面就要求展示這些時間,那他要展示,我必須得存著,不然怎麼拿得出這些數據呢? 原因⼆是⽅便排查問題。假如有⼀個⽤戶投訴過來了,說那個訂單很久沒有收到貨,那 你肯定要過來看時間。這到底是卡在哪⼀步了,是吧?原因三是為了數據分析。我想知 道我們平臺從⽤戶⽀付到最終發出獲取,這段時間平均大 概要多久呢?這時候你要把所 有的數據給他查出來,做個統計。

這四個欄位,這四個時間你是怎麼想出來的呢?為什麼要設定這四個欄位?

其實是根據訂單的狀態來的。想想剛開始下單的時候是新訂單,新訂單我記錄⼀個下單時間,下單時間其實createtime已經有了。接著之後⽤戶會去⽀付,所以有⼀個⽀付完成的時間。下完單之後,⽤戶也可以不⽀付,他可能會取消,那麼取消就有⼀個交易關閉的時間。總之你可以根據訂單狀態⼀旦有變化,就記錄對應的變化。

CREATE TABLE `mall_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '訂單id',
  `order_no` bigint(20) DEFAULT NULL COMMENT '訂單號',
  `user_id` int(11) DEFAULT NULL COMMENT '用戶id',
  `shipping_id` int(11) DEFAULT NULL,
  `payment` decimal(20,2) DEFAULT NULL COMMENT '實際付款金額,單位是元,保留兩位小數',
  `payment_type` int(4) DEFAULT NULL COMMENT '支付類型,1-線上支付',
  `postage` int(10) DEFAULT NULL COMMENT '運費,單位是元',
  `status` int(10) DEFAULT NULL COMMENT '訂單狀態:0-已取消-10-未付款,20-已付款,40-已發貨,50-交易成功,60-交易關閉',
  `payment_time` datetime DEFAULT NULL COMMENT '支付時間',
  `send_time` datetime DEFAULT NULL COMMENT '發貨時間',
  `end_time` datetime DEFAULT NULL COMMENT '交易完成時間',
  `close_time` datetime DEFAULT NULL COMMENT '交易關閉時間',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_no_index` (`order_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

訂單信息表:

每下⼀個訂單我可能包含多個商品。所以我們需要⼀個明細表來記錄,明細表⾥⾯有ordernumber就是訂單號了。再往下是商品ID、商品的名稱、商品圖⽚以及 ⽣成訂單時商品的單價。為什麼要存這麼多呢?只存⼀個商品ID不就⾏了嗎?有了商品ID剩下的商品名稱、圖⽚都可以去商品表⾥⾯查。現在存這麼多,不是占⽤空間嗎?這個空間還非占不可。想想商品的名稱,還有圖⽚以及它的單價,這些都是可變的。拿商品價格來說,昨天賣三⽑錢⼀件的,明天可能賣五⽑錢⼀件。 所以我肯定要記錄⽤戶購買那⼀瞬間這些商品屬性的值是多少,這⼀點⾮常重要。

表關聯的時候,⼀定要想到數據是不是會變化的。如果是會變化的那就該考慮給他做⼀個存檔在其他表中了。

CREATE TABLE `mall_order_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '訂單子表id',
  `user_id` int(11) DEFAULT NULL,
  `order_no` bigint(20) DEFAULT NULL,
  `product_id` int(11) DEFAULT NULL COMMENT '商品id',
  `product_name` varchar(100) DEFAULT NULL COMMENT '商品名稱',
  `product_image` varchar(500) DEFAULT NULL COMMENT '商品圖片地址',
  `current_unit_price` decimal(20,2) DEFAULT NULL COMMENT '生成訂單時的商品單價,單位是元,保留兩位小數',
  `quantity` int(10) DEFAULT NULL COMMENT '商品數量',
  `total_price` decimal(20,2) DEFAULT NULL COMMENT '商品總價,單位是元,保留兩位小數',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `order_no_index` (`order_no`) USING BTREE,
  KEY `order_no_user_id_index` (`user_id`,`order_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

支付信息表:

所以我們的欄位怎麼設計?⾃增ID訂單號,⽀付平臺,1是⽀付寶,2是微信。platform number是第三⽅⽀付平臺給我們的⼀個訂單號,每次發起⽀付時⾃⼰有⼀個訂單號,請求⽀付平臺之後,他會返回給我們⼀個⽀付平臺的訂單號。 支付之後,支付超時?支付失敗?支付成功?如果忘記了這些信息,是不是會導致重覆多次支付或者沒給錢就發貨了?所以要有一個欄位是⽀付狀態。當然還有最重要的支付金額,沒有金額的支付還叫什麼支付呢是吧。

DROP TABLE IF EXISTS `mall_pay_info`;
​
CREATE TABLE `mall_pay_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用戶id',
  `order_no` bigint(20) NOT NULL COMMENT '訂單號',
  `pay_platform` int(10) DEFAULT NULL COMMENT '支付平臺:1-支付寶,2-微信',
  `platform_number` varchar(200) DEFAULT NULL COMMENT '支付流水號',
  `platform_status` varchar(20) DEFAULT NULL COMMENT '支付狀態',
  `pay_amount` decimal(20,2) NOT NULL COMMENT '支付金額',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uqe_order_no` (`order_no`),
  UNIQUE KEY `uqe_platform_number` (`platform_number`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

由於具體每個表的數據過於龐大,建議點擊我的置頂隨筆聯繫我發你。當然你要是不嫌麻煩可以自己插入點數據作為測試。

 

索引怎麼設計的?

  • 唯一索引(不可重覆)保證數據的唯⼀性

所謂唯一索引,就是每個表中有一個不能重覆的值,比如說id或者名字

  • 單索引,組合索引(加快查詢速度)

其實這個涉及到MySQL的底層原理了,個人推薦一本書,《MySQL是怎樣運行的》,強烈建議去搞PDF或者花個20塊買本掘金小冊看,看完之後面試關於MySQL的問題基本可以迎刃而解。由於需要的篇幅過長,就不進行過多講解,大概就是給某些經常需要出現在where語句里的列名建立一個索引,這樣可以加快查詢速度。

為什麼要設計時間戳?

這個其實是業務才需要使用的東西,一般給每個表設立2個時間戳,一個是創建時間,一個是最後一次更新的時間,到時候有bug可以通過列印日誌的方式高效率排查錯誤。比如說某個訂單出現了問題,可以通過時間戳來校驗。(方便業務、排查問題使用)

 

MySQL 某條語句執行的好慢,訪問資料庫超時了,怎麼排查?

  1. 根據故障時段,推斷出故障是跟哪個功能有關。

  2. 根據系統能在流量峰值過後⾃動恢復這⼀現象,排除是其他後臺服務被⼤量請求打死的可能性(記憶體溢出、棧溢出或者進程直接掛掉)。然後可以把排查問題的重點 放到 MySQL 上。

  3. 觀察 MySQL 的 CPU 利⽤率,假如很⾼的話⼀般來說就是SQL導致的,去分析慢查詢日誌(慢 SQL 的⽇志中,會有這樣⼀些信息:SQL、執⾏次數、執⾏時長。)找到可能的爛SQL。

  4. 修改⽐較爛的SQL,或者適當做做緩存,再來看慢查詢⽇志還有沒有那個SQL,沒有了說明改對了。

  5. 再查看 MySQL的 CPU 利⽤率,如果還不正常就繼續觀察 CPU 利⽤率曲線變化規律,推斷可能的問題比如緩存使⽤不當,刷新得太慢或者其他原因,這個時候就想辦法去對症下藥了做針對性地預防和改進。

針對慢SQL,為了讓整個系統更加健壯,不⾄於因為某⼀個小的失誤,就導致全站無法訪問,可以這樣設計:

  1. 上線⼀個定時監控和殺掉慢 SQL 的腳本。這個腳本每分鐘執⾏⼀次,檢測上⼀分鐘內,有沒有執⾏時間超過⼀分鐘(這個閾值可以根據實際情況調整)的慢 SQL,如 果發現,直接殺掉這個會話。這樣可以有效地避免⼀個慢 SQL 拖垮整個資料庫的悲 劇。即使出現慢 SQL,資料庫也可以在⾄多 1 分鐘內⾃動恢復,避免資料庫⻓時間不可⽤。代價是,可能會有些功能,之前運⾏是正常的,這個腳本上線後,就會出現問題。但是,這個代價還是值得付出的,並且,可以反過來督促開發⼈員更加小心,避免寫出慢 SQL。

  2. 做某個模塊相應的降級⽅案。⽐如⾸⻚,做⼀個簡單靜態頁面的首頁 ,只要包含商品搜索欄、⼤的品類和其他頂級功能模塊⼊⼝的鏈接就可以了。在 Nginx 上做⼀個 策略,如果請求⾸⻚數據超時的時候,直接返回這個靜態的⾸⻚作為替代。這樣後 續即使⾸⻚再出現任何的故障,也可以暫時降級,⽤靜態⾸⻚替代。⾄少不會影響 到⽤戶使⽤其他功能。

  3. 優化緩存置換策略

    前兩個⽅法容易實施,不需要對系統做很⼤的改造,並且效果也⽴竿⻅影。

那假如找到慢的 SQL 了怎麼優化呢?

第⼀:索引優化。使⽤索引避免全表掃描,畢竟SQL執⾏速度的快慢關鍵還是語句需要 掃描數據的⾏數,同時儘量只獲取需要的列。避免索引失效,特定業務 可以設置聯合索引讓需要查詢返回的列都在索引中避免回表操作。具體查看《MySQL是怎樣運行的》的7.4節

第⼆:排序優化。排序也是可能完成慢SQL的因素,尤其是數據量⼤,需要使⽤外部排序的時候⼜可以與磁碟IO性能扯上關係等,常⻅的問題還有limit m,n m很⼤⼜⽆法使⽤索引的時候,可以通過查詢排序數據⾥⾯最小的。

第三:join優化。多表聯合查詢的時候,儘量使⽤小表驅動⼤表。具體查看《MySQL是怎樣運行的》的11.2節

第四:避免⼤事務。將⼤事務複雜的SQL,拆分成多個⼩的SQL單個表執⾏,獲取的結果在程式中進⾏封裝,儘量減小事務粒度,減少鎖表的時間。最後假如 SQL 本身沒問題了,但是還是慢,那就可能是資料庫自己的慢導致的 SQL 慢了。⾸先看看資料庫參數設置有沒有問題,其次還有數據量到達⼀定規模後,單機性能容易受限導致資料庫響應慢;讀寫分離,從庫提供讀服務,錯誤的認為從庫只需要提供 查詢服務採⽤了達不到性能指標的機器,其實是主庫承受的數據更新壓力,從庫⼀個不落的都要承受,還要更多的提供查詢服務。

3.用戶問題:

⽤戶管理是怎麼實現的?

先看一張圖

 

因為HTTP協議是⽆狀態⽆連接的協議,服務端對於客戶端每次發送的請求都認為它是⼀ 個新的請求,上⼀次會話和下⼀次會話是沒有聯繫的。因為它⽆法保存登錄狀態,所以 從協議本身來說,它不適合⽤來做會話管理。

因此,我們會使⽤⼀個上層應⽤去實現我們的會話管理功能。這個應⽤可以在切換頁面時保持登錄狀態,並且對⽤戶是透明的,這樣就使得我們能在短時間內再次訪問⼀個登錄過的頁面,就會保持登錄狀態。

我採⽤的是基於session的認證: 登錄的時候⾸先根據⽤戶名查詢⽤戶,如果查不到或者查詢出來密碼比對發現錯誤就返回錯誤;

查到了密碼還正確就是登錄成功,把用戶數據信息在密碼清空後再放到session 的currentUser屬性⾥,也放⽤戶信息到響應體⾥返回。退出登錄的時候再清空。

Web應⽤伺服器會給⽤戶配置⼀個sessionid,並將它存儲在伺服器記憶體中,之後再把這個sessionid發送給⽤戶。下次在進⾏需要登錄的操作時前端發送請求的時候在請求頭⾥帶上sessionId,伺服器的攔截器攔截判斷根據這個session⾥⾯是否有⽤戶信息,有說明已經登錄過了,沒有則返回錯誤。

有沒有可能攻擊者盜⽤sessionid繞過身份認證?

假如說瀏覽器禁⽤cookie,sessionid只能重寫在URL中,攻擊者發起會話固定攻擊怎麼辦?

攻擊者⾸先訪問⼀個需要登錄的⽹站,獲取到 Web 應⽤返回的 sessionid 信息。由於攻擊者沒有賬戶密碼,所以只能通過發送⼀個誘騙信息url上帶著這個sessionid給受害者,使得受害者⽤這個 sessionid 實現登錄操作。這樣攻擊者的 sessionid 就通過了驗證,使得攻擊者再次⽤這個 sessionid 信息訪問被攻擊⽹站時,可以直接通過保持登錄 的認證。

解決: 只要在⽤戶登錄時重置 session(session.invalidate()⽅法),然後把登錄信息保存到新的 session中即可。 為了安全考慮,Web應⽤通常會給 sessionid 設置⼀個過期時間,使得 sessionid 僅在某個時間段內有效。

瀏覽器突然關閉了,你⼜如何讓⽤戶⾃動退出 呢?

  1. 瀏覽器關閉的時候,發送請求到後臺調⽤退出接⼝,根據 sessionid來刪除當前會話 的⽤戶信息或者把cookie給⼲死,session沒有對應的sessionid等於廢了。

  2. 設置session的有效期⽐如30分鐘。寫⼀個監聽類,session超時的時候會⾃動刪除 session對應的⽤戶數據;

怎麼保證⽤戶密碼的安全?

每個⽤戶需要⼀個鹽值,密碼要加⼀個鹽值再進⾏加密。 這樣相同的密碼可以⽣成不同的hash值。

或者加密演算法MD5 可以換成SHA安全hash演算法,安全等級更⾼,但是加密速度更慢。

怎麼防⽌別⼈⽤腳本批量註冊刷掉⽹站?

可以加⼀個通過郵件、簡訊向⽤戶發送驗證碼的環節,驗證碼存在redis⾥,⽤戶輸⼊驗證碼和redis⾥的判斷,成功才能註冊。 對於簡訊驗證碼這種開放接⼝,程式邏輯內需要有防刷邏輯。好的防刷邏輯是,對正常使⽤的⽤戶毫⽆影響,只有疑似異常使⽤的⽤戶才會感受到。對於簡訊驗證碼,舉兩個常見的⽅式來防刷。

第⼀種⽅式,控制相同⼿機號的發送次數和發送頻次。

第⼆種⽅式,增加圖形驗證碼。

一些分散式問題

當多點部署伺服器的時候,你在伺服器a上登錄, 伺服器b 如何得到⽤戶登錄信息?

如果每個伺服器都有該⽤戶的session,如果有很⼤的⽤戶量,假如500萬⽤戶是每個伺服器存了8g的session信 息,如果再來500萬,豈不是各個伺服器均要擴容到16G,即每個伺服器都需擴容容量,怎麼優化?

如果在多個實例之間處理同⼀個⽤戶的登錄狀態?

這些問題統統採用 JWT、分散式session的方法,不過目前還未細學。只是大概瞭解有這2個方法,不過校招對分散式要求不高,只是加分項而已,問題不大。

 

4.商品問題:

商品管理是怎麼實現的?

 

商品信息假如欄位越來越多,而且流量上來了怎 麽辦?

⽆法⼀個系統解決,需要分而治之

  • 從存儲層面來說,數據區分為:固定結構數據、⾮固定結構數據、富媒體數據

  • 從讀取層⾯,將數據分為:經常變化數據、非經常變化數據

1.從數據存儲到哪的角度:

  • 固定結構數據: 商品主標題、副標題、價格,等商品最基本、最主要的信息(任何商品都有的屬性)

    • 存儲到:MySQL中

  • 非固定結構數據: 商品參數,不同類型的商品,參數基本完全不⼀樣。電腦的記憶體⼤⼩、⼿機的屏幕 尺⼨、酒的度數、⼝紅的⾊號等等。

    • 存儲到不需要固定結構的存儲:MongoDB或者MySQL的 json欄位中

  • 富媒體數據: 商品的主圖、詳情介紹圖⽚、視頻等富媒體數據

    • 存儲到:對象存儲。並且通過客戶端直接調⽤對象存儲的API,得到媒體資源在對象存儲中的ID或者URL之後,將ID或者URL提交到MySQL中。對象存儲⾃帶CDN加速服務,響應時間快。比如阿裡雲OSS

2.從數據讀取角度:

  • 存儲到MySQL中的數據,需要設計⼀層緩存層⽐如Redis,應對⾼併發讀

  • 對於不經常變動的數據: 可以交給前端靜態化加速處理

數據量最⼤的圖⽚、視頻和商品介紹都是從離⽤戶最近的 CDN 服務商獲取的,速度 快,節約帶寬。真正打到商品系統的請求,就是價格這些需要動態獲取的商品信息,⼀ 般做⼀次 Redis 查詢就可以了,基本不會有流量打到 MySQL 中。這樣⼀個商品系統的 存儲的架構,把⼤部分請求都轉移到了⼜便宜速度⼜快的 CDN 伺服器上,可以⽤很少 量的伺服器和帶寬資源,抗住⼤量的併發請求。

MongoDB 最⼤的特點就是,它的“表結構”是不需要事先定義的

其實,在 MongoDB 中根本沒有表結構。由於沒有表結構,它⽀持你把任意數據都放在同⼀張表⾥,你甚⾄ 可以在⼀張表⾥保存商品數據、訂單數據等這些結構完全不同的數據。

並且,還能⽀持 按照數據的某個欄位進⾏查詢。它是怎麼做到的呢?

MongoDB 中的每⼀⾏數據,在存 儲層就是簡單地被轉化成 BSON 格式後存起來,這個 BSON 就是⼀種更緊湊的 JSON。 所以,即使在同⼀張表⾥⾯,它每⼀⾏數據的結構都可以是不⼀樣的。

當然,這樣的靈活性也是有代價的,MongoDB 不⽀持 SQL,多表聯查和複雜事務⽐較孱弱,不太適合存儲⼀般的數據。

但是,對於商品參數信息,數據量⼤、數據結構不統⼀,這些 MongoDB 都可以很好的滿⾜。我們也不需要事務和多表聯查,MongoDB 簡直就是為 了保存商品參數量身定製的⼀樣。不過最新的MySQL⽀持json了所以其實不⽤MongoDB也ok

5.支付與訂單問題:(重點!!)

訂單管理是怎麼實現的?

 

⽀付系統是怎麼實現的?

 

為什麼支付系統要獨立出來?

把業務和⽀付解耦開來。今天電商系統需要⽀付,明天活動系統需要⽀付,你只需要跳轉到⽀付系統就可以發起⽀付。在新增的業務系統的情況下,⽀付系統不需要動一行代碼,這就是解耦的優勢。

怎麼防止重覆下單?

假如說,⽤戶點擊“創建訂單”的按鈕時⼿⼀抖,點了兩下,瀏覽器發了兩個 HTTP 請求。

⾸先前端⻚⾯上應該防⽌⽤戶重覆提交表單,不過這個不可靠,因為⽹絡錯誤會導致重傳,很多⾮⽤戶操作的部分都會有⾃動重試機制。

大概分為2個方法:

  1. 利用資料庫唯⼀索引約束

    解決辦法是,讓你的訂單服務具備冪等性: 我們可以利⽤資料庫的主鍵唯⼀約束特性,在插⼊數據的時候帶上主鍵,來解決創建訂單服務的冪等性問題。

    具體的做法是這樣的:我們給訂單系統增加⼀個“⽣成訂單號”的服務,這個服務的返回值就是⼀個新的、全局唯⼀的訂單號。(企業級是使用分散式唯一id,但是這個項目使用的是當前系統時間 + 一個隨機值從而實現訂單號大概率是唯一的。

    在⽤戶進⼊創建訂單的頁面時,先調⽤這個⽣成訂單號服務得到⼀個訂單號,在⽤戶提交訂單的時候,在創建訂單的請求中帶著這個訂單號。

    這個訂單號也是我們訂單表的主鍵,這樣,⽆論是⽤戶手抖, 還是各種情況導致的重覆請求,這些請求中帶的都是同⼀個訂單號。訂單服務在訂單表中插⼊數據的時候,執⾏的這些重覆 INSERT 語句中的主鍵,也都是同⼀個訂單號。資料庫的唯⼀約束就可以保證,只有⼀次 INSERT 語句是執⾏成功的,這樣就實現了創建訂單服務冪等性。

    還有⼀點需要註意的是,如果是因為重覆訂單導致插⼊訂單表失敗,訂單服務不要把這個錯誤返回給前端⻚⾯。否則,就有可能出現這樣的情況:⽤戶點擊創建訂單按鈕後, ⻚⾯提示創建訂單失敗,⽽實際上訂單卻創建成功了。

    正確的做法是,遇到這種情況, 訂單服務直接返回訂單創建成功就可以了。

  2. 利⽤Redis防重

    下單請求的時候加鎖,通常我們的服務都是集群部署,所以一般是⽤Redis實現分散式鎖。 ⼤概的邏輯: 以requestId為維度,進⾏加鎖,如果獲取鎖失敗,就拋⼀個⾃定義的重覆下單異常。 如果獲取到鎖,先check⼀下,是否已經下單,為了提⾼性能,下單完成後,也把下單 的結果放在Redis緩存⾥。

 

如何解決客戶的惡意下單問題?

封IP,nginx中設置,單個IP訪問頻率和次數多了之後進行拉⿊操作。

如果用戶支付成功,但是看到訂單的狀態還是未支付,於是又去支付了⼀次,會重覆支付嗎?

⽤戶再次發起⽀付請求的時候,⽀付系統會帶著要⽀付的訂單的ID來⽣成⽀付信息⼊庫,⽀付信息中的訂單ID是加了唯⼀索引的,只要是同⼀個訂單是沒辦法進⾏重覆⽀付的。

訂單id是怎麼生成的?怎麼保證唯⼀性?

如果單純是⽣成全局唯⼀ID⽅法有很多,⽐如⼩規模系統完全可以⽤MySQL的sequence表,可以每次請求從sequence表⾥拿⼀組id,放到記憶體,⽤完了再拿。或者 Redis單線程原⼦遞增操作來⽣成。⼤規模系統也可以採⽤類似雪花演算法之類的⽅式分佈 式⽣成全局唯⼀ID。也可以⽤uuid,不過容易導致資料庫⻚分離,這個也可以解決,訂 單號設為唯⼀索引約束即可,仍然有個⾃增主鍵,訂單號對外⽤,主鍵不暴露,這樣表 空間還是緊密的,其他關聯訂單表仍然以訂單號作外鍵,不過不⽤訂單號作主鍵的話導 致的就是多⾛⼀次索引,⽽且還會影響插⼊更新性能,各有優劣。 但是訂單號這個東⻄⼜有點⼉特殊要求,⽐如在訂單號中最好包含⼀些品類、時間等信 息,便於業務處理,再⽐如,訂單號它不能是⼀個單純⾃增的ID,否則別⼈很容易根據 訂單號計算出你⼤致的銷量,所以訂單號的⽣產演算法在保證不重覆的前提下,⼀般都會 加⼊很多業務規則在⾥⾯,這個每家都不⼀樣,算是商業秘密吧。 同時還得儘可能的短。

用戶下單這個時刻,正好趕上商品調價怎麼處理?

假如說商品可以單獨下單的話。⾸先,商品系統需要保存包含價格的商品基本信息的歷史數據,對每⼀次變更記錄⼀個⾃增的版本號。

在下單的請求中,不僅要帶上SKUID, 還要帶上版本號。訂單服務以請求中的商品版本對應的價格來創建訂單,就可以避免下單時突然變價的問題了。 但是,這樣改正之後會產⽣⼀個很嚴重的系統漏洞:⿊客有 可能會利⽤這個機制,以最便宜的歷史價格來下單。所以,我們在下單之前需要增加⼀個檢測邏輯:請求中的版本號只能是當前版本或者上⼀個版本,並且使⽤上⼀個版本要有⼀個時間限制,⽐如說調價5秒之後,就不再接受上⼀個版本的請求。這樣就可以避免這個調價漏洞了。 對於我們系統下單前必須先把商品加⼊購物⻋,所以如果購物⻋看到⼀個價格,下單時會重新檢查當前價格和最新價格,如果發⽣變更,⼀般會在下單頁面給出提醒,重新刷新當前頁面來解決。

假如支付完了微信⼀直不回調怎麼辦?

設定一個定時任務或者延遲消息,對沒有回調的⽀付訂單主動查詢狀態。

 

怎麼防止超賣?

使⽤了MySQL的事務,預設的隔離級別,因為讀是快照讀MVCC,所以加了排它鎖來當前讀,然後事務提交鎖才會釋放,通過悲觀鎖解決的超賣問題。同時對死鎖問題進⾏了優化,加鎖是按照商品id排序之後加鎖的。 可以將庫存放到Redis,然後利⽤Lua原⼦性地檢查庫存之後再更新扣減。

減庫存成功了,但是⽣成訂單失敗了,該怎辦?

⾮分散式的系統中使⽤Spring提供的事務功能即可。

6.購物車與Redis問題

購物車管理是怎麼實現的?

在項⽬中基於緩存Redis實現了⾼性能購物⻋。 緩存的數據類型⽤的是hash(是一種K-KV的存儲結構)。⾸先redis的key是⽤戶id,value是hash,hash⾥⾯的 key是商品id,value是商品id、商品在購物⻋中的數量、商品是否選中的結構體的Json 串。

<⽤戶id,<商品id,<商品id,商品num,商品selected>>>。

 

 

為什麼⽤Redis不直接⽤MySQL?⽤Redis丟數據 了怎麼辦?

因為購物⻋是寫多讀多的場景,可以⽤Redis的讀寫緩存模式來實現,讀寫都基於 redis 來操作能減少IO次數加快數據操作的性能,提升⽤戶體驗,同時還能減少對Mysql資料庫的壓⼒。 如果說怕丟數據的話可以⽤MySQL給Redis提供⼀個備份功能,主存儲還是⽤ Redis,⽐如寫完Redis之後通過MQ非同步備份到MySQL,這樣MySQL就起到⼀個備份的 作⽤,同時MySQL備份了數據假如Redis掛了MySQL可以提供降級服務保證可⽤性。

如果項⽬中的Redis掛掉,如何減輕資料庫的壓力?

組redis集群,主從模式、哨兵模式、集群模式。

主從模式中:如果主機宕機,使⽤slave of no one 斷開主從關係並且把從機升級為主機。

哨兵模式中:⾃動監控master / slave的運⾏狀態,基本原理是:⼼跳機制+投票裁決。 每個sentinel會向其它sentinel、master、slave定時發送消息(哨兵定期給主或者從和 slave發送ping包,正常則響應pong,ping和pong就叫⼼跳機制),以確認對⽅是否 “活”著,如果發現對⽅在指定時間內未回應,則暫時主觀認為對⽅已掛。 若master被判斷死亡之後,通過選舉演算法,從剩下的slave節點中選⼀台升級為 master。並⾃動修改相關配置。

7.MQ問題(一些mq的八股文罷了,慢慢更新)

為什麼要使⽤消息隊列?

  • 解耦

    A 系統跟其它各種亂七⼋糟的系統嚴重耦合,A 系統產⽣⼀條⽐較關鍵的數據,很多系 統都需要 A 系統將這個數據發送過來。A 系統要時時刻刻考慮 BCDE 四個系統如果掛了 該咋辦?要不要重發,要不要把消息存起來?頭髮都⽩了啊! 如果使⽤MQ,A 系統產⽣⼀條數據,發送到 MQ ⾥⾯去,哪個系統需要數據⾃⼰去 MQ ⾥⾯消費。如果新系統需要數據,直接從 MQ ⾥消費即可;如果某個系統不需要這 條數據了,就取消對 MQ 消息的消費即可。這樣下來,A 系統壓根⼉不需要去考慮要給 誰發送數據,不需要維護這個代碼,也不需要考慮⼈家是否調⽤成功、失敗超時等情 況。 通過⼀個MQ,Pub/Sub 發佈訂閱消息這麼⼀個模型,A 系統就跟其它系統徹底解耦 了。 ⽀付系統模塊,調⽤了電商系統模塊,互相之間的調⽤有可能以後增加了業務之後會很 複雜,維護起來很麻煩。但是其實這個調⽤是不需要直接同步調⽤接⼝的,如果⽤MQ 給它非同步化解耦,也是可以的

  • 非同步

    ⼀般互聯⽹類的企業,對於⽤戶直接的操作,⼀般要求是每個請求都必須在短時間內完成,這樣對⽤戶才是⽆感知的。 如果使⽤ MQ,那麼⽀付系統連續發送 3 條消息到 MQ 隊列中,假如耗時 5ms,A 系統 從接受⼀個請求到返迴響應給⽤戶,總時⻓是 3 + 5 = 8ms,對於⽤戶⽽⾔,其實感覺上 就是點個按鈕,8ms 以後就直接返回了,爽!⽹站做得真好,真快! 非同步化提升⽤戶體驗,同時消息隊列能保證消息的可靠;並且對⽐同步的⽅式,同步的 ⽅式會卡住⽤戶,還可能有⽐如瀏覽器不⼩⼼關閉或者⽤戶關閉,導致⻚⾯跳轉不了也 就⽆法修改訂單數據。

  • 削峰

    每天 0:00 到 12:00,A 系統⻛平浪靜,每秒併發請求數量就 50 個。結果每次⼀到 12:00 ~ 13:00 ,每秒併發請求數量突然會暴增到 5k+ 條。但是系統是直接基於 MySQL 的,⼤量的請求涌⼊ MySQL,每秒鐘對 MySQL 執⾏約 5k 條 SQL。 ⼀般的 MySQL,扛到每秒 2k 個請求就差不多了,如果每秒請求到 5k 的話,可能就直 接把 MySQL 給打死了,導致系統崩潰,⽤戶也就沒法再使⽤系統了。 但是⾼峰期⼀過,到了下午的時候,就成了低峰期,可能也就 1w 的⽤戶同時在⽹站上 操作,每秒中的請求數量可能也就 50 個請求,對整個系統⼏乎沒有任何的壓⼒。 如果使⽤ MQ,每秒 5k 個請求寫⼊ MQ,A 系統每秒鐘最多處理 2k 個請求,因為 MySQL 每秒鐘最多處理 2k 個。A 系統從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個請 求,不要超過⾃⼰每秒能處理的最⼤請求數量就 ok,這樣下來,哪怕是⾼峰期的時候, A 系統也絕對不會掛掉。⽽ MQ 每秒鐘 5k 個請求進來,就 2k 個請求出去,結果就導致 在中午⾼峰期(1 個⼩時),可能有⼏⼗萬甚⾄⼏百萬的請求積壓在 MQ 中。 這個短暫的⾼峰期積壓是 ok 的,因為⾼峰期過了之後,每秒鐘就 50 個請求進 MQ,但 是 A 系統依然會按照每秒 2k 個請求的速度在處理。所以說,只要⾼峰期⼀過,A 系統 就會快速將積壓的消息給解決掉。 系統使⽤了消息隊列,我們就能夠對流量更好的把控。簡單來說就是能夠扛住⾼併發⼤流量。

Mq怎麼選型?

我主要調研了⼏個主流的MQ,Kafka、Rabbitmq、Rocketmq 選型我們主要基於以下⼏個點去考慮:

  1. 由於我們系統的qps壓⼒⽐較⼤,所以性能是⾸要考慮的要素。 Kafka和Rocketmq 都是毫秒級別,Rabbitmq是微妙級別。

  2. 開發語⾔,由於我們的開發語⾔是java,主要是為了⽅便⼆次開發。 Kafka是Scala和Rocketmq都是Java,Rabbitmq是Erlang

  3. 對於⾼併發的業務場景是必須的,所以需要⽀持分散式架構的設計。 Kafka和Rocketmq 都⽀持分散式架構,Rabbitmq是主從架構

  4. 功能全⾯,由於不同的業務場景,可能會⽤到順序消息、事務消息等。 Kafka只⽀持主要Mq功能⽐較單⼀,Rocketmq 還⽀持順序、事務消息等 我們的系統是⾯向⽤戶的C端系統,未來可能具有⼀定的併發量,對性能也有⽐較⾼的 要求,所以選擇了低延遲、吞吐量⽐較⾼,可⽤性⽐較好的RocketMQ。

如何保證消息隊列的⾼可⽤?

NameServer⾼可⽤ NameServer因為是⽆狀態,且不相互通信的,所以只要集群部署就可以保證⾼可⽤。

如何處理消息重覆?

對分散式消息隊列來說,同時做到確保⼀定投遞和不重覆投遞是很難的,就是所謂的“有,且僅有⼀次” 。 RocketMQ擇了確保⼀定投遞,保證消息不丟失,但有可能造成消息重覆。 ⽐如消費者消費消息時本地事務執⾏但是提交offset時宕機導致mq給消費者重覆推送消息;再⽐如⽣產者發送消息到mq時發送成功未獲取到響應然後進⾏消息發送重試導致消息發送多次。

處理消息重覆問題,主要由業務端⾃⼰保證,主要的⽅式有兩種:業務冪等消息去重

  • 業務冪等:

    第⼀種是保證消費邏輯的冪等性,也就是多次調⽤和⼀次調⽤的效果是⼀樣 的。這樣⼀來,不管消息消費多少次,對業務都沒有影響。 ⽐如你的業務邏輯就是寫Redis,那沒問題了,反正每次都是 set,天然冪等性。 再⽐如我們項⽬電商系統消費mq消息修改訂單狀態,只有未⽀付才能變成已⽀付,配合資料庫的悲觀鎖就天然具有業務冪等,假如訂單狀態不是未⽀付,就沒有辦法更新訂單 狀態了,直接拋異常。

  • 消息去重:

    第⼆種是業務端,對重覆的消息就不再消費了。這種⽅法,需要保證每條消息都有⼀個唯⼀的編號,通常是業務相關的,⽐如訂單號,消費的記錄需要入庫,⽽且 需要保證和消息確認這⼀步的原⼦性。 ⽐如可以建⽴⼀個消費記錄表,拿到這個消息做資料庫的insert操作。給這個消息做⼀ 個唯⼀主鍵或者唯⼀約束,那麼就算出現重覆消費的情況,就會導致主鍵衝突,那麼就不再處理這條消息。再比如讓⽣產者發送每條數據的時候,⾥⾯加⼀個全局唯⼀的id,類似訂單 id 之類的東⻄,然後你這⾥消費到了之後,先根據這個 id 去 Redis ⾥查⼀下,之前消費過嗎?如果 沒有消費過,你就處理,然後這個 id 寫 Redis。如果消費過了,那就別處理了,保證別 重覆處理相同的消息。 假如是插⼊操作,兜底的⽅案⽐如基於資料庫的唯⼀鍵來保證重覆數據不會重覆插⼊多條。因為有唯⼀鍵約束了,重覆數據插⼊只會報錯,不會導致資料庫中出現臟數據。

如何處理消息丟失的問題?

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一. 介紹: Java中的 "==" 是一個運算符,是用於比較兩個對象地址值或基本數據類型之間的值是否相等。它的來源可以追溯到C語言,以及受C語言影響的許多其他編程語言。 Java中的equals() 是一個方法,可重寫該方法用於比較兩個對象屬性內容是否相等的方法。該方法繼承自Object類,在Ja ...
  • # Java 包、訪問修飾符 # 1. 包 ## 包可以理解為創建不同的目錄來分別存放類,類似電腦當中文件夾 > ## 通過包可以讓相同的類在不同的目錄下使用,防止重名的問題 > > ## 通過包可以很好的管理我們編寫的類 > > ## 通過包可以控制訪問範圍 ## 使用 idea 工具創建包通過 ...
  • ## 1.準備工作 首先創建一個空的項目,然後再項目里創建三個模塊,分別為springboot-dubbo-interface,springboot-dubbo-provider,springboot-dubbo-consumer,其中springboot-dubbo-interface模塊只是一個 ...
  • ## JDBC 驅動載入 => 連接創建 => 創建編譯 / 預編譯語句 => 獲取結果集 => 遍歷結果集 => 返回結果集 | 介面 | | | | | | Driver | 驅動 | | Connection | 連接 | | Statement | 操作 | | ResultSet | 結果 ...
  • 引自:https://blog.csdn.net/weixin_43795921/article/details/127224633 template <typename IdentifierType, class AbstractProduct, class ProductCreator = Ab ...
  • 將bytes 轉換為long類型: 第一種方式: String 接收 bytes 的構造器轉成 String,再 Long.parseLong; 但此種情況需要註意:位元組數組中的每個位元組都必須是有效的數字字元。如果位元組數組包含非數字字元,則會引發NumberFormatException異常。確保在 ...
  • 一. 介紹 String 是Java.long包下的String類,是一個特殊的引用類型,用於表示字元串。它提供了許多方法來操作和處理字元串,比如連接、截取、查找、替換等。String類內部使用字元數組( char[] ) 來存儲字元串的內容,value欄位被final修飾,String對象一旦創建 ...
  • # 1. 配置文件 ## 1.1 外部載入順序 1. 命令行參數 java -jar spring-boot-02-config-02.0.0.1-SNAPSHOT.jar --server.port=8087 java -jar spring-boot-02-config-02.0.0.1-SNA ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...