Redis基于Session實(shí)現(xiàn)分布式登錄的示例代碼
1.流程分析

1.1發(fā)送短信驗(yàn)證碼
提交手機(jī)號(hào)的時(shí)候要進(jìn)行校驗(yàn)手機(jī)號(hào),校驗(yàn)成功才會(huì)去生成驗(yàn)證碼,將驗(yàn)證碼保存到session,發(fā)生他把這部分那。
1.2短信驗(yàn)證碼登錄/注冊(cè)
如果提交手機(jī)號(hào)和驗(yàn)證碼之后,校驗(yàn)一致才進(jìn)行根據(jù)手機(jī)號(hào)查詢用戶,進(jìn)行創(chuàng)建新用戶/登錄成功,將信息保存到session進(jìn)行返回。
1.3校驗(yàn)登錄狀態(tài)
前端傳遞過(guò)來(lái)cookie,攜帶其中的sessionID,從session中獲取到用戶信息,校驗(yàn)是否存在,存在就將用戶保存到TheadLocal,不存在就攔截。
2.實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼
利用一些封裝的工具生成驗(yàn)證碼和校驗(yàn)手機(jī)號(hào)。
利用session進(jìn)行存儲(chǔ)驗(yàn)證碼,方便校驗(yàn),比較不錯(cuò)。
@Override
public Result sendCode(String phone, HttpSession session) {
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手機(jī)號(hào)格式錯(cuò)誤!");
}
// 3. 生成驗(yàn)證碼
String code = RandomUtil.randomNumbers(6);
// 2. 保存驗(yàn)證碼到session
session.setAttribute(LOGIN_CODE_KEY + phone, code);
// 發(fā)送驗(yàn)證碼
// todo 實(shí)現(xiàn)發(fā)送驗(yàn)證碼
log.debug("發(fā)送短信驗(yàn)證碼成功: {}", code);
// 返回OK
return Result.ok();
}
3.短信驗(yàn)證碼登錄
最重要的可能是封裝的思想吧,封裝一定的常量和借助mybatis-plus的高級(jí)的功能。
重點(diǎn):抽取邏輯,mybatis-plus高級(jí)功能。
/**
* 實(shí)現(xiàn)登錄功能
*
* @param loginForm
* @param session
* @return
*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1. 校驗(yàn)手機(jī)號(hào)
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手機(jī)號(hào)格式不正確");
}
// 2. 校驗(yàn)驗(yàn)證碼
// 從session中獲取到驗(yàn)證碼
String phoneForCode = (String) session.getAttribute(LOGIN_CODE_KEY + phone);
// 校驗(yàn)驗(yàn)證碼
String code = loginForm.getCode();
if (!StrUtil.equals(code, phoneForCode)) {
return Result.fail("驗(yàn)證碼錯(cuò)誤!");
}
// 3. 查詢用戶是否存在
// QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
// userQueryWrapper.eq("phone", phone);
// User user = userMapper.selectOne(userQueryWrapper);
User user = query().eq("phone", phone).one();
// 不存在則注冊(cè)
if (user == null) {
user = createUserWithPhone(phone);
}
// 4. 保存信息到session中
session.setAttribute("user", user);
return Result.ok();
}
/**
* 封裝一個(gè)創(chuàng)建用戶的邏輯
*
* @param phone
* @return
*/
private User createUserWithPhone(String phone) {
// 1. 創(chuàng)建用戶
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
// 2. 保存用戶
save(user);
return user;
}
4.登錄驗(yàn)證功能
需要封裝一個(gè)登錄校驗(yàn)的功能供前端進(jìn)行調(diào)取使用。
重點(diǎn):通過(guò)SpringMVC進(jìn)行攔截請(qǐng)求,封裝數(shù)據(jù)到ThreadLocal中。
使用SpringMVC統(tǒng)一攔截請(qǐng)求可以方便將數(shù)據(jù)存儲(chǔ)到ThreadLocal中,這樣就無(wú)需在每個(gè)接口中進(jìn)行配置了十分方便。
ThreadLocal是每個(gè)tomcat創(chuàng)建的請(qǐng)求線程中獨(dú)有的,不會(huì)被其它線程訪問(wèn)到的。
封裝攔截器,從session中獲取到數(shù)據(jù)即可,最后一定要在請(qǐng)求后攔截器中將ThreadLocal中的數(shù)據(jù)刪除。
package com.hmdp.utils;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 登錄攔截器
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 獲取sesson
HttpSession session = request.getSession();
// 2. 獲取用戶信息
User user = (User) session.getAttribute("user");
// 3. 處理用戶不存在
if (user == null) {
response.setStatus(401);
return false;
}
// 4. 存儲(chǔ)數(shù)據(jù)到ThreadLocal
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
UserHolder.saveUser(userDTO);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}將攔截器配置到SpringBoot中,可以進(jìn)行配置什么路徑需要排除,什么無(wú)需排除。
/**
* MVC攔截器配置
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(
"/shop/**",
"/shop-type/**",
"/upload/**",
"/voucher/**",
"/blog/hot",
"/user/code",
"/user/login"
).order(1);
}
}
通過(guò)攔截器可以完成登錄校驗(yàn)功能。

5.集群session共享問(wèn)題
多臺(tái)tomcat是并不進(jìn)行共享session存儲(chǔ)空間的,雖然tomact提供了將session共享到多臺(tái)tomcat,但是這樣性能太差了,還會(huì)有很多問(wèn)題,所以為了進(jìn)行實(shí)現(xiàn)分布式服務(wù)器tomcat共享session,建議使用redis進(jìn)行替代session,這樣就可以做到分布式session了。

6.基于redis實(shí)現(xiàn)
其實(shí)就是使用redis去利用鍵值對(duì)的形式進(jìn)行存儲(chǔ)用戶的信息.
key的設(shè)計(jì):項(xiàng)目名:業(yè)務(wù)名:類型:id。
這里只使用項(xiàng)目名,類型和id,使用這種結(jié)構(gòu)式key可以大大的幫助到我們。

其實(shí)就是把使用session的部分換為使用redis了,存儲(chǔ)key-value,取出key-value都使用redis即可。
重要點(diǎn):使用token令牌替代了cookie。
使用redis的時(shí)候,一定要使用token嗎?未必的,只是說(shuō)將token作為一個(gè)幫助客戶端和服務(wù)端之間進(jìn)行身份認(rèn)證的手段,完全也可以進(jìn)行使用分布式session,使用redis存儲(chǔ)session,前端依然攜帶cookie而來(lái),所以這只是一種手段而已,各有所長(zhǎng)。

6.1獲取驗(yàn)證碼
session => redis。
沒(méi)有太多亮點(diǎn),就簡(jiǎn)單分析一下key的構(gòu)造吧。
login:code:phone,典型的業(yè)務(wù)+類型+分辨標(biāo)識(shí)key,這樣就很好的能架構(gòu)出合理的key了。
public Result sendCode(String phone, HttpSession session) {
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手機(jī)號(hào)格式錯(cuò)誤!");
}
// 1. 生成驗(yàn)證碼
String code = RandomUtil.randomNumbers(6);
// 2. 保存驗(yàn)證碼到session
// session.setAttribute(LOGIN_CODE_KEY, code);
// 2. 保存驗(yàn)證碼到redis
// 構(gòu)造key
String key = LOGIN_CODE_KEY + phone;
stringRedisTemplate.opsForValue().set(key, code, LOGIN_CODE_TTL, TimeUnit.SECONDS);
// 3. 發(fā)送驗(yàn)證碼
// todo 實(shí)現(xiàn)發(fā)送驗(yàn)證碼
log.debug("發(fā)送短信驗(yàn)證碼成功: {}", code);
// 返回OK
return Result.ok();
}6.2注冊(cè)/登錄
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1. 校驗(yàn)手機(jī)號(hào)
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手機(jī)號(hào)格式不正確");
}
// 2. 校驗(yàn)驗(yàn)證碼
// 從session中獲取到驗(yàn)證碼
// String phoneForCode = (String) session.getAttribute(LOGIN_CODE_KEY);
// 從redis中獲取到驗(yàn)證碼
String codeKey = LOGIN_CODE_KEY + phone;
String phoneForCode = stringRedisTemplate.opsForValue().get(codeKey);
// 校驗(yàn)驗(yàn)證碼
String code = loginForm.getCode();
if (!StrUtil.equals(code, phoneForCode)) {
return Result.fail("驗(yàn)證碼錯(cuò)誤!");
}
// 3. 查詢用戶是否存在
// QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
// userQueryWrapper.eq("phone", phone);
// User user = userMapper.selectOne(userQueryWrapper);
User user = query().eq("phone", phone).one();
// 不存在則注冊(cè)
if (user == null) {
user = createUserWithPhone(phone);
}
// 4. 保存信息到session中
// session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
// 4. 保存信息到redis中
// 4.1 隨機(jī)生成token, 作為登錄令牌
String token = UUID.randomUUID().toString();
// 4.2 將User對(duì)象轉(zhuǎn)換為Hash存儲(chǔ)
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()));
// 4.3 存儲(chǔ)數(shù)據(jù)到redis
String userKey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll(userKey, userMap);
// 4.4 設(shè)置
stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
return Result.ok(token);
}6.2.1key的設(shè)計(jì)
其中key的設(shè)計(jì)是login:token:token,也是遵循的業(yè)務(wù)+類型+標(biāo)識(shí)的設(shè)計(jì)思想。
6.2.2token的設(shè)計(jì)
這里的token很值得分析一下,尤其是可以對(duì)比蒼穹外賣的進(jìn)行分析。
這里的key僅僅是用來(lái)作為一個(gè)獲取redis中數(shù)據(jù)使用的,并不是加密攜帶payload負(fù)載數(shù)據(jù)的,
其實(shí)就是使用了token替代了sessionID,但是其實(shí)還是有一個(gè)更為方便的解決方法的。
6.2.3存儲(chǔ)hash數(shù)據(jù)到redis中的注意事項(xiàng)
1.使用什么API?
使用的是StringRedisTemplate中的opsForHash.putAll(),這個(gè)方法接收兩個(gè)參數(shù),key => 字符串,value => Map,它可以將Map中的key-value全部存入redis的hash中,十分方便。
2.Map中數(shù)據(jù)規(guī)范是什么樣的?
由于我們進(jìn)行使用的Redis客戶端是stringRedisTemplate,這就限制了我們存儲(chǔ)hash數(shù)據(jù)的時(shí)候,map中的key-value都必須是string類型的,如果出現(xiàn)了其它類型:比如Long類型,就會(huì)拋出錯(cuò)誤,所以我們?cè)趯TO轉(zhuǎn)換為Map的時(shí)候,必須對(duì)value進(jìn)行處理。
借助hutool工具類中的BeanUtil.beanToMap就可以完成這個(gè)操作。
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
CopyOptions.create().setFieldValueEditor((filedName, filedValue) -> filedValue.toString()));6.2.4Redis中Key設(shè)置時(shí)間的問(wèn)題
使用stringRedisTemplate中的expire進(jìn)行設(shè)置key的存活時(shí)間,傳入key,time,TimeUnit即可。
6.3登錄認(rèn)證攔截器
我們需要將以前使用session進(jìn)行將數(shù)據(jù)取出存入ThreadLocal的邏輯變更為使用前端傳遞來(lái)的token進(jìn)行獲取數(shù)據(jù),從redis中獲取用戶數(shù)據(jù)DTO進(jìn)行使用。
6.3.1思考:沒(méi)有被SpringIOC托管的對(duì)象如何注入Bean
鑒于我們的登錄攔截器配置類是我們自定義的,并且沒(méi)有托管到SpringIOC容器,所以我們不能使用Resouce/Autowired。
那應(yīng)該怎么辦呢?我們發(fā)現(xiàn)MVC攔截器配置類是使用@Configuration進(jìn)行注解的,這個(gè)類會(huì)被托管到SpringIOC容器,而且我們的自定義攔截器也被該類實(shí)例化了一個(gè)對(duì)象,所以完全可以通過(guò)該類將Bean在自定義攔截器實(shí)例化的時(shí)候,傳遞進(jìn)來(lái)。
在構(gòu)造函數(shù)被回調(diào)的時(shí)候,接收StringRedisTemplate對(duì)象,進(jìn)行賦值給自身字段。
/**
* 登錄攔截器
*/
public class LoginInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
}將StringRedisTemplate進(jìn)行注入到MVC配置類中,在調(diào)用攔截器構(gòu)造函數(shù)的時(shí)候進(jìn)行注入StringRedisTemplate進(jìn)去即可。
/**
* MVC攔截器配置
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(
"/shop/**",
"/shop-type/**",
"/upload/**",
"/voucher/**",
"/blog/hot",
"/user/code",
"/user/login"
).order(1);
}
}6.3.2整體登錄校驗(yàn)流程
/**
* 登錄攔截器
*/
public class LoginInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 獲取sesson
// HttpSession session = request.getSession();
// 1. 獲取請(qǐng)求頭中的token
String token = request.getHeader("authorization");
if (StrUtil.isBlank(token)) {
response.setStatus(401);
return false;
}
// 2. 獲取用戶信息
// UserDTO user = (UserDTO) session.getAttribute("user");
String userKey = LOGIN_USER_KEY + token;
Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(userKey);
// 3. 處理用戶不存在
if (userMap.isEmpty()) {
response.setStatus(401);
return false;
}
// 4. 將用戶數(shù)據(jù)map -> userDTO
UserDTO user = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
// 5. 存儲(chǔ)數(shù)據(jù)到ThreadLocal
UserHolder.saveUser(user);
// 6. 刷新token有效期
stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 7. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}1.整體流程:先從request請(qǐng)求頭中獲取到authorization中的token數(shù)據(jù) => 然后封裝一個(gè)userkey => 然后封裝出來(lái)一個(gè)KEY,在redis客戶端中獲取到userDTO數(shù)據(jù) => 然后處理用戶不存在(redis獲取不到數(shù)據(jù)的情況)=> 將用戶數(shù)據(jù)map轉(zhuǎn)換為userDTO => 將用戶DTO存儲(chǔ)到ThreadLocal中即可 => 最后進(jìn)行刷新token有效期。
6.3.3刷新token時(shí)間
token在一定時(shí)間后會(huì)過(guò)期,但是如果在用戶持續(xù)使用的過(guò)程中過(guò)期,那真是一個(gè)糟糕的事件,所以要采用攔截器刷新token時(shí)間的方式,這樣就是在用戶持續(xù)使用的時(shí)候,可以幫用戶進(jìn)行刷新token,延期,不會(huì)導(dǎo)致用戶持續(xù)使用的時(shí)候過(guò)期。
在攔截器中進(jìn)行token續(xù)期,是一個(gè)非常聰明的決策,這里采用的續(xù)期策略是,只要發(fā)送了請(qǐng)求,在token有效期內(nèi)還在使用,就將時(shí)間續(xù)期到原始狀態(tài)。
// 6. 刷新token有效期 stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
7.登錄攔截器和刷新緩存攔截器如何設(shè)置?
7.1拆分的思路
主要是因?yàn)橛幸恍┱?qǐng)求是打不到登錄攔截器的,一些不需要登錄的請(qǐng)求根本打不到登錄校驗(yàn)攔截器,所以我們不能將刷新token時(shí)間放在登錄攔截器中去做,因?yàn)檫@樣就會(huì)有可能用戶看的都是不需要登錄的接口數(shù)據(jù),這樣就會(huì)導(dǎo)致token無(wú)法進(jìn)行續(xù)期,有可能出現(xiàn)用戶看著看著就token過(guò)期了,所以為了避免這種情況的發(fā)生,可以進(jìn)行設(shè)置兩個(gè)攔截器:1.刷新token攔截器,所有接口都可以進(jìn)行刷新token請(qǐng)求,當(dāng)用戶進(jìn)行發(fā)送請(qǐng)求之后,從redis中獲取到用戶的token數(shù)據(jù)(因?yàn)榍岸藭?huì)將token傳遞過(guò)來(lái)),當(dāng)查詢到數(shù)據(jù)的時(shí)候,就會(huì)去更新token時(shí)間,如果有數(shù)據(jù)將數(shù)據(jù)存儲(chǔ)到ThreadLocal中。2.登錄狀態(tài)攔截器,僅僅進(jìn)行攔截需要登錄狀態(tài)才能進(jìn)行訪問(wèn)的接口,在攔截器中進(jìn)行看一下ThreadLocal是否有數(shù)據(jù),有就放行,無(wú)則滾蛋。
7.2實(shí)現(xiàn)token刷新攔截器
/**
* 更新攔截器
*/
public class RefreshInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public RefreshInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 獲取sesson
// HttpSession session = request.getSession();
// 1. 獲取請(qǐng)求頭中的token
String token = request.getHeader("authorization");
if (StrUtil.isBlank(token)) {
return true;
}
// 2. 獲取用戶信息
// UserDTO user = (UserDTO) session.getAttribute("user");
String userKey = LOGIN_USER_KEY + token;
Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(userKey);
// 3. 處理用戶不存在
if (userMap.isEmpty()) {
return true;
}
// 4. 將用戶數(shù)據(jù)map -> userDTO
UserDTO user = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
// 5. 存儲(chǔ)數(shù)據(jù)到ThreadLocal
UserHolder.saveUser(user);
// 6. 刷新token有效期
stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 7. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}7.3實(shí)現(xiàn)登錄攔截器
/**
* 登錄攔截器
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (UserHolder.getUser() == null) {
response.setStatus(401);
return false;
}
return true;
}
}7.4SpringMVC配置攔截器
攔截器的優(yōu)先級(jí):可以在registry注冊(cè)的時(shí)候進(jìn)行指定order,里面的排序數(shù)越小的越先執(zhí)行,如果不進(jìn)行指定優(yōu)先級(jí),就按代碼的順序進(jìn)行注冊(cè),越靠上進(jìn)行注冊(cè)的攔截器,越先執(zhí)行。
這里將刷新token的攔截器放在最前面,指定的順序數(shù)是最小的。
/**
* MVC攔截器配置
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(
"/shop/**",
"/shop-type/**",
"/upload/**",
"/voucher/**",
"/blog/hot",
"/user/code",
"/user/login"
).order(1);
registry.addInterceptor(new RefreshInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
}
}到此這篇關(guān)于Redis基于Session實(shí)現(xiàn)分布式登錄的示例代碼的文章就介紹到這了,更多相關(guān)Redis Session分布式登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot集成redis與session實(shí)現(xiàn)分布式單點(diǎn)登錄
- redis中使用java腳本實(shí)現(xiàn)分布式鎖
- Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)
- 基于Redis實(shí)現(xiàn)分布式鎖以及任務(wù)隊(duì)列
- Redis分布式鎖的實(shí)現(xiàn)方式(redis面試題)
- Redis實(shí)現(xiàn)分布式鎖的五種方法詳解
- 淺談Redis分布式鎖的正確實(shí)現(xiàn)方式
- Redis分布式鎖實(shí)現(xiàn)方式及超時(shí)問(wèn)題解決
- redis分布式鎖及會(huì)出現(xiàn)的問(wèn)題解決
相關(guān)文章
Linux服務(wù)器安裝redis數(shù)據(jù)庫(kù)圖文教程
Redis是一個(gè)開(kāi)源的使用ANSI C語(yǔ)言編寫(xiě)、遵守BSD協(xié)議、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的API。這篇文章主要介紹了Linux服務(wù)器安裝redis數(shù)據(jù)庫(kù)圖文教程,需要的朋友可以參考下2018-03-03
Redis中Zset類型常用命令的實(shí)現(xiàn)
Zset是Redis的一種有序集合數(shù)據(jù)類型,Zset通過(guò)壓縮列表和跳躍表兩種底層編碼方式支持小數(shù)據(jù)集和大數(shù)據(jù)集,支持多種操作,包括添加、查詢、刪除元素以及集合運(yùn)算等,具有不同的時(shí)間復(fù)雜度,感興趣的可以了解一下2024-10-10
Redis結(jié)合 Docker 搭建集群并整合SpringBoot的詳細(xì)過(guò)程
這篇文章主要介紹了Redis結(jié)合Docker搭建集群并整合SpringBoot的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06
Redis事務(wù)機(jī)制與Springboot項(xiàng)目中的使用方式
Redis事務(wù)機(jī)制允許將多個(gè)命令打包在一起,作為一個(gè)原子操作來(lái)執(zhí)行,開(kāi)啟事務(wù)使用MULTI命令,執(zhí)行事務(wù)使用EXEC命令,取消事務(wù)使用DISCARD命令,監(jiān)視一個(gè)或多個(gè)鍵使用WATCH命令,Redis事務(wù)的核心思想是將多個(gè)命令放入一個(gè)隊(duì)列中2025-03-03

