java配置SSM純註解整合Redis開發高併發搶紅包項目

前言:

前段時間學習點Redis,這次結合ssm實現一個高併發搶紅包的項目。
跟以前不一樣的:

  1. 基於java配置SSM,而並非XML.爲什麼要這樣呢?
    找了點網絡上的答案:
在使用Spring開發時,我們經常會看到各種各樣xml配置,過於繁多的xml配置顯得複雜煩人。  
在Spring3之後,Spring支持使用JavaConfig來代替xml配置,  
這種方式也得到越來越多人的推薦,甚至在Spring Boot的項目中,基本上已經見不到xml的影子了。
  1. 搶紅包的記錄使用Lua保存到Redis內存中,最後再異步使用批量事務插入到Mysql。
    目的:Redis內存響應速度比Mysql硬盤響應速度快。

這個項目的目的也是爲了實現上面的兩個內容。
具體的項目我已經放到Github上了:有興趣的朋友可以下載看看,裏面也都寫好了註釋,如果有不明白的,可以聯繫我QQ:1115106468 一起交流

https://github.com/jjc123/Grab_RED_PACKET/blob/master/README.md

問題:此項目中高併發爲了使用Redis?

如果使用Mysql直接保存,也可以,那如何解決數據不一致的問題?
可以使用樂觀鎖和悲觀鎖,此次項目中我使用的是Lua+Redis,看一下這三者區別:
悲觀鎖:
使用數據庫的鎖機制,併發的過程中同時只能有一個線程操作數據,其他得不到資源的線程就會被掛起,過程中會頻繁得被掛起和恢復,導致cpu頻繁切換現場上下文。
可以通過for update語句鎖行如果是使用主鍵查詢如where id=#{id}是鎖行,如果是非主鍵查詢要考慮是否對全表加鎖。可以消除數據不一致性,但是性能會下降,因爲他是阻塞性的,而且需要大量的恢復過程。
樂觀鎖:
不會阻塞併發,是非阻塞鎖,他是具有CAS原理,但是會有ABA問題(CAS和ABA具體百度,更詳細),可以通過添加版本號,但是會導致多次請求服務失敗的概率大大提高,可以通過重入的方法(按時間戳或者次數限定)來提高成功率,但是會導致大量SQL被執行,容易引發性能瓶頸。
Lua+Redis:
正是因爲上面集中方法的侷限性,所以選擇Redis去實現高併發,因爲Lua具有原子性,消除了數據不一致。而且Redis是保存在內存中的,響應速度極快。
注意:
爲了不影響最後搶一個紅包的響應時間,在最後一次操作數據的時候,開啓一個新的線程,批量處理數據插入Mysql。而且最後還會刪除Redis中批量處理的數據。


現在正是開始這個高併發的搶紅包項目吧!

首先我們先配置數據庫:

兩個表:
T_RED_PACKET存紅包信息
T_USER_RED_PACKET存用戶搶紅包信息

create table T_RED_PACKET(
id int(12) not null auto_increment,
user_id int(12) not null,
amount decimal(16,2) not null,
send_date timestamp not null,
total int(12) not null,
unit_amount decimal(12) not null,
stock int(12) not null,
version int(12) default 0 not null,
note varchar(256) null,
primary key clustered(id)
);
create table T_USER_RED_PACKET(
id int(12) not null auto_increment,
red_packet_id int(12) not null,
user_id int(12) not null,
amount decimal(16,2) not null,
grab_time timestamp not null,
note varchar(256) null,
primary key clustered (id) 
);
insert into T_RED_PACKET(user_id,amount,send_date,total,unit_amount,stock,note)
values(1,200000.00,now(),20000,10.00,20000,'20萬元金額,2萬個小紅包 每個10元');

注意:

amount :金額 要用decimal而不是double
send_date :發紅包時間
stock:剩餘的紅包數
primary key clustered (id) 是設置主鍵和聚集索引

題外話:
Ubuntu下設置MySQL字符集爲utf8

1.mysql配置文件地址
/etc/mysql/my.cnf

2.在[mysqld]在下方添加以下代碼
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake

3.重啓mysql服務
sudo service mysql restart

4.檢測字符集是否更新成utf8.

進入mysql,mysql -u root -p,輸入show variables like '%character%' 查看字符集
Variable_name Value
character_set_client utf8
character_set_connection utf8
character_set_database utf8
character_set_filesystem binary
character_set_results utf8
character_set_server utf8
character_set_system utf8
character_sets_dir /usr/share/mysql/charsets/

配置Redis:

注意:redis的數據即使電腦關機下次打開redis的時候還會存在

進入redis的src然後./redis-server ../redis.conf &指定配置文件啓動而且是後臺啓動
再打開新的客戶端:./redis-cli
127.0.0.1:6379> hset red_packet_2 stock 20000 (保存紅包庫存 也可以用來直接修改)
(integer) 0
127.0.0.1:6379> hset red_packet_2 unit_amount 10 (保存單個紅包庫存)
(integer) 0
題外話:

hget red_packet_2 stock   hash獲取該鍵的值
ltrim red_packet_list_2 1 0  清空list對應鍵的值
RPUSH red_packet_list_2 c  list添加單個元素
LRANGE red_packet_list_2 0 -1 獲取對應list的所有值
LLEN red_packet_list_2 獲取list長度

這些外部內容已經配置好了,具體的項目我也放到了github上 有興趣的朋友可以看看。

需要注意的是ubuntu每次開機運行項目的時候老是我的端口被佔用:
查看端口:
sudo netstat -lnp | grep 8082
殺死佔用端口進程:
sudo kill-9 進程號

注意:使用jdbc批量處理的時候需要:url添加rewriteBatchedStatements=true

我的測試數據:
url添加rewriteBatchedStatements=true

開始保存數據
耗時:5917插入數量:20000

url不添加rewriteBatchedStatements=true

開始保存數據
耗時:13315插入數量:20000

所以批量處理一定要加上rewriteBatchedStatements=true

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章