即時聊天(IM)儲存方案

壹頁書發表於2015-11-24
單位即時聊天改版了.
正好趁著這次機會踢了MongoDB.
大家都拍手叫好..可見MongoDB這玩意多麼的不得人心...都覺得不好用..

場景描述:
我們的IM軟體有PC端和手機端.
同時線上的使用者,透過長連線轉發,並且儲存訊息.
接收方不線上,儲存訊息.
使用者開啟電腦端軟體或者手機端網路掉線重新連線,都需要獲取未讀訊息數量.當使用者點選未讀訊息的時候,提供訊息正文.

經過抽象,JAVA這塊需要提供兩個介面
1.獲取使用者的未讀訊息列表
2.給定傳送方ID和接收方ID,返回訊息內容.



傳送方使用者ID  srcid
接收方使用者ID  destid
每個會話的當前訊息ID  mid(針對每個傳送方和接收方,自增)

1.傳送方使用者透過電腦將訊息傳送至Web伺服器.訊息主要內容(srcid,destid,msg)

2.Web伺服器根據srcid,destid獲取會話的mid(會話狀態的自增ID)



3.將訊息放入持久化佇列,並且更新redis未讀訊息列表
首先將(srcid,destid,mid,msg)放入持久化佇列.
然後更新redis的使用者未讀訊息列表.

未讀訊息列表在Redis的儲存結構如下.

在接收方ID的前面增加一個字首,表明是手機未讀訊息還是PC未讀訊息.這個作為key
value是一個HashMap.這個HashMap的key是傳送方ID,value是未讀訊息數量.
一旦使用者登入,直接拉取該使用者的HashMap內容展示.

這部分內容是有過期時間的,假如使用者長時間未使用.這個PC-destid和Mobile-destid的條目將被刪除.
如果程式發現這個條目不存在.則去資料庫中查詢未讀訊息列表.



假如我的ID是1000,老闆的ID是1001,老闆給我傳送了三條訊息,但是我一直沒有線上.
程式將做如下操作.
127.0.0.1:6379> HINCRBY pc-1000 1001 1
(integer) 1
127.0.0.1:6379> HINCRBY pc-1000 1001 1
(integer) 2
127.0.0.1:6379> HINCRBY pc-1000 1001 1
(integer) 3

127.0.0.1:6379> HINCRBY mobile-1000 1001 1
(integer) 1
127.0.0.1:6379> HINCRBY mobile-1000 1001 1
(integer) 2
127.0.0.1:6379> HINCRBY mobile-1000 1001 1
(integer) 3

如果我登入手機客戶端,點選未讀訊息.則對Redis操作如下,刪除對應條目
127.0.0.1:6379> hdel mobile-1000 1001
(integer) 1
127.0.0.1:6379> 
並且回寫資料庫一個狀態標識(已讀的最大mid).這個回寫資料庫的過程,也是非同步的,每隔30s回寫一次資料庫狀態.


這個時候,我登入PC端,還是可以檢視未讀訊息數量的.這個業務要求有點奇怪.但是要求就是這麼做.

4.如果傳送方的其他裝置線上,或者接收方的裝置線上,則轉發訊息.

5.JAVA從佇列中非同步獲取訊息,然後批次Insert到資料庫.


資料庫儲存設計
初始設計使用4臺MySQL分庫.使用(傳送方使用者ID+接收方使用者ID) mod 64
這樣的好處是,A使用者傳送B使用者和B使用者傳送A使用者的訊息,都會落在同一個底層資料庫.
這樣獲取A,B之間的聊天內容,使用一個SQL查詢即可.




聊天訊息表,本身也是按照時間進行分割槽

  1. create table chat_msg(
  2.     id bigint auto_increment,
  3.     srcid bigint not null,
  4.     destid bigint not null,
  5.     mid bigint not null,
  6.     msg TEXT,
  7.     ts timestamp not null default current_timestamp,
  8.     hashvalue tinyint not null,
  9.     primary key (id,ts)
  10. )partition by range(UNIX_TIMESTAMP(ts))
  11. (
  12.     partition p201511 VALUES LESS THAN(UNIX_TIMESTAMP('2015-11-01 00:00:00')),
  13.     partition p201512 VALUES LESS THAN(UNIX_TIMESTAMP('2015-12-01 00:00:00')),
  14.     partition p201601 VALUES LESS THAN(UNIX_TIMESTAMP('2016-01-01 00:00:00'))
  15. );

使用者狀態表
sessionId是聊天雙方,(較小的ID在前,較大的ID在後) 的會話狀態資訊
pcMid是使用者pc端已讀的最大訊息ID
mobileMid是使用者手機端已讀的最大訊息ID
hashvalue是(傳送方使用者ID+接收方使用者ID) mod 64 計算後的值.暫時沒有用.

  1. create table read_chat_mid(
  2.     id bigint primary key auto_increment,
  3.     uid bigint not null,
  4.     sessionId varchar(45) not null,
  5.     pcMid bigint not null default 0,
  6.     mobileMid bigint not null default 0,
  7.     hashvalue tinyint not null,
  8.     ts timestamp not null default current_timestamp on update current_timestamp
  9. );

獲取使用者未讀訊息的SQL如下.

  1. create index inx_1 on chat_msg(ts,srcid,destid,mid);

  2. create unique index inx_2 on read_chat_mid(uid,sessionId);

查詢使用者1,2之間的聊天內容

  1. select mid,srcid,destid,msgpb,ts from im_0.chat_msg_3 where id in (
  2.     select t.id from(
  3.         select t1.id from
  4.         (
  5.             (select id from im_0.chat_msg_3 where srcid=1 and destid=2
  6.             and ts>now()-interval '1' month
  7.             and mid>ifnull((select mobileMid from im_0.read_chat_mid_3 where sessionId='1,2' and uid=1),0) order by ts desc limit 200)
  8.             union all
  9.             (select id from im_0.chat_msg_3 where srcid=2 and destid=1
  10.             and ts>now()-interval '1' month
  11.             and mid>ifnull((select mobileMid from im_0.read_chat_mid_3 where sessionId='1,2' and uid=1),0) order by ts desc limit 200)
  12.         ) as t1 order by id desc limit 200
  13.     ) as t
  14. ) order by mid desc;

系統效能評估

MySQL基本配置
innodb_buffer_pool_size=512m
innodb_flush_log_at_trx_commit =0
sync_binlog=0
innodb_support_xa=0
log_bin=master

下面是工作環境測試,在生產伺服器上使用raid10,IO能力還會進一步提升.
每個月每臺資料庫伺服器 在200w記錄之前,使用Load File介面,每秒Insert可以達到 1.7w左右
每個月每臺資料庫伺服器 在800w記錄之前,使用Load File介面,每秒Insert可以達到 1.1w左右
可以認為系統每秒Insert至少在4w以上.
理論上的系統最大處理能力至少在每秒64w以上.

僅僅是四臺資料庫的情況,對於我們的業務.5年之內應該都不用考慮擴容的問題.

讓DBA做架構設計的好處是,不會把坑留在資料庫層面坑自己...

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1845643/,如需轉載,請註明出處,否則將追究法律責任。

相關文章