1.redis源码学习-quicklist篇
2.服务器开发学习中间件:Redis 码学缓存中间件
3.redis7.0源码阅读:Redis中的IO多线程(线程池)
4.å°åºå¦ä½å¨springä¸ä½¿ç¨redis
redis源码学习-quicklist篇
Redis源码中的quicklist是ziplist优化版的双端链表,旨在提高内存效率和操作效率。码学ziplist虽然内存使用率高,码学但查找和增删操作的码学最坏时间复杂度可能达到O(n^2),这与Redis高效数据处理的码学要求不符。quicklist通过每个节点独立的码学柳州打车app源码ziplist结构,降低了更新复杂度,码学同时保持了内存使用率。码学
quicklist的码学基本结构包括:头节点(head)、尾节点(tail)、码学entry总数(count)、码学节点总数(len)、码学容量指示(fill)、码学压缩深度(compress)、码学以及用于内存管理的码学bookmarks。节点结构包括双向链表的prev和next,ziplist的引用zl,ziplist的字节数sz、item数count、phpqq业务源码以及ziplist类型(raw或lzf压缩)和尝试压缩标志(attempted_compress)。
核心操作函数如create用于初始化节点,insert则根据需求执行头插法或尾插法。delete则简单地从链表中移除节点,释放相关内存。quicklist的优化重点在于ziplist,理解了ziplist的工作原理,quicklist的数据结构理解就相对容易了。
服务器开发学习中间件:Redis 缓存中间件
为什么需要缓存中间件?
在网站的用户量增加,引发并发量提升的情况下,频繁查询数据库会导致页面显示缓慢,服务器与数据库压力增大。如果页面展示数据更新不频繁,为了加速页面显示与减轻服务器负担,应考虑使用缓存。缓存位于应用程序与物理数据源之间,主要目的是减少应用程序对物理数据源的访问频率,从而提高应用性能。hoorayos 源码下载缓存存储的数据是对物理数据源的复制,应用程序在运行时从缓存读写数据,在特定时刻或事件时同步缓存与物理数据源的数据。
使用缓存遵循高层次缓存原则,推荐使用页面缓存。
Redis简介
Redis是一个键值存储系统,支持多种数据类型,包括字符串、链表、集合、有序集合和哈希表。它支持多种操作,如push、pop、add、remove、并集、交集、清新导航源码差集等。支持排序,与Memcached类似,数据存于内存中,通过周期性写入磁盘或追加记录文件实现持久化,并支持主从同步。
Redis是一个高性能的键值数据库,能很好地补充Memcached的不足,某些情况下可以替代关系数据库。提供多种语言客户端,使用便利。
Redis特性
速度快、持久化、高可用。
为何如此快?如何使用?
Redis存储机制
Redis采用Snapshot和AOF两种持久化方式。数据均存储在内存中。
Snapshot工作原理
先存储内存,当数据达到设定阈值时触发一次DUMP操作,redhat coreutils 源码将变化的数据一次性写入RDB文件。
AOF工作原理
先存内存,存储时调用fsync完成日志记录,日志文件基于Redis网络交互协议,可配置存储频率,避免阻塞。
存储模式性能与安全
Snapshot方式性能高于AOF,原因:Snapshot一次性写入,效率高;AOF实时或准实时存储,效率低。
AOF数据安全优于Snapshot,原因:Snapshot数据依赖时间积累,长时间不写入RDB数据丢失;AOF配置策略实现最小数据丢失与高恢复能力。
Redis中的Rewrite功能缩小日志文件大小。
数据结构及使用场景
字符串:用于缓存、计数、锁、ID生成。
哈希:适合存储对象,每个字段与值映射,适合存储多亿键值对。
列表:有序字符串集合,支持插入、弹出、读取指定范围或下标元素。
集合:存储无重复元素的集合,适合分类聚合,复杂度O(1)。
有序集合:集合加分数排序,用于成绩排名等。
技术总结
介绍了Redis的存储模式与数据结构,分析了性能与安全特性,并列举了应用场景。
参考资料
详细阅读原文:Redis数据详解:源码阅读、数据组织、数据存储
Redis 6.0后支持的io多线程工作原理分析
全面解析Redis事务:实现zpop、lua脚本、EVALSHA、ACID特性与异步连接
LinuxC/C++服务器开发/架构师面试题、学习资料、教学视频和学习路线图(包含C/C++、Linux、golang等技术)
redis7.0源码阅读:Redis中的IO多线程(线程池)
Redis服务端处理客户端请求时,采用单线程模型执行逻辑操作,然而读取和写入数据的操作则可在IO多线程模型中进行。在Redis中,命令执行发生在单线程环境中,而数据的读取与写入则通过线程池进行。一个命令从客户端接收,解码成具体命令,根据该命令生成结果后编码并回传至客户端。 Redis配置文件redis.conf中可设置开启IO多线程。通过设置`io-threads-do-reads yes`开启多线程,同时配置`io-threads 2`来创建两个线程,其中一个是主线程,另一个为IO线程。在网络处理文件networking.c中,`stopThreadedIOIfNeeded`函数会判断当前需要执行的命令数是否超过线程数,若少于线程数,则不开启多线程模式,便于调试。 要进入IO多线程模式,运行redis-server命令,然后在调试界面设置断点在networking.c的`readQueryFromClient`函数中。使用redis-cli输入命令时,可以观察到两个线程在运行,一个为主线程,另一个为IO线程。 相关视频推荐帮助理解线程池在Redis中的应用,包括手写线程池及线程池在后端开发中的实际应用。学习资源包括C/C++ Linux服务器开发、后台架构师技术等领域,需要相关资料可加入交流群获取免费分享。 在Redis中,IO线程池实现中,主要包括以下步骤:读取任务的处理通过`postponeClientRead`函数,判断是否启用IO多线程模式,将任务加入到待执行任务队列。
主线程执行`postponeClientRead`函数,将待读客户端任务加入到读取任务队列。在多线程模式下,任务被添加至队列中,由IO线程后续执行。
多线程读取IO任务`handleClientsWithPendingReadsUsingThreads`通过解析协议进行数据读取,与写入任务的多线程处理机制相似。
多线程写入IO任务`handleClientsWithPendingWritesUsingThreads`包括判断是否需要启动IO多线程、负载均衡分配任务到不同IO线程、启动IO子线程执行写入操作、等待IO线程完成写入任务等步骤。负载均衡通过将任务队列中的任务均匀分配至不同的线程消费队列中,实现无锁化操作。
线程调度部分包含开启和关闭IO线程的功能。在`startThreadedIO`中,每个IO线程持有锁,若主线程释放锁,线程开始工作,IO线程标识设置为活跃状态。而在`stopThreadedIO`中,若主线程获取锁,则IO线程等待并停止,IO线程标识设置为非活跃状态。å°åºå¦ä½å¨springä¸ä½¿ç¨redis
1. Redis使ç¨åºæ¯
Redisæ¯ä¸ä¸ªå¼æºç使ç¨ANSI Cè¯è¨ç¼åãæ¯æç½ç»ãå¯åºäºå å亦å¯æä¹ åçæ¥å¿åãKey-Valueæ°æ®åºï¼å¹¶æä¾å¤ç§è¯è¨çAPIã
æ们é½ç¥éï¼å¨æ¥å¸¸çåºç¨ä¸ï¼æ°æ®åºç¶é¢æ¯æ容æåºç°çãæ°æ®é太大åé¢ç¹çæ¥è¯¢ï¼ç±äºç£çIOæ§è½çå±éæ§ï¼å¯¼è´é¡¹ç®çæ§è½è¶æ¥è¶ä½ã
è¿æ¶åï¼åºäºå åçç¼åæ¡æ¶ï¼å°±è½è§£å³æ们å¾å¤é®é¢ãä¾å¦Memcacheï¼Redisçãå°ä¸äºé¢ç¹ä½¿ç¨çæ°æ®æ¾å ¥ç¼å读åï¼å¤§å¤§éä½äºæ°æ®åºçè´æ ãæåäºç³»ç»çæ§è½ã
å ¶å®ï¼å¯¹äºhibernateçäºçº§ç¼åï¼æ¯åæ ·çéçãå©ç¨å åé«éç读åé度ï¼æ¥è§£å³ç¡¬ççç¶é¢ã
2. é 置使ç¨redis
é¦å ï¼æ们éè¦å¼å ¥åºæ¬çjarå ãmavenä¸çåºæ¬å¼ç¨å¦ä¸ï¼
ãããã<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
ç¶åï¼å¨applicationContextä¸é ç½®å¦ä¸:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${ redis.maxIdle}" />
<property name="maxTotal" value="${ redis.maxActive}" />
<property name="maxWaitMillis" value="${ redis.maxWait}" />
<property name="testOnBorrow" value="${ redis.testOnBorrow}" />
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${ redis.host}" p:port="${ redis.port}" p:password="${ redis.pass}"
p:pool-config-ref="poolConfig" />
<bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- å¼å¯äºå¡ï¼å¯ä»¥éè¿transcational注解æ§å¶ -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringSerializer" />
<property name="enableTransactionSupport" value="true" />
</bean>
对äºhibernateçé ç½®å¯ç¥ï¼ç¬¬ä¸ä¸ªpoolconfigæ¯å¯¹è¿æ¥æ± çé ç½®ãå æ¬æ大è¿æ¥æ°ï¼éåæ°ï¼åæ´»æ¶é´ï¼æ大çå¾ æ¶é´ççï¼è¿æä¸äºé¢å¤çé ç½®ï¼è¯·ç´æ¥ç¹å»JedisPoolConfigç±»æºç ï¼è¿è¡æ¥çã
è¿äºé ç½®çææå¦æä¸æç½çè¯ï¼ä¸å®è¦å»æ线ç¨æ± 好好å¦ä¹ ä¸ã
第ä¸ä¸ªé ç½®æ¯è¿æ¥å·¥åï¼é¡¾åæä¹ï¼æåºæ¬ç使ç¨ä¸å®æ¯å¯¹è¿æ¥çæå¼åå ³éãæ们éè¦ä¸ºå ¶é ç½®redisæå¡å¨çè´¦æ·å¯ç ï¼ç«¯å£å·ãï¼è¿éè¿å¯ä»¥é ç½®æ°æ®åºçindexï¼ä½æ¯æ使ç¨æ¶åä¸ç´ä½¿ç¨redisçé»è®¤æ°æ®åºï¼ä¹å°±æ¯ç¬¬0个ï¼
æåä¸ä¸ªé ç½®ç¹å«éè¦ãè¿ä¸ªç±»ä¼¼äºspringæä¾çHibernateDaoSupportã
æ¥ä¸æ¥ï¼å ¨é¨è®²è§£é½å°å´ç»è¿ä¸ªç±»å±å¼ã
3. RedisTemplateç使ç¨
è¿ä¸ªç±»ä½ä¸ºä¸ä¸ªæ¨¡çç±»ï¼æä¾äºå¾å¤å¿«é使ç¨redisçapiï¼èä¸éè¦èªå·±æ¥ç»´æ¤è¿æ¥ï¼äºå¡ã
æåçæ¶åï¼æå建çBaseRedisDaoæ¯ç»§æ¿èªè¿ä¸ªç±»çã继æ¿ç好å¤æ¯æçæ¯ä¸ªDaoä¸ï¼é½å¯ä»¥èªç±çæ§å¶åºååå¨ï¼èªç±çæ§å¶èªå·±æ¯å¦éè¦äºå¡ï¼è¿ä¸ªå ä¸éè¦äºè§£ï¼è·çæç®åçè¿ç§é ç½®æ¹æ³æ¥å³å¯ã
templateæä¾äºä¸ç³»åçoperation,æ¯å¦valueOperation,HashOperation,ListOperation,SetOperationçï¼ç¨æ¥æä½ä¸åæ°æ®ç±»åçRedisã
并ä¸ï¼RedisTemplateè¿æä¾äºå¯¹åºç*OperationsEditorï¼ç¨æ¥éè¿RedisTemplateç´æ¥æ³¨å ¥å¯¹åºçOperationãæ们ææ¶ä¸è®²è¿ä¸ªã
对äºä¸é¢çtest1æ¹æ³ï¼æ们ææ¶ä¸ç¨èèï¼å äºè§£éè¿RedisTemplateæ¥ä½¿ç¨connectionæä½Redisã
Test代ç å¦ä¸ï¼
package cn.test.spjedis;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.cn.redis2.dao.IncrDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestRedis {
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template; // inject the template as ListOperations
//è³äºè¿ä¸ªä¸ºä»ä¹å¯ä»¥æ³¨å ¥ãéè¦åèAbstractBeanFactory doGetBean
//super.setValue(((RedisOperations) value).opsForValue());å°±è¿ä¸è¡ä»£ç ä¾é ä¸ä¸ªeditor
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;
public void testSet(){
template.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte [] key = "tempkey".getBytes();
byte[] value = "tempvalue".getBytes();
connection.set(key, value);
return true;
}
});
}
public void testSet1(){
vOps.set("tempkey", "tempvalue");
}
@Autowired
private IncrDao incr;
@Test
public void addLink() {
System.out.println(incr.incr());
System.out.println(incr.get());
}
}
è¿ä¸ªæ¯å¯¹Stringç±»åæå ¥ç两个æµè¯ãtestæ¹æ³ä¸ï¼ä½¿ç¨äºæ¨¡çç±»æ交åè°(RedisCallBack)çæ¹æ³æ¥ä½¿ç¨jedis connectionæä½æ°æ®ãè¿ä¸é¨åï¼æ没æä¼¼æ¾ç¸è¯å¢ï¼
HibernateTemplateçHibernateCallbackï¼ä»¥åHibernate Sessionç±»ä¸çdoWork以ådoReturningWorkæ¹æ³ï¼é½æ¯ä½¿ç¨äºè¿æ ·çæºå¶ï¼æ¹ä¾¿å¯¹äºè¿æ¥æè sessionçç»ä¸ç®¡çã
public int excuteHqlUpdate(final String hql,final Object ...params){
return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {
@Override
@SuppressWarnings("unchecked")
public Integer doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
queryObject.setParameter(i, params[i]);
}
}
return queryObject.executeUpdate();
}
});
}