diff --git src/wp-admin/includes/file.php src/wp-admin/includes/file.php
index d588e15..eb6dd0e 100644
--- src/wp-admin/includes/file.php
+++ src/wp-admin/includes/file.php
@@ -378,7 +378,7 @@ function _wp_handle_upload( &$file, $overrides, $time, $action ) {
 	$url = $uploads['url'] . "/$filename";
 
 	if ( is_multisite() ) {
-		delete_transient( 'dirsize_cache' );
+		clear_dirsize_cache( $new_file );
 	}
 
 	/**
diff --git src/wp-includes/ms-functions.php src/wp-includes/ms-functions.php
index f41d2b6..55d3d51 100644
--- src/wp-includes/ms-functions.php
+++ src/wp-includes/ms-functions.php
@@ -1658,23 +1658,15 @@ function get_most_recent_post_of_user( $user_id ) {
  * @return int Size of the directory in MB.
  */
 function get_dirsize( $directory ) {
-	$dirsize = get_transient( 'dirsize_cache' );
-	if ( is_array( $dirsize ) && isset( $dirsize[ $directory ][ 'size' ] ) )
-		return $dirsize[ $directory ][ 'size' ];
-
-	if ( ! is_array( $dirsize ) )
-		$dirsize = array();
-
 	// Exclude individual site directories from the total when checking the main site,
 	// as they are subdirectories and should not be counted.
 	if ( is_main_site() ) {
-		$dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory, $directory . '/sites' );
+		$size = recurse_dirsize( $directory, $directory . '/sites' );
 	} else {
-		$dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory );
+		$size = recurse_dirsize( $directory );
 	}
 
-	set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
-	return $dirsize[ $directory ][ 'size' ];
+	return $size;
 }
 
 /**
@@ -1686,14 +1678,26 @@ function get_dirsize( $directory ) {
  * @since MU
  * @since 4.3.0 $exclude parameter added.
  *
- * @param string $directory Full path of a directory.
- * @param string $exclude   Optional. Full path of a subdirectory to exclude from the total.
+ * @param string $directory       Full path of a directory.
+ * @param string $exclude         Optional. Full path of a subdirectory to exclude from the total.
+ * @param array  $directory_cache Optional. Array of cached directory paths.
  * @return int|false Size in MB if a valid directory. False if not.
  */
-function recurse_dirsize( $directory, $exclude = null ) {
+function recurse_dirsize( $directory, $exclude = null, &$directory_cache = null ) {
 	$size = 0;
 
-	$directory = untrailingslashit( $directory );
+	$directory  = untrailingslashit( $directory );
+	$cache_path = normalize_dirsize_cache_path( $directory );
+	$save_cache = false;
+
+	if ( ! isset( $directory_cache ) ) {
+		$directory_cache = get_transient( 'dirsize_cache' );
+		$save_cache      = true;
+	}
+
+	if ( isset( $directory_cache[ $cache_path ] ) ) {
+		return $directory_cache[ $cache_path ];
+	}
 
 	if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
 		return false;
@@ -1706,7 +1710,7 @@ function recurse_dirsize( $directory, $exclude = null ) {
 				if (is_file($path)) {
 					$size += filesize($path);
 				} elseif (is_dir($path)) {
-					$handlesize = recurse_dirsize( $path, $exclude );
+					$handlesize = recurse_dirsize( $path, $exclude, $directory_cache );
 					if ($handlesize > 0)
 						$size += $handlesize;
 				}
@@ -1714,10 +1718,57 @@ function recurse_dirsize( $directory, $exclude = null ) {
 		}
 		closedir($handle);
 	}
+
+	$directory_cache[ $cache_path ] = $size;
+
+	if ( $save_cache ) {
+		set_transient( 'dirsize_cache', $directory_cache );
+	}
+
 	return $size;
 }
 
 /**
+ * Clear dirsize_cache
+ *
+ * Remove the current directory and all parent directories
+ * from the dirsize_cache transient.
+ *
+ * @param string $path Full path of a directory.
+ */
+function clear_dirsize_cache( $path ) {
+	$directory_cache = get_transient( 'dirsize_cache' );
+
+	if ( empty( $directory_cache ) ) {
+		return;
+	}
+
+	$cache_path = normalize_dirsize_cache_path( $path );
+	unset( $directory_cache[ $cache_path ] );
+
+	while ( DIRECTORY_SEPARATOR !== $cache_path && '.' !== $cache_path ) {
+		$cache_path = dirname( $cache_path );
+		unset( $directory_cache[ $cache_path ] );
+	}
+
+	set_transient( 'dirsize_cache', $directory_cache );
+}
+
+/**
+ * Normalize dirsize cache path.
+ *
+ * Ensures array keys follow the same format.
+ *
+ * @param string $path
+ * @return string
+ */
+function normalize_dirsize_cache_path( $path ) {
+	$path = str_replace( ABSPATH, '', $path );
+
+	return untrailingslashit( $path );
+}
+
+/**
  * Check an array of MIME types against a whitelist.
  *
  * WordPress ships with a set of allowed upload filetypes,
diff --git src/wp-includes/post.php src/wp-includes/post.php
index d0bc1a3..254de80 100644
--- src/wp-includes/post.php
+++ src/wp-includes/post.php
@@ -4704,8 +4704,9 @@ function wp_delete_attachment( $post_id, $force_delete = false ) {
 	$backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
 	$file = get_attached_file( $post_id );
 
-	if ( is_multisite() )
-		delete_transient( 'dirsize_cache' );
+	if ( is_multisite() ) {
+		clear_dirsize_cache( $file );
+	}
 
 	/**
 	 * Fires before an attachment is deleted, at the start of wp_delete_attachment().
